diff --git a/src/core/main.c b/src/core/main.c index c54558f23b9..f8b0c69f5aa 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1891,7 +1891,7 @@ static int do_reexecute( r = switch_root(/* new_root= */ switch_root_dir, /* old_root_after= */ NULL, /* flags= */ (objective == MANAGER_SWITCH_ROOT ? SWITCH_ROOT_DESTROY_OLD_ROOT : 0) | - (objective == MANAGER_SOFT_REBOOT ? SWITCH_ROOT_SKIP_RECURSIVE_RUN : 0)); + (objective == MANAGER_SOFT_REBOOT ? 0 : SWITCH_ROOT_RECURSIVE_RUN)); if (r < 0) log_error_errno(r, "Failed to switch root, trying to continue: %m"); } diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index 9fb9a3e3768..b620156c75b 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -30,23 +30,22 @@ int switch_root(const char *new_root, const char *old_root_after, /* path below the new root, where to place the old root after the transition; may be NULL to unmount it */ SwitchRootFlags flags) { - /* Stuff mounted below /run we don't save on soft reboot, as it might have lost its relevance, i.e. - * credentials, removable media and such, we rather want that the new boot mounts this fresh. - * But on the switch from initrd we do use MS_REC, as it is expected that mounts set up in /run - * are maintained. */ - unsigned long run_mount_flags = MS_BIND|(!FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_RUN) ? MS_REC : 0); - struct { + /* Stuff mounted below /run/ we don't save on soft reboot, as it might have lost its relevance, i.e. + * credentials, removable media and such, we rather want that the new boot mounts this fresh. But on + * the switch from initrd we do use MS_REC, as it is expected that mounts set up in /run/ are + * maintained. */ + static const struct { const char *path; - unsigned long mount_flags; - bool skip_if_run_is_rec; /* For child mounts of /run, if it's moved recursively no need to handle */ + unsigned long mount_flags; /* Flags to apply if SWITCH_ROOT_RECURSIVE_RUN is unset */ + unsigned long mount_flags_recursive_run; /* Flags to apply if SWITCH_ROOT_RECURSIVE_RUN is set (0 if shall be skipped) */ } transfer_table[] = { - { "/dev", MS_BIND|MS_REC, false }, /* Recursive, because we want to save the original /dev/shm + /dev/pts and similar */ - { "/sys", MS_BIND|MS_REC, false }, /* Similar, we want to retain various API VFS, or the cgroupv1 /sys/fs/cgroup/ tree */ - { "/proc", MS_BIND|MS_REC, false }, /* Similar */ - { "/run", run_mount_flags, false }, /* Recursive except on soft reboot, see above */ - { SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, true }, /* Credentials passed into the system should survive */ - { ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, true }, /* Similar */ - { "/run/host", MS_BIND|MS_REC, true }, /* Host supplied hierarchy should also survive */ + { "/dev", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Recursive, because we want to save the original /dev/shm/ + /dev/pts/ and similar */ + { "/sys", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Similar, we want to retain various API VFS, or the cgroupv1 /sys/fs/cgroup/ tree */ + { "/proc", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Similar */ + { "/run", MS_BIND, MS_BIND|MS_REC }, /* Recursive except on soft reboot, see above */ + { SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, 0 /* skip! */ }, /* Credentials passed into the system should survive */ + { ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, 0 /* skip! */ }, /* Similar */ + { "/run/host", MS_BIND|MS_REC, 0 /* skip! */ }, /* Host supplied hierarchy should also survive */ }; _cleanup_close_ int old_root_fd = -EBADF, new_root_fd = -EBADF; @@ -129,8 +128,10 @@ int switch_root(const char *new_root, FOREACH_ARRAY(transfer, transfer_table, ELEMENTSOF(transfer_table)) { _cleanup_free_ char *chased = NULL; + unsigned long mount_flags; - if (transfer->skip_if_run_is_rec && !FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_RUN)) + mount_flags = FLAGS_SET(flags, SWITCH_ROOT_RECURSIVE_RUN) ? transfer->mount_flags_recursive_run : transfer->mount_flags; + if (mount_flags == 0) /* skip if zero */ continue; if (access(transfer->path, F_OK) < 0) { @@ -149,7 +150,7 @@ int switch_root(const char *new_root, if (r > 0) /* If it is already mounted, then do nothing */ continue; - r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, transfer->mount_flags, NULL); + r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, mount_flags, NULL); if (r < 0) return r; } @@ -172,14 +173,19 @@ int switch_root(const char *new_root, if (r < 0) { log_debug_errno(r, "Pivoting root file system failed, moving mounts instead: %m"); + if (resolved_old_root_after) { + r = mount_nofollow_verbose(LOG_ERR, "/", resolved_old_root_after, NULL, MS_BIND|MS_REC, NULL); + if (r < 0) + return r; + } + /* If we have to use MS_MOVE let's first try to get rid of *all* mounts we can, with the * exception of the path we want to switch to, plus everything leading to it and within * it. This is necessary because unlike pivot_root() just moving the mount to the root via * MS_MOVE won't magically unmount anything below it. Once the chroot() succeeds the mounts * below would still be around but invisible to us, because not accessible via * /proc/self/mountinfo. Hence, let's clean everything up first, as long as we still can. */ - if (!FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT)) - (void) umount_recursive_full(NULL, MNT_DETACH, STRV_MAKE(new_root)); + (void) umount_recursive_full(NULL, MNT_DETACH, STRV_MAKE(new_root)); if (mount(".", "/", NULL, MS_MOVE, NULL) < 0) return log_error_errno(errno, "Failed to move %s to /: %m", new_root); diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h index 20561fcee8b..ba0d280eba4 100644 --- a/src/shared/switch-root.h +++ b/src/shared/switch-root.h @@ -7,8 +7,7 @@ typedef enum SwitchRootFlags { SWITCH_ROOT_DESTROY_OLD_ROOT = 1 << 0, /* rm -rf old root when switching – under the condition * that it is backed by non-persistent tmpfs/ramfs/… */ SWITCH_ROOT_DONT_SYNC = 1 << 1, /* don't call sync() immediately before switching root */ - SWITCH_ROOT_SKIP_RECURSIVE_RUN = 1 << 2, /* move /run without MS_REC */ - SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT = 1 << 3, /* do not umount recursively on move */ + SWITCH_ROOT_RECURSIVE_RUN = 1 << 2, /* move /run/ with MS_REC from old to new root */ } SwitchRootFlags; int switch_root(const char *new_root, const char *old_root_after, SwitchRootFlags flags); diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 263c444ac20..b976b7d8cf6 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -169,13 +169,11 @@ static int switch_root_initramfs(void) { * Disable sync() during switch-root, we after all sync'ed here plenty, and a dumb sync (as opposed * to the "smart" sync() we did here that looks at progress parameters) would defeat much of our * efforts here. As the new root will be /run/initramfs/, it is not necessary to mount /run/ - * recursively. Also, do not umount filesystems before MS_MOVE, as that should be done by ourself. */ + * recursively. */ return switch_root( /* new_root= */ "/run/initramfs", /* old_root_after= */ "/oldroot", - /* flags= */ SWITCH_ROOT_DONT_SYNC | - SWITCH_ROOT_SKIP_RECURSIVE_RUN | - SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT); + /* flags= */ SWITCH_ROOT_DONT_SYNC); } /* Read the following fields from /proc/meminfo: