chiark / gitweb /
util: loop_write - accept 0-length message
[elogind.git] / src / shared / rm-rf.c
index 99d12b11c6da6362c111ba7f2105ccd449045a9a..a89e8afc2ae6980a5ede4b54d2785f4ce5c22254 100644 (file)
@@ -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;
@@ -89,7 +91,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                 if (is_dir) {
                         int subdir_fd;
 
-                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        /* if root_dev is set, remove subdirectories only if device is same */
                         if (root_dev && st.st_dev != root_dev->st_dev)
                                 continue;
 
@@ -100,6 +102,44 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
                                 continue;
                         }
 
+                        /* Stop at mount points */
+                        r = fd_is_mount_point(subdir_fd);
+                        if (r < 0) {
+                                if (ret == 0 && r != -ENOENT)
+                                        ret = r;
+
+                                safe_close(subdir_fd);
+                                continue;
+                        }
+                        if (r) {
+                                safe_close(subdir_fd);
+                                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 */
@@ -136,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) {
 
@@ -162,9 +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;
                 }
         }