X-Git-Url: http://git.ipfire.org/?p=people%2Fteissler%2Fipfire-2.x.git;a=blobdiff_plain;f=src%2Fdracut%2Fswitch_root.c;fp=src%2Fdracut%2Fswitch_root.c;h=1520387239e1d8d0ac797f57ae08d474be4a7e8d;hp=0000000000000000000000000000000000000000;hb=41c0d0cab7d24d6bf2fa1f3ada1c86da79aaa762;hpb=6f21706ddaf26fbae1d3118bef93e25db90aef29 diff --git a/src/dracut/switch_root.c b/src/dracut/switch_root.c new file mode 100644 index 000000000..152038723 --- /dev/null +++ b/src/dracut/switch_root.c @@ -0,0 +1,188 @@ +/* + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MS_MOVE +#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) +{ + 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); +} +