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