diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:49:52 +0000 |
commit | 55944e5e40b1be2afc4855d8d2baf4b73d1876b5 (patch) | |
tree | 33f869f55a1b149e9b7c2b7e201867ca5dd52992 /src/basic/btrfs.c | |
parent | Initial commit. (diff) | |
download | systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.tar.xz systemd-55944e5e40b1be2afc4855d8d2baf4b73d1876b5.zip |
Adding upstream version 255.4.upstream/255.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/basic/btrfs.c')
-rw-r--r-- | src/basic/btrfs.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/basic/btrfs.c b/src/basic/btrfs.c new file mode 100644 index 0000000..bb07374 --- /dev/null +++ b/src/basic/btrfs.c @@ -0,0 +1,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 */ +} |