1/* vi: set sw=4 ts=4: */ 2/* Copyright 2005 Rob Landley <rob@landley.net> 3 * 4 * Switch from rootfs to another filesystem as the root of the mount tree. 5 * 6 * Licensed under GPL version 2, see file LICENSE in this tarball for details. 7 */ 8 9#include "libbb.h" 10#include <sys/vfs.h> 11 12 13// Make up for header deficiencies. 14 15#ifndef RAMFS_MAGIC 16#define RAMFS_MAGIC 0x858458f6 17#endif 18 19#ifndef TMPFS_MAGIC 20#define TMPFS_MAGIC 0x01021994 21#endif 22 23#ifndef MS_MOVE 24#define MS_MOVE 8192 25#endif 26 27static dev_t rootdev; 28 29// Recursively delete contents of rootfs. 30 31static void delete_contents(const char *directory) 32{ 33 DIR *dir; 34 struct dirent *d; 35 struct stat st; 36 37 // Don't descend into other filesystems 38 if (lstat(directory, &st) || st.st_dev != rootdev) return; 39 40 // Recursively delete the contents of directories. 41 if (S_ISDIR(st.st_mode)) { 42 dir = opendir(directory); 43 if (dir) { 44 while ((d = readdir(dir))) { 45 char *newdir = d->d_name; 46 47 // Skip . and .. 48 if (*newdir=='.' && (!newdir[1] || (newdir[1]=='.' && !newdir[2]))) 49 continue; 50 51 // Recurse to delete contents 52 newdir = alloca(strlen(directory) + strlen(d->d_name) + 2); 53 sprintf(newdir, "%s/%s", directory, d->d_name); 54 delete_contents(newdir); 55 } 56 closedir(dir); 57 58 // Directory should now be empty. Zap it. 59 rmdir(directory); 60 } 61 62 // It wasn't a directory. Zap it. 63 64 } else unlink(directory); 65} 66 67int switch_root_main(int argc, char **argv); 68int switch_root_main(int argc, char **argv) 69{ 70 char *newroot, *console = NULL; 71 struct stat st1, st2; 72 struct statfs stfs; 73 74 // Parse args (-c console) 75 76 opt_complementary = "-2"; 77 getopt32(argv, "c:", &console); 78 argv += optind; 79 80 // Change to new root directory and verify it's a different fs. 81 82 newroot = *argv++; 83 84 xchdir(newroot); 85 if (lstat(".", &st1) || lstat("/", &st2) || st1.st_dev == st2.st_dev) { 86 bb_error_msg_and_die("bad newroot %s", newroot); 87 } 88 rootdev = st2.st_dev; 89 90 // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE 91 // we mean it. (I could make this a CONFIG option, but I would get email 92 // from all the people who WILL eat their filesystems.) 93 94 if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) || 95 (stfs.f_type != RAMFS_MAGIC && stfs.f_type != TMPFS_MAGIC) || 96 getpid() != 1) 97 { 98 bb_error_msg_and_die("not rootfs"); 99 } 100 101 // Zap everything out of rootdev 102 103 delete_contents("/"); 104 105 // Overmount / with newdir and chroot into it. The chdir is needed to 106 // recalculate "." and ".." links. 107 108 if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".")) 109 bb_error_msg_and_die("error moving root"); 110 xchdir("/"); 111 112 // If a new console specified, redirect stdin/stdout/stderr to that. 113 114 if (console) { 115 close(0); 116 xopen(console, O_RDWR); 117 dup2(0, 1); 118 dup2(0, 2); 119 } 120 121 // Exec real init. (This is why we must be pid 1.) 122 execv(argv[0], argv); 123 bb_perror_msg_and_die("bad init %s", argv[0]); 124} 125