]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
switch-root: don't do rm_rf() of old superblock on switch root if pivot_root() worked
authorLennart Poettering <lennart@poettering.net>
Fri, 2 Jan 2026 15:30:58 +0000 (16:30 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 4 Jan 2026 10:41:24 +0000 (19:41 +0900)
We do the rm_rf_children() call only because in some cases we cannot
pivot_root() and hence the orginal root superblock stays pinned, and we
thus have to empty it to minimize its memory use. But if pivot_root()
worked (and the umount() for the old root), then there's really no need
to do this work.

Dropping this codepath is useful in context of Christian's recent work
to make the original initrd tmpfs unmountable, which means pivot_root()
will work, and thus there's no need to empty the tmpfs anymore, and we
can speed up boot a bit.

Fixes: #40250
src/shared/switch-root.c

index d5f34c5d37d4e47920f095af4ec0b79319fc2f0d..6017200d79c3ea8105ed8f524add3a572e3d1b3f 100644 (file)
@@ -54,7 +54,7 @@ int switch_root(const char *new_root,
         if (new_root_fd < 0)
                 return log_error_errno(errno, "Failed to open target directory '%s': %m", new_root);
 
-        r = fds_are_same_mount(old_root_fd, new_root_fd);
+        r = fds_are_same_mount(old_root_fd, new_root_fd); /* checks if referenced inodes and mounts match */
         if (r < 0)
                 return log_error_errno(r, "Failed to check if old and new root directory/mount are the same: %m");
         if (r > 0) {
@@ -186,18 +186,23 @@ int switch_root(const char *new_root,
 
                 if (chdir(".") < 0)
                         return log_error_errno(errno, "Failed to change directory: %m");
-        }
 
-        if (istmp > 0) {
-                struct stat rb;
+                /* Now empty the old root superblock */
+                if (istmp > 0) {
+                        struct stat rb;
 
-                if (fstat(old_root_fd, &rb) < 0)
-                        return log_error_errno(errno, "Failed to stat old root directory: %m");
+                        if (fstat(old_root_fd, &rb) < 0)
+                                return log_error_errno(errno, "Failed to stat old root directory: %m");
 
-                /* Note: the below won't operate on non-memory file systems (i.e. only on tmpfs, ramfs), and
-                 * it will stop at mount boundaries */
-                (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
-        }
+                        /* Note: the below won't operate on non-memory file systems (i.e. only on tmpfs, ramfs), and
+                         * it will stop at mount boundaries */
+                        (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
+                }
+        } else
+                /* NB: we don't bother with emptying the old root superblock here, under the assumption the
+                 * pivot_root() + umount() sufficiently detached from the superblock to the point we don't
+                 * need to empty it anymore */
+                log_debug("Pivoting root worked.");
 
         return 0;
 }