X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Frm-rf.c;h=bafd483be2bbf39f42d09257b03decbeb5bef9fc;hb=d896ac2d2fbce41a0b11a0618a685adeaf18b8fe;hp=eeb2e3919659a3d9a7a051dd1b5af915579a66c5;hpb=34da583ada206b2d4b5431a90c67a771c62e604d;p=elogind.git diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index eeb2e3919..bafd483be 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -21,6 +21,7 @@ #include "util.h" #include "path-util.h" +#include "btrfs-util.h" #include "rm-rf.h" int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { @@ -75,7 +76,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; @@ -101,7 +103,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { } /* Stop at mount points */ - r = fd_is_mount_point(subdir_fd); + r = fd_is_mount_point(fd, de->d_name, 0); if (r < 0) { if (ret == 0 && r != -ENOENT) ret = r; @@ -114,6 +116,30 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { continue; } + if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { + + /* This could be a subvolume, try to remove it */ + + r = btrfs_subvol_remove_fd(fd, de->d_name, true); + if (r < 0) { + if (r != -ENOTTY && r != -EINVAL) { + if (ret == 0) + ret = r; + + safe_close(subdir_fd); + continue; + } + + /* ENOTTY, then it wasn't a + * btrfs subvolume, continue + * below. */ + } 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 */ @@ -150,6 +176,18 @@ int rm_rf(const char *path, RemoveFlags flags) { return -EPERM; } + if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) { + /* Try to remove as subvolume first */ + r = btrfs_subvol_remove(path, true); + if (r >= 0) + return r; + + if (r != -ENOTTY && r != -EINVAL) + return r; + + /* Not btrfs or not a subvolume */ + } + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (fd < 0) { @@ -176,8 +214,8 @@ int rm_rf(const char *path, RemoveFlags flags) { r = rm_rf_children(fd, flags, NULL); if (flags & REMOVE_ROOT) { - if (rmdir(path) < 0 && errno != ENOENT) { - if (r == 0) + if (rmdir(path) < 0) { + if (r == 0 && errno != ENOENT) r = -errno; } }