diff options
Diffstat (limited to 'util-linux/switch_root.c')
-rw-r--r-- | util-linux/switch_root.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c new file mode 100644 index 000000000..4c23f69da --- /dev/null +++ b/util-linux/switch_root.c | |||
@@ -0,0 +1,122 @@ | |||
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 "busybox.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 | |||
27 | dev_t rootdev; | ||
28 | |||
29 | // Recursively delete contents of rootfs. | ||
30 | |||
31 | static void delete_contents(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 | if((dir = opendir(directory))) { | ||
43 | while ((d = readdir(dir))) { | ||
44 | char *newdir=d->d_name; | ||
45 | |||
46 | // Skip . and .. | ||
47 | if(*newdir=='.' && (!newdir[1] || (newdir[1]=='.' && !newdir[2]))) | ||
48 | continue; | ||
49 | |||
50 | // Recurse to delete contents | ||
51 | newdir = alloca(strlen(directory) + strlen(d->d_name) + 2); | ||
52 | sprintf(newdir, "%s/%s", directory, d->d_name); | ||
53 | delete_contents(newdir); | ||
54 | } | ||
55 | closedir(dir); | ||
56 | |||
57 | // Directory should now be empty. Zap it. | ||
58 | rmdir(directory); | ||
59 | } | ||
60 | |||
61 | // It wasn't a directory. Zap it. | ||
62 | |||
63 | } else unlink(directory); | ||
64 | } | ||
65 | |||
66 | int switch_root_main(int argc, char *argv[]) | ||
67 | { | ||
68 | char *newroot, *console=NULL; | ||
69 | struct stat st1, st2; | ||
70 | struct statfs stfs; | ||
71 | |||
72 | // Parse args (-c console) | ||
73 | |||
74 | opt_complementary = "-2"; | ||
75 | getopt32(argc, argv, "c:", &console); | ||
76 | |||
77 | // Change to new root directory and verify it's a different fs. | ||
78 | |||
79 | newroot=argv[optind++]; | ||
80 | |||
81 | if (chdir(newroot) || lstat(".", &st1) || lstat("/", &st2) || | ||
82 | st1.st_dev == st2.st_dev) | ||
83 | { | ||
84 | bb_error_msg_and_die("bad newroot %s", newroot); | ||
85 | } | ||
86 | rootdev=st2.st_dev; | ||
87 | |||
88 | // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE | ||
89 | // we mean it. (I could make this a CONFIG option, but I would get email | ||
90 | // from all the people who WILL eat their filesystemss.) | ||
91 | |||
92 | if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) || | ||
93 | (stfs.f_type != RAMFS_MAGIC && stfs.f_type != TMPFS_MAGIC) || | ||
94 | getpid() != 1) | ||
95 | { | ||
96 | bb_error_msg_and_die("not rootfs"); | ||
97 | } | ||
98 | |||
99 | // Zap everything out of rootdev | ||
100 | |||
101 | delete_contents("/"); | ||
102 | |||
103 | // Overmount / with newdir and chroot into it. The chdir is needed to | ||
104 | // recalculate "." and ".." links. | ||
105 | |||
106 | if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/")) | ||
107 | bb_error_msg_and_die("moving root"); | ||
108 | |||
109 | // If a new console specified, redirect stdin/stdout/stderr to that. | ||
110 | |||
111 | if (console) { | ||
112 | close(0); | ||
113 | if(open(console, O_RDWR) < 0) | ||
114 | bb_error_msg_and_die("bad console '%s'", console); | ||
115 | dup2(0, 1); | ||
116 | dup2(0, 2); | ||
117 | } | ||
118 | |||
119 | // Exec real init. (This is why we must be pid 1.) | ||
120 | execv(argv[optind], argv+optind); | ||
121 | bb_error_msg_and_die("bad init '%s'", argv[optind]); | ||
122 | } | ||