chiark / gitweb /
switch-root: move switch_root() call into its own .c file
[elogind.git] / src / shared / util.c
index 53185403d62d8e762682d1f7b499e04904fd15c1..5acddd3e0f9250c61a6bfc0a5be4d19c3fca5a53 100644 (file)
@@ -3139,7 +3139,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) {
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
         DIR *d;
         int ret = 0;
 
@@ -3208,24 +3208,37 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
 
                 if (is_dir) {
                         int subdir_fd;
-
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
-                        if (subdir_fd < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                continue;
+                        struct stat sb;
+                        if (root_dev) {
+                                if (fstatat(fd, de->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                        continue;
+                                }
                         }
 
-                        r = rm_rf_children(subdir_fd, only_dirs, honour_sticky);
-                        if (r < 0 && ret == 0)
-                                ret = r;
+                        /* if root_dev is set, remove subdirectories only, if device is same as dir */
+                        if ((root_dev == NULL) || (sb.st_dev == root_dev->st_dev)) {
 
-                        if (!keep_around)
-                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+                                subdir_fd = openat(fd, de->d_name,
+                                                   O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                                if (subdir_fd < 0) {
                                         if (ret == 0 && errno != ENOENT)
                                                 ret = -errno;
+                                        continue;
                                 }
 
+                                r = rm_rf_children(subdir_fd, only_dirs, honour_sticky, root_dev);
+                                if (r < 0 && ret == 0)
+                                        ret = r;
+
+                                if (!keep_around)
+                                        if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+                                                if (ret == 0 && errno != ENOENT)
+                                                        ret = -errno;
+                                        }
+                        }
+
                 } else if (!only_dirs && !keep_around) {
 
                         if (unlinkat(fd, de->d_name, 0) < 0) {
@@ -3259,7 +3272,7 @@ 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);
+        r = rm_rf_children(fd, only_dirs, honour_sticky, NULL);
 
         if (delete_root) {
 
@@ -5639,3 +5652,18 @@ bool is_valid_documentation_url(const char *url) {
 
         return false;
 }
+
+bool in_initrd(void) {
+        static bool checked=false;
+        static bool is_in_initrd=false;
+
+        if (!checked) {
+                struct stat sb;
+                if (stat("/", &sb) == 0) {
+                        is_in_initrd = (sb.st_dev == 1);
+                        checked = true;
+                }
+        }
+
+        return is_in_initrd;
+}