diff options
Diffstat (limited to 'libbtrfsutil/subvolume.c')
-rw-r--r-- | libbtrfsutil/subvolume.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c index 7ae8142c..6ac372ca 100644 --- a/libbtrfsutil/subvolume.c +++ b/libbtrfsutil/subvolume.c @@ -19,6 +19,8 @@ #include <errno.h> #include <fcntl.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/stat.h> @@ -123,3 +125,116 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_id_fd(int fd, return BTRFS_UTIL_OK; } + +static enum btrfs_util_error openat_parent_and_name(int dirfd, const char *path, + char *name, size_t name_len, + int *fd) +{ + char *tmp_path, *slash, *dirname, *basename; + size_t len; + + /* Ignore trailing slashes. */ + len = strlen(path); + while (len > 1 && path[len - 1] == '/') + len--; + + tmp_path = malloc(len + 1); + if (!tmp_path) + return BTRFS_UTIL_ERROR_NO_MEMORY; + memcpy(tmp_path, path, len); + tmp_path[len] = '\0'; + + slash = memrchr(tmp_path, '/', len); + if (slash == tmp_path) { + dirname = "/"; + basename = tmp_path + 1; + } else if (slash) { + *slash = '\0'; + dirname = tmp_path; + basename = slash + 1; + } else { + dirname = "."; + basename = tmp_path; + } + + len = strlen(basename); + if (len >= name_len) { + free(tmp_path); + errno = ENAMETOOLONG; + return BTRFS_UTIL_ERROR_INVALID_ARGUMENT; + } + memcpy(name, basename, len); + name[len] = '\0'; + + *fd = openat(dirfd, dirname, O_RDONLY | O_DIRECTORY); + if (*fd == -1) { + free(tmp_path); + return BTRFS_UTIL_ERROR_OPEN_FAILED; + } + + free(tmp_path); + return BTRFS_UTIL_OK; +} + +PUBLIC enum btrfs_util_error btrfs_util_create_subvolume(const char *path, + int flags, + uint64_t *async_transid, + struct btrfs_util_qgroup_inherit *qgroup_inherit) +{ + char name[BTRFS_SUBVOL_NAME_MAX + 1]; + enum btrfs_util_error err; + int parent_fd; + + err = openat_parent_and_name(AT_FDCWD, path, name, sizeof(name), + &parent_fd); + if (err) + return err; + + err = btrfs_util_create_subvolume_fd(parent_fd, name, flags, + async_transid, qgroup_inherit); + SAVE_ERRNO_AND_CLOSE(parent_fd); + return err; +} + +PUBLIC enum btrfs_util_error btrfs_util_create_subvolume_fd(int parent_fd, + const char *name, + int flags, + uint64_t *async_transid, + struct btrfs_util_qgroup_inherit *qgroup_inherit) +{ + struct btrfs_ioctl_vol_args_v2 args = {}; + size_t len; + int ret; + + if (flags) { + errno = EINVAL; + return BTRFS_UTIL_ERROR_INVALID_ARGUMENT; + } + + if (async_transid) + args.flags |= BTRFS_SUBVOL_CREATE_ASYNC; + if (qgroup_inherit) { + args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT; + args.qgroup_inherit = (struct btrfs_qgroup_inherit *)qgroup_inherit; + args.size = (sizeof(*args.qgroup_inherit) + + args.qgroup_inherit->num_qgroups * + sizeof(args.qgroup_inherit->qgroups[0])); + } + + len = strlen(name); + if (len >= sizeof(args.name)) { + errno = ENAMETOOLONG; + return BTRFS_UTIL_ERROR_INVALID_ARGUMENT; + } + memcpy(args.name, name, len); + args.name[len] = '\0'; + + ret = ioctl(parent_fd, BTRFS_IOC_SUBVOL_CREATE_V2, &args); + if (ret == -1) + return BTRFS_UTIL_ERROR_SUBVOL_CREATE_FAILED; + + if (async_transid) + *async_transid = args.transid; + + return BTRFS_UTIL_OK; +} |