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