summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-04-04 19:22:00 +0200
committerLennart Poettering <lennart@poettering.net>2015-04-06 10:57:53 +0200
commit9e9b663aae201c24ba4f673cf77ee6bebe4edaa6 (patch)
tree002441fab44af8ba874e436ccfb9d2a81e26e765
parent8f06b239f2a41310582f9d1932778afbc0c9c9f6 (diff)
rm-rf: add support for recursively removing btrfs subvolumes
-rw-r--r--src/shared/rm-rf.c27
-rw-r--r--src/shared/rm-rf.h1
2 files changed, 27 insertions, 1 deletions
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
index eeb2e3919..50112e3b5 100644
--- a/src/shared/rm-rf.c
+++ b/src/shared/rm-rf.c
@@ -75,7 +75,8 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
- if (de->d_type == DT_UNKNOWN || (de->d_type == DT_DIR && root_dev)) {
+ if (de->d_type == DT_UNKNOWN ||
+ (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
@@ -114,6 +115,30 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
continue;
}
+ if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) {
+ struct btrfs_ioctl_vol_args args = {};
+
+ /* This could be a subvolume, try to remove it */
+
+ strncpy(args.name, de->d_name, sizeof(args.name)-1);
+ if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args) < 0) {
+
+ if (errno != ENOTTY && errno != EINVAL) {
+ if (ret == 0)
+ ret = -errno;
+
+ safe_close(subdir_fd);
+ continue;
+ }
+
+ /* ENOTTY, then it wasn't a btrfs subvolume */
+ } else {
+ /* It was a subvolume, continue. */
+ safe_close(subdir_fd);
+ continue;
+ }
+ }
+
/* We pass REMOVE_PHYSICAL here, to avoid
* doing the fstatfs() to check the file
* system type again for each directory */
diff --git a/src/shared/rm-rf.h b/src/shared/rm-rf.h
index 769bbc853..96579eb18 100644
--- a/src/shared/rm-rf.h
+++ b/src/shared/rm-rf.h
@@ -27,6 +27,7 @@ typedef enum RemoveFlags {
REMOVE_ONLY_DIRECTORIES = 1,
REMOVE_ROOT = 2,
REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 8,
} RemoveFlags;
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);