diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-21 20:40:51 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-21 20:40:51 +0000 |
commit | f9d4fc3cf8ca91dbebfa305c5c08f8781caa1a0f (patch) | |
tree | b341f97661b629cc112ebe09933c044ebb31a6f7 /util-linux/switch_root.c | |
parent | 950bd729665cecf1fbee65bc6e57e087c93aaab6 (diff) | |
download | busybox-w32-f9d4fc3cf8ca91dbebfa305c5c08f8781caa1a0f.tar.gz busybox-w32-f9d4fc3cf8ca91dbebfa305c5c08f8781caa1a0f.tar.bz2 busybox-w32-f9d4fc3cf8ca91dbebfa305c5c08f8781caa1a0f.zip |
switch_root: improve behavior on error; improve help text
*: make "can't execute '%s'" message uniform
Diffstat (limited to 'util-linux/switch_root.c')
-rw-r--r-- | util-linux/switch_root.c | 62 |
1 files changed, 34 insertions, 28 deletions
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c index 08aa72597..f9e3444d5 100644 --- a/util-linux/switch_root.c +++ b/util-linux/switch_root.c | |||
@@ -5,11 +5,10 @@ | |||
5 | * | 5 | * |
6 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | 6 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. |
7 | */ | 7 | */ |
8 | |||
9 | #include "libbb.h" | 8 | #include "libbb.h" |
10 | #include <sys/vfs.h> | 9 | #include <sys/vfs.h> |
11 | 10 | ||
12 | // Make up for header deficiencies. | 11 | // Make up for header deficiencies |
13 | #ifndef RAMFS_MAGIC | 12 | #ifndef RAMFS_MAGIC |
14 | #define RAMFS_MAGIC ((unsigned)0x858458f6) | 13 | #define RAMFS_MAGIC ((unsigned)0x858458f6) |
15 | #endif | 14 | #endif |
@@ -22,7 +21,7 @@ | |||
22 | #define MS_MOVE 8192 | 21 | #define MS_MOVE 8192 |
23 | #endif | 22 | #endif |
24 | 23 | ||
25 | // Recursively delete contents of rootfs. | 24 | // Recursively delete contents of rootfs |
26 | static void delete_contents(const char *directory, dev_t rootdev) | 25 | static void delete_contents(const char *directory, dev_t rootdev) |
27 | { | 26 | { |
28 | DIR *dir; | 27 | DIR *dir; |
@@ -33,7 +32,7 @@ static void delete_contents(const char *directory, dev_t rootdev) | |||
33 | if (lstat(directory, &st) || st.st_dev != rootdev) | 32 | if (lstat(directory, &st) || st.st_dev != rootdev) |
34 | return; | 33 | return; |
35 | 34 | ||
36 | // Recursively delete the contents of directories. | 35 | // Recursively delete the contents of directories |
37 | if (S_ISDIR(st.st_mode)) { | 36 | if (S_ISDIR(st.st_mode)) { |
38 | dir = opendir(directory); | 37 | dir = opendir(directory); |
39 | if (dir) { | 38 | if (dir) { |
@@ -51,42 +50,47 @@ static void delete_contents(const char *directory, dev_t rootdev) | |||
51 | } | 50 | } |
52 | closedir(dir); | 51 | closedir(dir); |
53 | 52 | ||
54 | // Directory should now be empty. Zap it. | 53 | // Directory should now be empty, zap it |
55 | rmdir(directory); | 54 | rmdir(directory); |
56 | } | 55 | } |
57 | 56 | } else { | |
58 | // It wasn't a directory. Zap it. | 57 | // It wasn't a directory, zap it |
59 | } else unlink(directory); | 58 | unlink(directory); |
59 | } | ||
60 | } | 60 | } |
61 | 61 | ||
62 | int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 62 | int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
63 | int switch_root_main(int argc UNUSED_PARAM, char **argv) | 63 | int switch_root_main(int argc UNUSED_PARAM, char **argv) |
64 | { | 64 | { |
65 | char *newroot, *console = NULL; | 65 | char *newroot, *console = NULL; |
66 | struct stat st1, st2; | 66 | struct stat st; |
67 | struct statfs stfs; | 67 | struct statfs stfs; |
68 | dev_t rootdev; | 68 | dev_t rootdev; |
69 | 69 | ||
70 | // Parse args (-c console) | 70 | // Parse args (-c console) |
71 | opt_complementary = "-2"; // minimum 2 params | 71 | opt_complementary = "-2"; // minimum 2 params |
72 | getopt32(argv, "+c:", &console); // '+': stop parsing at first non-option | 72 | getopt32(argv, "+c:", &console); // '+': stop at first non-option |
73 | argv += optind; | 73 | argv += optind; |
74 | |||
75 | // Change to new root directory and verify it's a different fs. | ||
76 | newroot = *argv++; | 74 | newroot = *argv++; |
77 | 75 | ||
76 | // Change to new root directory and verify it's a different fs | ||
78 | xchdir(newroot); | 77 | xchdir(newroot); |
79 | if (lstat(".", &st1) || lstat("/", &st2) || st1.st_dev == st2.st_dev) { | 78 | xstat("/", &st); |
80 | bb_error_msg_and_die("bad newroot %s", newroot); | 79 | rootdev = st.st_dev; |
80 | xstat(".", &st); | ||
81 | if (st.st_dev == rootdev || getpid() != 1) { | ||
82 | // Show usage, it says new root must be a mountpoint | ||
83 | // and we must be PID 1 | ||
84 | bb_show_usage(); | ||
81 | } | 85 | } |
82 | rootdev = st2.st_dev; | 86 | |
83 | 87 | // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE | |
84 | // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE | 88 | // we mean it. I could make this a CONFIG option, but I would get email |
85 | // we mean it. (I could make this a CONFIG option, but I would get email | 89 | // from all the people who WILL destroy their filesystems. |
86 | // from all the people who WILL eat their filesystems.) | 90 | statfs("/", &stfs); // this never fails |
87 | if (lstat("/init", &st1) || !S_ISREG(st1.st_mode) || statfs("/", &stfs) | 91 | if (lstat("/init", &st) != 0 || !S_ISREG(st.st_mode) |
88 | || (((unsigned)stfs.f_type != RAMFS_MAGIC) && ((unsigned)stfs.f_type != TMPFS_MAGIC)) | 92 | || ((unsigned)stfs.f_type != RAMFS_MAGIC |
89 | || (getpid() != 1) | 93 | && (unsigned)stfs.f_type != TMPFS_MAGIC) |
90 | ) { | 94 | ) { |
91 | bb_error_msg_and_die("not rootfs"); | 95 | bb_error_msg_and_die("not rootfs"); |
92 | } | 96 | } |
@@ -94,14 +98,16 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv) | |||
94 | // Zap everything out of rootdev | 98 | // Zap everything out of rootdev |
95 | delete_contents("/", rootdev); | 99 | delete_contents("/", rootdev); |
96 | 100 | ||
97 | // Overmount / with newdir and chroot into it. The chdir is needed to | 101 | // Overmount / with newdir and chroot into it |
98 | // recalculate "." and ".." links. | 102 | if (mount(".", "/", NULL, MS_MOVE, NULL)) { |
99 | if (mount(".", "/", NULL, MS_MOVE, NULL)) | 103 | // For example, fails when newroot is not a mountpoint |
100 | bb_perror_msg_and_die("error moving root"); | 104 | bb_perror_msg_and_die("error moving root"); |
105 | } | ||
106 | // The chdir is needed to recalculate "." and ".." links | ||
101 | xchroot("."); | 107 | xchroot("."); |
102 | xchdir("/"); | 108 | xchdir("/"); |
103 | 109 | ||
104 | // If a new console specified, redirect stdin/stdout/stderr to that. | 110 | // If a new console specified, redirect stdin/stdout/stderr to it |
105 | if (console) { | 111 | if (console) { |
106 | close(0); | 112 | close(0); |
107 | xopen(console, O_RDWR); | 113 | xopen(console, O_RDWR); |
@@ -109,7 +115,7 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv) | |||
109 | xdup2(0, 2); | 115 | xdup2(0, 2); |
110 | } | 116 | } |
111 | 117 | ||
112 | // Exec real init. (This is why we must be pid 1.) | 118 | // Exec real init |
113 | execv(argv[0], argv); | 119 | execv(argv[0], argv); |
114 | bb_perror_msg_and_die("bad init %s", argv[0]); | 120 | bb_perror_msg_and_die("can't execute '%s'", argv[0]); |
115 | } | 121 | } |