diff options
-rw-r--r-- | src/core/main.c | 55 | ||||
-rw-r--r-- | src/shared/util.c | 2 | ||||
-rw-r--r-- | src/shared/util.h | 1 |
3 files changed, 51 insertions, 7 deletions
diff --git a/src/core/main.c b/src/core/main.c index a51d1be45b..ea06dc8014 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1168,30 +1168,73 @@ static void test_cgroups(void) { } static int do_switch_root(const char *switch_root) { - int r; + int r=0; + /* Don't try to unmount the old "/", there's no way to do it. */ + const char *umounts[] = { "/dev", "/proc", "/sys", "/run", NULL }; + int i; + int cfd = -1; + struct stat switch_root_stat, sb; if (path_equal(switch_root, "/")) return 0; - if (chdir(switch_root) < 0) { + if (stat(switch_root, &switch_root_stat) != 0) { r = -errno; + log_error("failed to stat directory %s", switch_root); goto fail; } + for (i = 0; umounts[i] != NULL; i++) { + char newmount[PATH_MAX]; + + snprintf(newmount, sizeof(newmount), "%s%s", switch_root, umounts[i]); + + if ((stat(newmount, &sb) != 0) || (sb.st_dev != switch_root_stat.st_dev)) { + /* mount point seems to be mounted already or stat failed */ + umount2(umounts[i], MNT_DETACH); + continue; + } + + if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { + log_error("failed to mount moving %s to %s", + umounts[i], newmount); + log_error("forcing unmount of %s", umounts[i]); + umount2(umounts[i], MNT_FORCE); + } + } + + if (chdir(switch_root)) { + r = -errno; + log_error("failed to change directory to %s", switch_root); + goto fail; + } + + cfd = open("/", O_RDONLY); + if (mount(switch_root, "/", NULL, MS_MOVE, NULL) < 0) { r = -errno; - chdir("/"); + log_error("failed to mount moving %s to /", switch_root); goto fail; } - if (chroot(".") < 0) - log_warning("Failed to change root, ignoring: %m"); + if (chroot(".")) { + r = -errno; + log_error("failed to change root"); + goto fail; + } - /* FIXME: remove old root */ + if (cfd >= 0) { + rm_rf_children(cfd, false, false); + close(cfd); + cfd=-1; + } return 0; fail: + if (cfd >= 0) + close(cfd); + log_error("Failed to switch root, ignoring: %s", strerror(-r)); return r; diff --git a/src/shared/util.c b/src/shared/util.c index 0234f3b483..53185403d6 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -3139,7 +3139,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } -static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { +int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) { DIR *d; int ret = 0; diff --git a/src/shared/util.h b/src/shared/util.h index 49756dc21f..0af0299dbb 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -349,6 +349,7 @@ int get_ctty(pid_t, dev_t *_devnr, char **r); 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); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); int pipe_eof(int fd); |