From: Lennart Poettering Date: Wed, 13 Dec 2023 15:48:58 +0000 (+0100) Subject: mount-util: make sure mount_switch_root() works as clean NOP when '/' is specified... X-Git-Tag: v256-rc1~1494 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=bb59b922565b3f512ea13af38a6fb38470bfc6ad;p=thirdparty%2Fsystemd.git mount-util: make sure mount_switch_root() works as clean NOP when '/' is specified as target --- diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 4f2acce513d..ba3a9e995d1 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -457,10 +457,6 @@ static int mount_switch_root_pivot(int fd_newroot, const char *path) { assert(fd_newroot >= 0); assert(path); - /* Change into the new rootfs. */ - if (fchdir(fd_newroot) < 0) - return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); - /* Let the kernel tuck the new root under the old one. */ if (pivot_root(".", ".") < 0) return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path); @@ -477,10 +473,6 @@ static int mount_switch_root_move(int fd_newroot, const char *path) { assert(fd_newroot >= 0); assert(path); - /* Change into the new rootfs. */ - if (fchdir(fd_newroot) < 0) - return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); - /* Move the new root fs */ if (mount(".", "/", NULL, MS_MOVE, NULL) < 0) return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path); @@ -494,7 +486,7 @@ static int mount_switch_root_move(int fd_newroot, const char *path) { int mount_switch_root_full(const char *path, unsigned long mount_propagation_flag, bool force_ms_move) { _cleanup_close_ int fd_newroot = -EBADF; - int r; + int r, is_current_root; assert(path); assert(mount_propagation_flag_is_valid(mount_propagation_flag)); @@ -503,19 +495,31 @@ int mount_switch_root_full(const char *path, unsigned long mount_propagation_fla if (fd_newroot < 0) return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path); - if (!force_ms_move) { - r = mount_switch_root_pivot(fd_newroot, path); - if (r < 0) { - log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path); - force_ms_move = true; + is_current_root = path_is_root_at(fd_newroot, NULL); + if (is_current_root < 0) + return log_debug_errno(is_current_root, "Failed to determine if target dir is our root already: %m"); + + /* Change into the new rootfs. */ + if (fchdir(fd_newroot) < 0) + return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path); + + /* Make this a NOP if we are supposed to switch to our current root fs. After all, both pivot_root() + * and MS_MOVE don't like that. */ + if (!is_current_root) { + if (!force_ms_move) { + r = mount_switch_root_pivot(fd_newroot, path); + if (r < 0) { + log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path); + force_ms_move = true; + } + } + if (force_ms_move) { + /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is + * an initramfs in which case pivot_root() isn't supported. */ + r = mount_switch_root_move(fd_newroot, path); + if (r < 0) + return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path); } - } - if (force_ms_move) { - /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is - * an initramfs in which case pivot_root() isn't supported. */ - r = mount_switch_root_move(fd_newroot, path); - if (r < 0) - return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path); } /* Finally, let's establish the requested propagation flags. */ diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index c3d0acb6afe..069a63290ce 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -276,7 +276,17 @@ TEST(make_mount_switch_root) { assert_se(s); assert_se(touch(s) >= 0); - for (int force_ms_move = 0; force_ms_move < 2; force_ms_move++) { + struct { + const char *path; + bool force_ms_move; + } table[] = { + { t, false }, + { t, true }, + { "/", false }, + { "/", true }, + }; + + FOREACH_ARRAY(i, table, ELEMENTSOF(table)) { r = safe_fork("(switch-root)", FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | @@ -290,12 +300,14 @@ TEST(make_mount_switch_root) { assert_se(r >= 0); if (r == 0) { - assert_se(make_mount_point(t) >= 0); - assert_se(mount_switch_root_full(t, /* mount_propagation_flag= */ 0, force_ms_move) >= 0); + assert_se(make_mount_point(i->path) >= 0); + assert_se(mount_switch_root_full(i->path, /* mount_propagation_flag= */ 0, i->force_ms_move) >= 0); - assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */ - assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */ - assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */ + if (!path_equal(i->path, "/")) { + assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */ + assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */ + assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */ + } _exit(EXIT_SUCCESS); }