/* vi: set sw=4 ts=4: */ /* Copyright 2005 Rob Landley * * Switch from rootfs to another filesystem as the root of the mount tree. * * Licensed under GPL version 2, see file LICENSE in this tarball for details. */ #include "libbb.h" #include // Make up for header deficiencies #ifndef RAMFS_MAGIC #define RAMFS_MAGIC ((unsigned)0x858458f6) #endif #ifndef TMPFS_MAGIC #define TMPFS_MAGIC ((unsigned)0x01021994) #endif #ifndef MS_MOVE #define MS_MOVE 8192 #endif // Recursively delete contents of rootfs static void delete_contents(const char *directory, dev_t rootdev) { DIR *dir; struct dirent *d; struct stat st; // Don't descend into other filesystems if (lstat(directory, &st) || st.st_dev != rootdev) return; // Recursively delete the contents of directories if (S_ISDIR(st.st_mode)) { dir = opendir(directory); if (dir) { while ((d = readdir(dir))) { char *newdir = d->d_name; // Skip . and .. if (DOT_OR_DOTDOT(newdir)) continue; // Recurse to delete contents newdir = concat_path_file(directory, newdir); delete_contents(newdir, rootdev); free(newdir); } closedir(dir); // Directory should now be empty, zap it rmdir(directory); } } else { // It wasn't a directory, zap it unlink(directory); } } int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int switch_root_main(int argc UNUSED_PARAM, char **argv) { char *newroot, *console = NULL; struct stat st; struct statfs stfs; dev_t rootdev; // Parse args (-c console) opt_complementary = "-2"; // minimum 2 params getopt32(argv, "+c:", &console); // '+': stop at first non-option argv += optind; newroot = *argv++; // Change to new root directory and verify it's a different fs xchdir(newroot); xstat("/", &st); rootdev = st.st_dev; xstat(".", &st); if (st.st_dev == rootdev || getpid() != 1) { // Show usage, it says new root must be a mountpoint // and we must be PID 1 bb_show_usage(); } // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE // we mean it. I could make this a CONFIG option, but I would get email // from all the people who WILL destroy their filesystems. statfs("/", &stfs); // this never fails if (lstat("/init", &st) != 0 || !S_ISREG(st.st_mode) || ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC) ) { bb_error_msg_and_die("not rootfs"); } // Zap everything out of rootdev delete_contents("/", rootdev); // Overmount / with newdir and chroot into it if (mount(".", "/", NULL, MS_MOVE, NULL)) { // For example, fails when newroot is not a mountpoint bb_perror_msg_and_die("error moving root"); } xchroot("."); // The chdir is needed to recalculate "." and ".." links xchdir("/"); // If a new console specified, redirect stdin/stdout/stderr to it if (console) { close(0); xopen(console, O_RDWR); xdup2(0, 1); xdup2(0, 2); } // Exec real init execv(argv[0], argv); bb_perror_msg_and_die("can't execute '%s'", argv[0]); }