diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 074deeccda..ec7e00986b 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -799,6 +799,45 @@ int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) { return btrfs_resize_loopback_fd(fd, new_size, grow_only); } +static int make_qgroup_id(uint64_t level, uint64_t id, uint64_t *ret) { + assert(ret); + + if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT))) + return -EINVAL; + + if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT)) + return -EINVAL; + + *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id; + return 0; +} + +static int qgroup_create_or_destroy(int fd, bool b, uint64_t level, uint64_t id) { + + struct btrfs_ioctl_qgroup_create_args args = { + .create = b, + }; + + int r; + + r = make_qgroup_id(level, id, (uint64_t*) &args.qgroupid); + if (r < 0) + return r; + + if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) + return -errno; + + return 0; +} + +int btrfs_qgroup_create(int fd, uint64_t level, uint64_t id) { + return qgroup_create_or_destroy(fd, true, level, id); +} + +int btrfs_qgroup_destroy(int fd, uint64_t level, uint64_t id) { + return qgroup_create_or_destroy(fd, false, level, id); +} + static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, bool recursive) { struct btrfs_ioctl_search_args args = { .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, @@ -828,16 +867,6 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol if (!S_ISDIR(st.st_mode)) return -EINVAL; - /* First, try to remove the subvolume. If it happens to be - * already empty, this will just work. */ - strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); - if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) - return 0; - if (!recursive || errno != ENOTEMPTY) - return -errno; - - /* OK, the subvolume is not empty, let's look for child - * subvolumes, and remove them, first */ subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); if (subvol_fd < 0) return -errno; @@ -848,6 +877,19 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol return r; } + /* First, try to remove the subvolume. If it happens to be + * already empty, this will just work. */ + strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); + if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) { + (void) btrfs_qgroup_destroy(fd, 0, subvol_id); + return 0; + } + if (!recursive || errno != ENOTEMPTY) + return -errno; + + /* OK, the subvolume is not empty, let's look for child + * subvolumes, and remove them, first */ + args.key.min_offset = args.key.max_offset = subvol_id; while (btrfs_ioctl_search_args_compare(&args) <= 0) { @@ -925,6 +967,7 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0) return -errno; + (void) btrfs_qgroup_destroy(fd, 0, subvol_id); return 0; } diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 8632c3638c..ad7c7009ab 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -86,3 +86,6 @@ int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); int btrfs_subvol_remove(const char *path, bool recursive); int btrfs_subvol_remove_fd(int fd, const char *subvolume, bool recursive); + +int btrfs_qgroup_create(int fd, uint64_t level, uint64_t id); +int btrfs_qgroup_destroy(int fd, uint64_t level, uint64_t id); diff --git a/src/basic/missing.h b/src/basic/missing.h index 59e835a466..8d11b80d95 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -248,6 +248,10 @@ static inline int getrandom(void *buffer, size_t count, unsigned flags) { #define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) #endif +#ifndef BTRFS_QGROUP_LEVEL_SHIFT +#define BTRFS_QGROUP_LEVEL_SHIFT 48 +#endif + #ifndef HAVE_LINUX_BTRFS_H struct btrfs_ioctl_vol_args { int64_t fd;