1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/btrfs.h>
#include <sys/ioctl.h>
#include "btrfs.h"
#include "fd-util.h"
#include "fs-util.h"
#include "path-util.h"
int btrfs_validate_subvolume_name(const char *name) {
if (!filename_is_valid(name))
return -EINVAL;
if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
return -E2BIG;
return 0;
}
static int extract_subvolume_name(const char *path, char **ret) {
_cleanup_free_ char *fn = NULL;
int r;
assert(path);
assert(ret);
r = path_extract_filename(path, &fn);
if (r < 0)
return r;
r = btrfs_validate_subvolume_name(fn);
if (r < 0)
return r;
*ret = TAKE_PTR(fn);
return 0;
}
int btrfs_subvol_make(int dir_fd, const char *path) {
struct btrfs_ioctl_vol_args args = {};
_cleanup_free_ char *subvolume = NULL, *parent = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(!isempty(path));
r = extract_subvolume_name(path, &subvolume);
if (r < 0)
return r;
r = path_extract_directory(path, &parent);
if (r < 0) {
if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
return r;
dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
if (dir_fd < 0)
return dir_fd;
} else {
fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
if (fd < 0)
return -errno;
dir_fd = fd;
}
strncpy(args.name, subvolume, sizeof(args.name)-1);
return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
}
int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
mode_t old, combined;
int r;
assert(path);
/* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
old = umask(~mode);
combined = old | ~mode;
if (combined != ~mode)
umask(combined);
r = btrfs_subvol_make(dir_fd, path);
umask(old);
if (r >= 0)
return 1; /* subvol worked */
if (!ERRNO_IS_NOT_SUPPORTED(r))
return r;
if (mkdirat(dir_fd, path, mode) < 0)
return -errno;
return 0; /* plain directory */
}
|