From 711ea7307d54caa74aa89fc7e8614236e3721f1c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 09:58:46 +0200 Subject: switch_root: new command Copied from Dracut project: git://dracut.git.sourceforge.net/gitroot/dracut switch_root history in dracut.git repository: $ git shortlog switch_root.c Harald Hoyer (5): replace switch_root shell script with binary add \n to switch_root use switch_root code from http://pjones.fedorapeople.org/mkstart/usr/lib/mkstart/switchroot.c mount move instead of umount and fix the search for fallback inits setsid() and set controlling terminal for real /sbin/init Victor Lowther (2): Simplify switch_root.c a bit Remove all files on the initramfs before switching root Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 sys-utils/switch_root.c (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c new file mode 100644 index 00000000..9188006e --- /dev/null +++ b/sys-utils/switch_root.c @@ -0,0 +1,198 @@ +/* + * switchroot.c - switch to new root directory and start init. + * + * Copyright 2002-2008 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: + * Peter Jones + * Jeremy Katz + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 0x2 +#endif + +enum { + ok, + err_no_directory, + err_usage, +}; + +/* remove all files/directories below dirName -- don't cross mountpoints */ +static int +recursiveRemove(char * dirName) + { + struct stat sb,rb; + DIR * dir; + struct dirent * d; + char * strBuf = alloca(strlen(dirName) + 1024); + + if (!(dir = opendir(dirName))) { + printf("error opening %s: %m\n", dirName); + return 0; + } + + if (fstat(dirfd(dir),&rb)) { + printf("unable to stat %s: %m\n", dirName); + closedir(dir); + return 0; + } + + errno = 0; + while ((d = readdir(dir))) { + errno = 0; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { + errno = 0; + continue; + } + + strcpy(strBuf, dirName); + strcat(strBuf, "/"); + strcat(strBuf, d->d_name); + + if (lstat(strBuf, &sb)) { + printf("failed to stat %s: %m\n", strBuf); + errno = 0; + continue; + } + + /* only descend into subdirectories if device is same as dir */ + if (S_ISDIR(sb.st_mode)) { + if (sb.st_dev == rb.st_dev) { + recursiveRemove(strBuf); + if (rmdir(strBuf)) + printf("failed to rmdir %s: %m\n", strBuf); + } + errno = 0; + continue; + } + if (unlink(strBuf)) { + printf("failed to remove %s: %m\n", strBuf); + errno = 0; + continue; + } + } + + if (errno) { + closedir(dir); + printf("error reading from %s: %m\n", dirName); + return 1; + } + + closedir(dir); + + return 0; + } + +static int switchroot(const char *newroot) +{ + /* Don't try to unmount the old "/", there's no way to do it. */ + const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; + int errnum; + int i; + + for (i = 0; umounts[i] != NULL; i++) { + char newmount[PATH_MAX]; + strcpy(newmount, newroot); + strcat(newmount, umounts[i]); + if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { + fprintf(stderr, "Error mount moving old %s %s %m\n", + umounts[i], newmount); + fprintf(stderr, "Forcing unmount of %s\n", umounts[i]); + umount2(umounts[i], MNT_FORCE); + } + } + + if (chdir(newroot) < 0) { + errnum=errno; + fprintf(stderr, "switchroot: chdir failed: %m\n"); + errno=errnum; + return -1; + } + recursiveRemove("/"); + if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { + errnum = errno; + fprintf(stderr, "switchroot: mount failed: %m\n"); + errno = errnum; + return -1; + } + + if (chroot(".")) { + errnum = errno; + fprintf(stderr, "switchroot: chroot failed: %m\n"); + errno = errnum; + return -2; + } + return 1; +} + +static void usage(FILE *output) +{ + fprintf(output, "usage: switchroot \n"); + if (output == stderr) + exit(err_usage); + exit(ok); +} + +int main(int argc, char *argv[]) +{ + char *newroot = argv[1]; + char *init = argv[2]; + char **initargs = &argv[2]; + + if (newroot == NULL || newroot[0] == '\0' || + init == NULL || init[0] == '\0' ) { + usage(stderr); + } + + if (switchroot(newroot) < 0) { + fprintf(stderr, "switchroot has failed. Sorry.\n"); + return 1; + } + if (access(initargs[0], X_OK)) + fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); + + /* get session leader */ + setsid(); + /* set controlling terminal */ + ioctl (0, TIOCSCTTY, 1); + + execv(initargs[0], initargs); +} + +/* + * vim:noet:ts=8:sw=8:sts=8 + */ -- cgit v1.2.3 From 3ddbe4d26b15a09bbec11f031aa522b0c9a9eebd Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 10:37:45 +0200 Subject: switch_root: fix coding style Signed-off-by: Peter Jones Acked-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 150 ++++++++++++++++++++++-------------------------- 1 file changed, 70 insertions(+), 80 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 9188006e..15203872 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -20,9 +20,6 @@ * Peter Jones * Jeremy Katz */ - -#define _GNU_SOURCE 1 - #include #include #include @@ -40,10 +37,6 @@ #define MS_MOVE 8192 #endif -#ifndef MNT_DETACH -#define MNT_DETACH 0x2 -#endif - enum { ok, err_no_directory, @@ -53,69 +46,69 @@ enum { /* remove all files/directories below dirName -- don't cross mountpoints */ static int recursiveRemove(char * dirName) - { - struct stat sb,rb; - DIR * dir; - struct dirent * d; - char * strBuf = alloca(strlen(dirName) + 1024); - - if (!(dir = opendir(dirName))) { - printf("error opening %s: %m\n", dirName); - return 0; - } - - if (fstat(dirfd(dir),&rb)) { - printf("unable to stat %s: %m\n", dirName); - closedir(dir); - return 0; - } - - errno = 0; - while ((d = readdir(dir))) { - errno = 0; - - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { - errno = 0; - continue; - } - - strcpy(strBuf, dirName); - strcat(strBuf, "/"); - strcat(strBuf, d->d_name); - - if (lstat(strBuf, &sb)) { - printf("failed to stat %s: %m\n", strBuf); - errno = 0; - continue; - } - - /* only descend into subdirectories if device is same as dir */ - if (S_ISDIR(sb.st_mode)) { - if (sb.st_dev == rb.st_dev) { - recursiveRemove(strBuf); - if (rmdir(strBuf)) - printf("failed to rmdir %s: %m\n", strBuf); - } - errno = 0; - continue; - } - if (unlink(strBuf)) { - printf("failed to remove %s: %m\n", strBuf); - errno = 0; - continue; - } - } - - if (errno) { - closedir(dir); - printf("error reading from %s: %m\n", dirName); - return 1; - } - - closedir(dir); - - return 0; - } +{ + struct stat sb,rb; + DIR * dir; + struct dirent * d; + char * strBuf = alloca(strlen(dirName) + 1024); + + if (!(dir = opendir(dirName))) { + printf("error opening %s: %m\n", dirName); + return 0; + } + + if (fstat(dirfd(dir),&rb)) { + printf("unable to stat %s: %m\n", dirName); + closedir(dir); + return 0; + } + + errno = 0; + + while ((d = readdir(dir))) { + errno = 0; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { + errno = 0; + continue; + } + + strcpy(strBuf, dirName); + strcat(strBuf, "/"); + strcat(strBuf, d->d_name); + + if (lstat(strBuf, &sb)) { + printf("failed to stat %s: %m\n", strBuf); + errno = 0; + continue; + } + + /* only descend into subdirectories if device is same as dir */ + if (S_ISDIR(sb.st_mode)) { + if (sb.st_dev == rb.st_dev) { + recursiveRemove(strBuf); + if (rmdir(strBuf)) + printf("failed to rmdir %s: %m\n", strBuf); + } + errno = 0; + continue; + } + if (unlink(strBuf)) { + printf("failed to remove %s: %m\n", strBuf); + errno = 0; + continue; + } + } + + if (errno) { + closedir(dir); + printf("error reading from %s: %m\n", dirName); + return 1; + } + + closedir(dir); + return 0; +} static int switchroot(const char *newroot) { @@ -137,10 +130,10 @@ static int switchroot(const char *newroot) } if (chdir(newroot) < 0) { - errnum=errno; - fprintf(stderr, "switchroot: chdir failed: %m\n"); - errno=errnum; - return -1; + errnum=errno; + fprintf(stderr, "switchroot: chdir failed: %m\n"); + errno=errnum; + return -1; } recursiveRemove("/"); if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { @@ -179,11 +172,11 @@ int main(int argc, char *argv[]) } if (switchroot(newroot) < 0) { - fprintf(stderr, "switchroot has failed. Sorry.\n"); - return 1; + fprintf(stderr, "switchroot has failed. Sorry.\n"); + return 1; } if (access(initargs[0], X_OK)) - fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); + fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); /* get session leader */ setsid(); @@ -193,6 +186,3 @@ int main(int argc, char *argv[]) execv(initargs[0], initargs); } -/* - * vim:noet:ts=8:sw=8:sts=8 - */ -- cgit v1.2.3 From a6fc8b07f9fff4e055949e2386f8b494201d02ac Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 13:24:11 +0200 Subject: switch_root: rewrite to use fstatat() and unlinkat() Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 87 +++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 43 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 15203872..3c42c3d1 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -44,70 +44,71 @@ enum { }; /* remove all files/directories below dirName -- don't cross mountpoints */ -static int -recursiveRemove(char * dirName) +static int recursiveRemove(char *dirName) { - struct stat sb,rb; - DIR * dir; - struct dirent * d; - char * strBuf = alloca(strlen(dirName) + 1024); + struct stat rb; + DIR *dir; + int rc = -1; + int dfd; if (!(dir = opendir(dirName))) { printf("error opening %s: %m\n", dirName); - return 0; + goto done; } - if (fstat(dirfd(dir),&rb)) { + dfd = dirfd(dir); + + if (fstat(dfd, &rb)) { printf("unable to stat %s: %m\n", dirName); - closedir(dir); - return 0; + goto done; } - errno = 0; + while(1) { + struct dirent *d; - while ((d = readdir(dir))) { errno = 0; + if (!(d = readdir(dir))) { + if (errno) { + printf("error reading from %s: %m\n", dirName); + goto done; + } + break; /* end of directory */ + } - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { - errno = 0; + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; - } - strcpy(strBuf, dirName); - strcat(strBuf, "/"); - strcat(strBuf, d->d_name); + if (d->d_type == DT_DIR) { + struct stat sb; - if (lstat(strBuf, &sb)) { - printf("failed to stat %s: %m\n", strBuf); - errno = 0; - continue; - } + if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { + printf("failed to stat %s/%s: %m\n", + dirName, d->d_name); + continue; + } - /* only descend into subdirectories if device is same as dir */ - if (S_ISDIR(sb.st_mode)) { + /* remove subdirectories if device is same as dir */ if (sb.st_dev == rb.st_dev) { - recursiveRemove(strBuf); - if (rmdir(strBuf)) - printf("failed to rmdir %s: %m\n", strBuf); - } - errno = 0; - continue; - } - if (unlink(strBuf)) { - printf("failed to remove %s: %m\n", strBuf); - errno = 0; - continue; + char subdir[ strlen(dirName) + + strlen(d->d_name) + 2 ]; + + sprintf(subdir, "%s/%s", dirName, d->d_name); + recursiveRemove(subdir); + } else + continue; } - } - if (errno) { - closedir(dir); - printf("error reading from %s: %m\n", dirName); - return 1; + if (unlinkat(dfd, d->d_name, + d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) + printf("failed to unlink %s/%s: %m\n", dirName, d->d_name); } - closedir(dir); - return 0; + rc = 0; /* success */ + +done: + if (dir) + closedir(dir); + return rc; } static int switchroot(const char *newroot) -- cgit v1.2.3 From fcb495b1df4ca3ece07b43c3adc0f1d034cc8136 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 15:54:00 +0200 Subject: switch_root: use err.h, clean up return codes Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 53 +++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 3c42c3d1..5dcc7f1e 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -32,6 +32,7 @@ #include #include #include +#include #ifndef MS_MOVE #define MS_MOVE 8192 @@ -52,14 +53,14 @@ static int recursiveRemove(char *dirName) int dfd; if (!(dir = opendir(dirName))) { - printf("error opening %s: %m\n", dirName); + warn("failed to open %s", dirName); goto done; } dfd = dirfd(dir); if (fstat(dfd, &rb)) { - printf("unable to stat %s: %m\n", dirName); + warn("failed to stat %s", dirName); goto done; } @@ -69,7 +70,7 @@ static int recursiveRemove(char *dirName) errno = 0; if (!(d = readdir(dir))) { if (errno) { - printf("error reading from %s: %m\n", dirName); + warn("failed to read %s", dirName); goto done; } break; /* end of directory */ @@ -82,8 +83,7 @@ static int recursiveRemove(char *dirName) struct stat sb; if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { - printf("failed to stat %s/%s: %m\n", - dirName, d->d_name); + warn("failed to stat %s/%s", dirName, d->d_name); continue; } @@ -100,7 +100,7 @@ static int recursiveRemove(char *dirName) if (unlinkat(dfd, d->d_name, d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) - printf("failed to unlink %s/%s: %m\n", dirName, d->d_name); + warn("failed to unlink %s/%s", dirName, d->d_name); } rc = 0; /* success */ @@ -123,42 +123,39 @@ static int switchroot(const char *newroot) strcpy(newmount, newroot); strcat(newmount, umounts[i]); if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { - fprintf(stderr, "Error mount moving old %s %s %m\n", + warn("failed to mount moving %s to %s", umounts[i], newmount); - fprintf(stderr, "Forcing unmount of %s\n", umounts[i]); + warnx("forcing unmount of %s", umounts[i]); umount2(umounts[i], MNT_FORCE); } } - if (chdir(newroot) < 0) { - errnum=errno; - fprintf(stderr, "switchroot: chdir failed: %m\n"); - errno=errnum; + if (chdir(newroot)) { + warn("failed to change directory to %s", newroot); return -1; } + recursiveRemove("/"); + if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { - errnum = errno; - fprintf(stderr, "switchroot: mount failed: %m\n"); - errno = errnum; + warn("failed to mount moving %s to /", newroot); return -1; } if (chroot(".")) { - errnum = errno; - fprintf(stderr, "switchroot: chroot failed: %m\n"); - errno = errnum; - return -2; + warn("failed to change root"); + return -1; } - return 1; + return 0; } static void usage(FILE *output) { - fprintf(output, "usage: switchroot \n"); + fprintf(output, "usage: %s \n", + program_invocation_short_name); if (output == stderr) - exit(err_usage); - exit(ok); + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) @@ -172,12 +169,11 @@ int main(int argc, char *argv[]) usage(stderr); } - if (switchroot(newroot) < 0) { - fprintf(stderr, "switchroot has failed. Sorry.\n"); - return 1; - } + if (switchroot(newroot)) + errx(EXIT_FAILURE, "failed. Sorry."); + if (access(initargs[0], X_OK)) - fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); + warn("cannot access %s", initargs[0]); /* get session leader */ setsid(); @@ -185,5 +181,6 @@ int main(int argc, char *argv[]) ioctl (0, TIOCSCTTY, 1); execv(initargs[0], initargs); + err(EXIT_FAILURE, "failed to execute %s", initargs[0]); } -- cgit v1.2.3 From 8b6457d06a1e27228975b35418cacb1bc3027201 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 16:16:46 +0200 Subject: switch_root: clean up argv[] usage, add -h and -V Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 5dcc7f1e..14d2916b 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -38,12 +38,6 @@ #define MS_MOVE 8192 #endif -enum { - ok, - err_no_directory, - err_usage, -}; - /* remove all files/directories below dirName -- don't cross mountpoints */ static int recursiveRemove(char *dirName) { @@ -115,7 +109,6 @@ static int switchroot(const char *newroot) { /* Don't try to unmount the old "/", there's no way to do it. */ const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; - int errnum; int i; for (i = 0; umounts[i] != NULL; i++) { @@ -153,34 +146,48 @@ static void usage(FILE *output) { fprintf(output, "usage: %s \n", program_invocation_short_name); - if (output == stderr) - exit(EXIT_FAILURE); + exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void version(void) +{ + fprintf(stdout, "%s from %s\n", program_invocation_short_name, + PACKAGE_STRING); exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { - char *newroot = argv[1]; - char *init = argv[2]; - char **initargs = &argv[2]; + char *newroot, *init, **initargs; - if (newroot == NULL || newroot[0] == '\0' || - init == NULL || init[0] == '\0' ) { + if (argv[1] && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) + usage(stdout); + if (argv[1] && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-V"))) + version(); + if (argc < 3) + usage(stderr); + + newroot = argv[1]; + init = argv[2]; + initargs = &argv[2]; + + if (!*newroot || !*init) usage(stderr); - } if (switchroot(newroot)) errx(EXIT_FAILURE, "failed. Sorry."); - if (access(initargs[0], X_OK)) - warn("cannot access %s", initargs[0]); + if (access(init, X_OK)) + warn("cannot access %s", init); /* get session leader */ setsid(); + /* set controlling terminal */ - ioctl (0, TIOCSCTTY, 1); + if (ioctl (0, TIOCSCTTY, 1)) + warn("failed to TIOCSCTTY"); - execv(initargs[0], initargs); - err(EXIT_FAILURE, "failed to execute %s", initargs[0]); + execv(init, initargs); + err(EXIT_FAILURE, "failed to execute %s", init); } -- cgit v1.2.3 From 8f24e52ed75ea16962a40a9628ae9ef85a2d034c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 9 Jun 2009 16:35:07 +0200 Subject: switch_root: use snprintf() rather tan str{cpy,cat}() Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 14d2916b..8daacb1d 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -1,7 +1,7 @@ /* * switchroot.c - switch to new root directory and start init. * - * Copyright 2002-2008 Red Hat, Inc. All rights reserved. + * Copyright 2002-2009 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -113,8 +113,9 @@ static int switchroot(const char *newroot) for (i = 0; umounts[i] != NULL; i++) { char newmount[PATH_MAX]; - strcpy(newmount, newroot); - strcat(newmount, umounts[i]); + + snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]); + if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { warn("failed to mount moving %s to %s", umounts[i], newmount); -- cgit v1.2.3 From 82476a9080f1ce8d1d8701f1b2c879c1e5b7523c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Jun 2009 15:21:58 -0400 Subject: switch_root: use file descriptor instead of path for recursiveRemove() This makes recursiveRemove() use fdopendir() instead of taking a path, so we're always sure about which namespace we're starting from. Signed-off-by: Peter Jones Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 8daacb1d..75725e70 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -39,22 +39,23 @@ #endif /* remove all files/directories below dirName -- don't cross mountpoints */ -static int recursiveRemove(char *dirName) +static int recursiveRemove(int fd) { struct stat rb; DIR *dir; int rc = -1; int dfd; - if (!(dir = opendir(dirName))) { - warn("failed to open %s", dirName); + if (!(dir = fdopendir(fd))) { + warn("failed to open directory"); goto done; } + /* fdopendir() precludes us from continuing to use the input fd */ dfd = dirfd(dir); if (fstat(dfd, &rb)) { - warn("failed to stat %s", dirName); + warn("failed to stat directory"); goto done; } @@ -64,7 +65,7 @@ static int recursiveRemove(char *dirName) errno = 0; if (!(d = readdir(dir))) { if (errno) { - warn("failed to read %s", dirName); + warn("failed to read directory"); goto done; } break; /* end of directory */ @@ -77,24 +78,26 @@ static int recursiveRemove(char *dirName) struct stat sb; if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { - warn("failed to stat %s/%s", dirName, d->d_name); + warn("failed to stat %s", d->d_name); continue; } /* remove subdirectories if device is same as dir */ if (sb.st_dev == rb.st_dev) { - char subdir[ strlen(dirName) + - strlen(d->d_name) + 2 ]; + int cfd; - sprintf(subdir, "%s/%s", dirName, d->d_name); - recursiveRemove(subdir); + cfd = openat(dfd, d->d_name, O_RDONLY); + if (cfd >= 0) { + recursiveRemove(cfd); + close(cfd); + } } else continue; } if (unlinkat(dfd, d->d_name, d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) - warn("failed to unlink %s/%s", dirName, d->d_name); + warn("failed to unlink %s", d->d_name); } rc = 0; /* success */ @@ -110,6 +113,7 @@ static int switchroot(const char *newroot) /* Don't try to unmount the old "/", there's no way to do it. */ const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; int i; + int cfd; for (i = 0; umounts[i] != NULL; i++) { char newmount[PATH_MAX]; @@ -129,7 +133,11 @@ static int switchroot(const char *newroot) return -1; } - recursiveRemove("/"); + cfd = open("/", O_RDONLY); + if (cfd >= 0) { + recursiveRemove(cfd); + close(cfd); + } if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { warn("failed to mount moving %s to /", newroot); -- cgit v1.2.3 From 4c2d96e62a081aa0441a7724323a45035413da56 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Jun 2009 15:21:59 -0400 Subject: switch_root: fork before cleaning up the filesystem. Fork before cleaning up the old filesystem, so it becomes asyncronous, which results in a faster boot time. Signed-off-by: Peter Jones --- sys-utils/switch_root.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 75725e70..1de3a2e5 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -114,6 +114,7 @@ static int switchroot(const char *newroot) const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; int i; int cfd; + pid_t pid; for (i = 0; umounts[i] != NULL; i++) { char newmount[PATH_MAX]; @@ -135,7 +136,12 @@ static int switchroot(const char *newroot) cfd = open("/", O_RDONLY); if (cfd >= 0) { - recursiveRemove(cfd); + pid = fork(); + if (pid <= 0) { + recursiveRemove(cfd); + if (pid == 0) + exit(EXIT_SUCCESS); + } close(cfd); } -- cgit v1.2.3 From 2a7ccc65e998fb5b5ad679866730bcdbfc2c1f88 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 19 Jun 2009 15:44:32 -0400 Subject: switch_root: do recursiveRemove after our root is moved to avoid races. This way there's no race between unlinking the /newroot directory and the MS_MOVE/chroot() to get away from it. Signed-off-by: Peter Jones --- sys-utils/switch_root.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index 1de3a2e5..b947f544 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -135,15 +135,6 @@ static int switchroot(const char *newroot) } cfd = open("/", O_RDONLY); - if (cfd >= 0) { - pid = fork(); - if (pid <= 0) { - recursiveRemove(cfd); - if (pid == 0) - exit(EXIT_SUCCESS); - } - close(cfd); - } if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { warn("failed to mount moving %s to /", newroot); @@ -154,6 +145,16 @@ static int switchroot(const char *newroot) warn("failed to change root"); return -1; } + + if (cfd >= 0) { + pid = fork(); + if (pid <= 0) { + recursiveRemove(cfd); + if (pid == 0) + exit(EXIT_SUCCESS); + } + close(cfd); + } return 0; } -- cgit v1.2.3 From a692a8745941a192528c5e2a05de97155ba586f9 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 14 Jul 2009 14:41:33 +0200 Subject: switch_root: add subroot support The current switch_root can only switch to a new root that is the root of a mount point. This patch adds support for "subroots", where the new root is somewhere below a mount point. It does this by adding in a few extra steps to chroot into the subroot after the enclosing partition has been moved and entered. This will be used by OLPC, who sort-of have 2 copies of Fedora stored on a single partition under different directory trees, where the initramfs decides which one to boot into [kzak@redhat.com: - port to the current u-l-ng switch_root code - don't use static buffer for "dir" in get_parent_mount()] CC: Peter Jones Signed-off-by: Daniel Drake Signed-off-by: Karel Zak --- sys-utils/switch_root.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 5 deletions(-) (limited to 'sys-utils/switch_root.c') diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c index b947f544..b192a08e 100644 --- a/sys-utils/switch_root.c +++ b/sys-utils/switch_root.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifndef MS_MOVE #define MS_MOVE 8192 @@ -108,13 +109,58 @@ done: return rc; } +/* find the enclosing mount point of a path, by examining the backing device + * of parent directories until we reach / or find a parent with a differing + * device. Result must be freed. + */ +static char *get_parent_mount(const char *path) +{ + struct stat sb; + char *dir; + char tmp[PATH_MAX]; + dev_t inner_dev; + + if (stat(path, &sb) != 0) { + warn("failed to stat %s", path); + return NULL; + } + + inner_dev = sb.st_dev; + dir = strdup(path); + + while (dir) { + char *parent; + + strncpy(tmp, dir, PATH_MAX); + tmp[PATH_MAX - 1] = '\0'; + parent = dirname(tmp); + + if (stat(parent, &sb) != 0) { + warn("failed to stat %s", parent); + return NULL; + } + if (sb.st_dev != inner_dev) + return dir; + + strncpy(dir, parent, PATH_MAX); + dir[PATH_MAX - 1] = '\0'; + + /* maybe we've reached / */ + if (*dir == '/' && !*(dir + 1)) + return dir; + } + return NULL; +} + static int switchroot(const char *newroot) { /* Don't try to unmount the old "/", there's no way to do it. */ const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; int i; - int cfd; + int cfd, rc = -1; pid_t pid; + const char *chroot_path = NULL; + char *newroot_mnt; for (i = 0; umounts[i] != NULL; i++) { char newmount[PATH_MAX]; @@ -129,21 +175,43 @@ static int switchroot(const char *newroot) } } + newroot_mnt = get_parent_mount(newroot); + if (newroot_mnt && strcmp(newroot, newroot_mnt)) { + /* newroot is not a mount point, so we have to MS_MOVE the + * parent mount point and then chroot in to the "subroot" + */ + chroot_path = newroot + strlen(newroot_mnt); + newroot = newroot_mnt; + } + if (chdir(newroot)) { warn("failed to change directory to %s", newroot); - return -1; + goto done; } cfd = open("/", O_RDONLY); if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { warn("failed to mount moving %s to /", newroot); - return -1; + goto done; } + /* move to the real root of the device */ if (chroot(".")) { warn("failed to change root"); - return -1; + goto done; + } + + /* move to the subdirectory on the root device (subroot) */ + if (chroot_path) { + if (chdir(chroot_path)) { + warn("failed to chdir to subroot %s", chroot_path); + goto done; + } + if (chroot(".")) { + warn("failed to change root to subroot %s", chroot_path); + goto done; + } } if (cfd >= 0) { @@ -155,7 +223,10 @@ static int switchroot(const char *newroot) } close(cfd); } - return 0; + rc = 0; +done: + free(newroot_mnt); + return rc; } static void usage(FILE *output) -- cgit v1.2.3