summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/main.c55
-rw-r--r--src/shared/util.c2
-rw-r--r--src/shared/util.h1
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);