chiark / gitweb /
util: rm_rf() refuse cleaning non-memory file systems, as extra paranoia
authorLennart Poettering <lennart@poettering.net>
Tue, 10 Jul 2012 17:05:58 +0000 (19:05 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 10 Jul 2012 17:05:58 +0000 (19:05 +0200)
src/shared/util.c
src/shared/util.h
src/tmpfiles/tmpfiles.c

index 8caac7b..62121b5 100644 (file)
@@ -3243,7 +3243,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         return 0;
 }
 
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
         DIR *d;
         int ret = 0;
 
@@ -3335,14 +3335,43 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
         return ret;
 }
 
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
-        int fd;
-        int r;
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+        struct statfs s;
+
+        assert(fd >= 0);
+
+        if (fstatfs(fd, &s) < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        /* We refuse to clean disk file systems with this call. This
+         * is extra paranoia just to be sure we never ever remove
+         * non-state data */
+
+        if (s.f_type != TMPFS_MAGIC &&
+            s.f_type != RAMFS_MAGIC) {
+                log_error("Attempted to remove disk file system, and we can't allow that.");
+                close_nointr_nofail(fd);
+                return -EPERM;
+        }
+
+        return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
+
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+        int fd, r;
+        struct statfs s;
 
         assert(path);
 
-        /* Be paranoid */
-        assert(!streq(path, "/"));
+        /* We refuse to clean the root file system with this
+         * call. This is extra paranoia to never cause a really
+         * seriously broken system. */
+        if (path_equal(path, "/")) {
+                log_error("Attempted to remove entire root file system, and we can't allow that.");
+                return -EPERM;
+        }
 
         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
         if (fd < 0) {
@@ -3350,6 +3379,17 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
                 if (errno != ENOTDIR)
                         return -errno;
 
+                if (!dangerous) {
+                        if (statfs(path, &s) < 0)
+                                return -errno;
+
+                        if (s.f_type != TMPFS_MAGIC &&
+                            s.f_type != RAMFS_MAGIC) {
+                                log_error("Attempted to remove disk file system, and we can't allow that.");
+                                return -EPERM;
+                        }
+                }
+
                 if (delete_root && !only_dirs)
                         if (unlink(path) < 0 && errno != ENOENT)
                                 return -errno;
@@ -3357,8 +3397,21 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
                 return 0;
         }
 
-        r = rm_rf_children(fd, only_dirs, honour_sticky, NULL);
+        if (!dangerous) {
+                if (fstatfs(fd, &s) < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                if (s.f_type != TMPFS_MAGIC &&
+                    s.f_type != RAMFS_MAGIC) {
+                        log_error("Attempted to remove disk file system, and we can't allow that.");
+                        close_nointr_nofail(fd);
+                        return -EPERM;
+                }
+        }
 
+        r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
         if (delete_root) {
 
                 if (honour_sticky && file_is_priv_sticky(path) > 0)
@@ -3373,6 +3426,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
         return r;
 }
 
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         assert(path);
 
index 47497b5..89e9a00 100644 (file)
@@ -358,7 +358,9 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
 
 int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
 int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
 
 int pipe_eof(int fd);
 
index e0b0e94..2d5d90d 100644 (file)
@@ -845,7 +845,7 @@ static int remove_item_instance(Item *i, const char *instance) {
         case RECURSIVE_REMOVE_PATH:
                 /* FIXME: we probably should use dir_cleanup() here
                  * instead of rm_rf() so that 'x' is honoured. */
-                r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
+                r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
                 if (r < 0 && r != -ENOENT) {
                         log_error("rm_rf(%s): %s", instance, strerror(-r));
                         return r;