diff options
author | Omar Sandoval <osandov@fb.com> | 2018-01-18 14:26:35 -0800 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2018-03-06 11:28:37 +0100 |
commit | 678da5a7f71ed7fbc887e40cc8ef91008c6fe843 (patch) | |
tree | 1a2f01608affa5b7b4a9cd91976996c82450044a /libbtrfsutil/subvolume.c | |
parent | cfa89b30821fedb48ca163a9c1128c4db596c911 (diff) |
libbtrfsutil: add btrfs_util_delete_subvolume()
We also support recursive deletion using a subvolume iterator.
Signed-off-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'libbtrfsutil/subvolume.c')
-rw-r--r-- | libbtrfsutil/subvolume.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/libbtrfsutil/subvolume.c b/libbtrfsutil/subvolume.c index 28b38e5a..56cd2c6d 100644 --- a/libbtrfsutil/subvolume.c +++ b/libbtrfsutil/subvolume.c @@ -1005,6 +1005,108 @@ PUBLIC enum btrfs_util_error btrfs_util_create_snapshot_fd2(int fd, return BTRFS_UTIL_OK; } +static enum btrfs_util_error delete_subvolume_children(int parent_fd, + const char *name) +{ + struct btrfs_util_subvolume_iterator *iter; + enum btrfs_util_error err; + int fd; + + fd = openat(parent_fd, name, O_RDONLY); + if (fd == -1) + return BTRFS_UTIL_ERROR_OPEN_FAILED; + + err = btrfs_util_create_subvolume_iterator_fd(fd, 0, + BTRFS_UTIL_SUBVOLUME_ITERATOR_POST_ORDER, + &iter); + if (err) + goto out; + + for (;;) { + char child_name[BTRFS_PATH_NAME_MAX + 1]; + char *child_path; + int child_parent_fd; + + err = btrfs_util_subvolume_iterator_next(iter, &child_path, + NULL); + if (err) { + if (err == BTRFS_UTIL_ERROR_STOP_ITERATION) + err = BTRFS_UTIL_OK; + break; + } + + err = openat_parent_and_name(fd, child_path, child_name, + sizeof(child_name), + &child_parent_fd); + free(child_path); + if (err) + break; + + err = btrfs_util_delete_subvolume_fd(child_parent_fd, + child_name, 0); + SAVE_ERRNO_AND_CLOSE(child_parent_fd); + if (err) + break; + } + + btrfs_util_destroy_subvolume_iterator(iter); +out: + SAVE_ERRNO_AND_CLOSE(fd); + return err; +} + +PUBLIC enum btrfs_util_error btrfs_util_delete_subvolume(const char *path, + int flags) +{ + char name[BTRFS_PATH_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_delete_subvolume_fd(parent_fd, name, flags); + SAVE_ERRNO_AND_CLOSE(parent_fd); + return err; +} + +PUBLIC enum btrfs_util_error btrfs_util_delete_subvolume_fd(int parent_fd, + const char *name, + int flags) +{ + struct btrfs_ioctl_vol_args args = {}; + enum btrfs_util_error err; + size_t len; + int ret; + + if (flags & ~BTRFS_UTIL_DELETE_SUBVOLUME_MASK) { + errno = EINVAL; + return BTRFS_UTIL_ERROR_INVALID_ARGUMENT; + } + + if (flags & BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE) { + err = delete_subvolume_children(parent_fd, name); + if (err) + return err; + } + + 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_SNAP_DESTROY, &args); + if (ret == -1) + return BTRFS_UTIL_ERROR_SNAP_DESTROY_FAILED; + + return BTRFS_UTIL_OK; +} + PUBLIC void btrfs_util_destroy_subvolume_iterator(struct btrfs_util_subvolume_iterator *iter) { if (iter) { |