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