]>
Commit | Line | Data |
---|---|---|
711ea730 KZ |
1 | /* |
2 | * switchroot.c - switch to new root directory and start init. | |
3 | * | |
8f24e52e | 4 | * Copyright 2002-2009 Red Hat, Inc. All rights reserved. |
711ea730 KZ |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | * Authors: | |
20 | * Peter Jones <pjones@redhat.com> | |
21 | * Jeremy Katz <katzj@redhat.com> | |
22 | */ | |
711ea730 KZ |
23 | #include <sys/mount.h> |
24 | #include <sys/types.h> | |
25 | #include <sys/stat.h> | |
26 | #include <sys/param.h> | |
27 | #include <fcntl.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <unistd.h> | |
31 | #include <string.h> | |
32 | #include <errno.h> | |
33 | #include <ctype.h> | |
34 | #include <dirent.h> | |
eb76ca98 | 35 | #include "c.h" |
711ea730 KZ |
36 | |
37 | #ifndef MS_MOVE | |
38 | #define MS_MOVE 8192 | |
39 | #endif | |
40 | ||
711ea730 | 41 | /* remove all files/directories below dirName -- don't cross mountpoints */ |
82476a90 | 42 | static int recursiveRemove(int fd) |
3ddbe4d2 | 43 | { |
a6fc8b07 KZ |
44 | struct stat rb; |
45 | DIR *dir; | |
46 | int rc = -1; | |
47 | int dfd; | |
3ddbe4d2 | 48 | |
82476a90 PJ |
49 | if (!(dir = fdopendir(fd))) { |
50 | warn("failed to open directory"); | |
a6fc8b07 | 51 | goto done; |
3ddbe4d2 KZ |
52 | } |
53 | ||
82476a90 | 54 | /* fdopendir() precludes us from continuing to use the input fd */ |
a6fc8b07 KZ |
55 | dfd = dirfd(dir); |
56 | ||
57 | if (fstat(dfd, &rb)) { | |
82476a90 | 58 | warn("failed to stat directory"); |
a6fc8b07 | 59 | goto done; |
3ddbe4d2 KZ |
60 | } |
61 | ||
a6fc8b07 KZ |
62 | while(1) { |
63 | struct dirent *d; | |
3ddbe4d2 | 64 | |
3ddbe4d2 | 65 | errno = 0; |
a6fc8b07 KZ |
66 | if (!(d = readdir(dir))) { |
67 | if (errno) { | |
82476a90 | 68 | warn("failed to read directory"); |
a6fc8b07 KZ |
69 | goto done; |
70 | } | |
71 | break; /* end of directory */ | |
72 | } | |
3ddbe4d2 | 73 | |
a6fc8b07 | 74 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) |
3ddbe4d2 | 75 | continue; |
3ddbe4d2 | 76 | |
a6fc8b07 KZ |
77 | if (d->d_type == DT_DIR) { |
78 | struct stat sb; | |
3ddbe4d2 | 79 | |
a6fc8b07 | 80 | if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { |
82476a90 | 81 | warn("failed to stat %s", d->d_name); |
a6fc8b07 KZ |
82 | continue; |
83 | } | |
3ddbe4d2 | 84 | |
a6fc8b07 | 85 | /* remove subdirectories if device is same as dir */ |
3ddbe4d2 | 86 | if (sb.st_dev == rb.st_dev) { |
82476a90 | 87 | int cfd; |
a6fc8b07 | 88 | |
82476a90 PJ |
89 | cfd = openat(dfd, d->d_name, O_RDONLY); |
90 | if (cfd >= 0) { | |
91 | recursiveRemove(cfd); | |
92 | close(cfd); | |
93 | } | |
a6fc8b07 KZ |
94 | } else |
95 | continue; | |
3ddbe4d2 | 96 | } |
3ddbe4d2 | 97 | |
a6fc8b07 KZ |
98 | if (unlinkat(dfd, d->d_name, |
99 | d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) | |
82476a90 | 100 | warn("failed to unlink %s", d->d_name); |
3ddbe4d2 KZ |
101 | } |
102 | ||
a6fc8b07 KZ |
103 | rc = 0; /* success */ |
104 | ||
105 | done: | |
106 | if (dir) | |
107 | closedir(dir); | |
108 | return rc; | |
3ddbe4d2 | 109 | } |
711ea730 KZ |
110 | |
111 | static int switchroot(const char *newroot) | |
112 | { | |
113 | /* Don't try to unmount the old "/", there's no way to do it. */ | |
114 | const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; | |
711ea730 | 115 | int i; |
558417e8 | 116 | int cfd; |
4c2d96e6 | 117 | pid_t pid; |
711ea730 KZ |
118 | |
119 | for (i = 0; umounts[i] != NULL; i++) { | |
120 | char newmount[PATH_MAX]; | |
8f24e52e KZ |
121 | |
122 | snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]); | |
123 | ||
711ea730 | 124 | if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { |
fcb495b1 | 125 | warn("failed to mount moving %s to %s", |
711ea730 | 126 | umounts[i], newmount); |
fcb495b1 | 127 | warnx("forcing unmount of %s", umounts[i]); |
711ea730 KZ |
128 | umount2(umounts[i], MNT_FORCE); |
129 | } | |
130 | } | |
131 | ||
fcb495b1 KZ |
132 | if (chdir(newroot)) { |
133 | warn("failed to change directory to %s", newroot); | |
558417e8 | 134 | return -1; |
711ea730 | 135 | } |
fcb495b1 | 136 | |
82476a90 | 137 | cfd = open("/", O_RDONLY); |
fcb495b1 | 138 | |
711ea730 | 139 | if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { |
fcb495b1 | 140 | warn("failed to mount moving %s to /", newroot); |
558417e8 | 141 | return -1; |
711ea730 KZ |
142 | } |
143 | ||
144 | if (chroot(".")) { | |
fcb495b1 | 145 | warn("failed to change root"); |
558417e8 | 146 | return -1; |
711ea730 | 147 | } |
2a7ccc65 PJ |
148 | |
149 | if (cfd >= 0) { | |
150 | pid = fork(); | |
151 | if (pid <= 0) { | |
152 | recursiveRemove(cfd); | |
153 | if (pid == 0) | |
154 | exit(EXIT_SUCCESS); | |
155 | } | |
156 | close(cfd); | |
157 | } | |
558417e8 | 158 | return 0; |
711ea730 KZ |
159 | } |
160 | ||
161 | static void usage(FILE *output) | |
162 | { | |
fcb495b1 KZ |
163 | fprintf(output, "usage: %s <newrootdir> <init> <args to init>\n", |
164 | program_invocation_short_name); | |
8b6457d0 KZ |
165 | exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS); |
166 | } | |
167 | ||
168 | static void version(void) | |
169 | { | |
170 | fprintf(stdout, "%s from %s\n", program_invocation_short_name, | |
171 | PACKAGE_STRING); | |
fcb495b1 | 172 | exit(EXIT_SUCCESS); |
711ea730 KZ |
173 | } |
174 | ||
175 | int main(int argc, char *argv[]) | |
176 | { | |
8b6457d0 | 177 | char *newroot, *init, **initargs; |
711ea730 | 178 | |
8b6457d0 KZ |
179 | if (argv[1] && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) |
180 | usage(stdout); | |
181 | if (argv[1] && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-V"))) | |
182 | version(); | |
183 | if (argc < 3) | |
184 | usage(stderr); | |
185 | ||
186 | newroot = argv[1]; | |
187 | init = argv[2]; | |
188 | initargs = &argv[2]; | |
189 | ||
190 | if (!*newroot || !*init) | |
711ea730 | 191 | usage(stderr); |
711ea730 | 192 | |
fcb495b1 KZ |
193 | if (switchroot(newroot)) |
194 | errx(EXIT_FAILURE, "failed. Sorry."); | |
195 | ||
8b6457d0 KZ |
196 | if (access(init, X_OK)) |
197 | warn("cannot access %s", init); | |
711ea730 | 198 | |
8b6457d0 KZ |
199 | execv(init, initargs); |
200 | err(EXIT_FAILURE, "failed to execute %s", init); | |
711ea730 KZ |
201 | } |
202 |