]>
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> | |
ed8d2938 | 35 | |
eb76ca98 | 36 | #include "c.h" |
ed8d2938 | 37 | #include "nls.h" |
efb8854f | 38 | #include "closestream.h" |
711ea730 KZ |
39 | |
40 | #ifndef MS_MOVE | |
41 | #define MS_MOVE 8192 | |
42 | #endif | |
43 | ||
944de78b HH |
44 | #ifndef MNT_DETACH |
45 | #define MNT_DETACH 0x00000002 /* Just detach from the tree */ | |
46 | #endif | |
47 | ||
711ea730 | 48 | /* remove all files/directories below dirName -- don't cross mountpoints */ |
82476a90 | 49 | static int recursiveRemove(int fd) |
3ddbe4d2 | 50 | { |
a6fc8b07 KZ |
51 | struct stat rb; |
52 | DIR *dir; | |
53 | int rc = -1; | |
54 | int dfd; | |
3ddbe4d2 | 55 | |
82476a90 | 56 | if (!(dir = fdopendir(fd))) { |
0fbd4c85 | 57 | warn(_("failed to open directory")); |
a6fc8b07 | 58 | goto done; |
3ddbe4d2 KZ |
59 | } |
60 | ||
82476a90 | 61 | /* fdopendir() precludes us from continuing to use the input fd */ |
a6fc8b07 KZ |
62 | dfd = dirfd(dir); |
63 | ||
64 | if (fstat(dfd, &rb)) { | |
0fbd4c85 | 65 | warn(_("failed to stat directory")); |
a6fc8b07 | 66 | goto done; |
3ddbe4d2 KZ |
67 | } |
68 | ||
a6fc8b07 KZ |
69 | while(1) { |
70 | struct dirent *d; | |
3ddbe4d2 | 71 | |
3ddbe4d2 | 72 | errno = 0; |
a6fc8b07 KZ |
73 | if (!(d = readdir(dir))) { |
74 | if (errno) { | |
0fbd4c85 | 75 | warn(_("failed to read directory")); |
a6fc8b07 KZ |
76 | goto done; |
77 | } | |
78 | break; /* end of directory */ | |
79 | } | |
3ddbe4d2 | 80 | |
a6fc8b07 | 81 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) |
3ddbe4d2 | 82 | continue; |
3ddbe4d2 | 83 | |
a6fc8b07 KZ |
84 | if (d->d_type == DT_DIR) { |
85 | struct stat sb; | |
3ddbe4d2 | 86 | |
a6fc8b07 | 87 | if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { |
0fbd4c85 | 88 | warn(_("failed to stat %s"), d->d_name); |
a6fc8b07 KZ |
89 | continue; |
90 | } | |
3ddbe4d2 | 91 | |
a6fc8b07 | 92 | /* remove subdirectories if device is same as dir */ |
3ddbe4d2 | 93 | if (sb.st_dev == rb.st_dev) { |
82476a90 | 94 | int cfd; |
a6fc8b07 | 95 | |
82476a90 PJ |
96 | cfd = openat(dfd, d->d_name, O_RDONLY); |
97 | if (cfd >= 0) { | |
98 | recursiveRemove(cfd); | |
99 | close(cfd); | |
100 | } | |
a6fc8b07 KZ |
101 | } else |
102 | continue; | |
3ddbe4d2 | 103 | } |
3ddbe4d2 | 104 | |
a6fc8b07 KZ |
105 | if (unlinkat(dfd, d->d_name, |
106 | d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) | |
0fbd4c85 | 107 | warn(_("failed to unlink %s"), d->d_name); |
3ddbe4d2 KZ |
108 | } |
109 | ||
a6fc8b07 KZ |
110 | rc = 0; /* success */ |
111 | ||
112 | done: | |
113 | if (dir) | |
114 | closedir(dir); | |
115 | return rc; | |
3ddbe4d2 | 116 | } |
711ea730 KZ |
117 | |
118 | static int switchroot(const char *newroot) | |
119 | { | |
120 | /* Don't try to unmount the old "/", there's no way to do it. */ | |
acb03ad4 | 121 | const char *umounts[] = { "/dev", "/proc", "/sys", "/run", NULL }; |
711ea730 | 122 | int i; |
558417e8 | 123 | int cfd; |
4c2d96e6 | 124 | pid_t pid; |
acb03ad4 HH |
125 | struct stat newroot_stat, sb; |
126 | ||
127 | if (stat(newroot, &newroot_stat) != 0) { | |
0fbd4c85 | 128 | warn(_("failed to stat directory %s"), newroot); |
acb03ad4 HH |
129 | return -1; |
130 | } | |
711ea730 KZ |
131 | |
132 | for (i = 0; umounts[i] != NULL; i++) { | |
133 | char newmount[PATH_MAX]; | |
8f24e52e KZ |
134 | |
135 | snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]); | |
136 | ||
acb03ad4 HH |
137 | if ((stat(newmount, &sb) != 0) || (sb.st_dev != newroot_stat.st_dev)) { |
138 | /* mount point seems to be mounted already or stat failed */ | |
944de78b | 139 | umount2(umounts[i], MNT_DETACH); |
acb03ad4 HH |
140 | continue; |
141 | } | |
142 | ||
711ea730 | 143 | if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { |
0fbd4c85 | 144 | warn(_("failed to mount moving %s to %s"), |
711ea730 | 145 | umounts[i], newmount); |
0fbd4c85 | 146 | warnx(_("forcing unmount of %s"), umounts[i]); |
711ea730 KZ |
147 | umount2(umounts[i], MNT_FORCE); |
148 | } | |
149 | } | |
150 | ||
fcb495b1 | 151 | if (chdir(newroot)) { |
0fbd4c85 | 152 | warn(_("failed to change directory to %s"), newroot); |
558417e8 | 153 | return -1; |
711ea730 | 154 | } |
fcb495b1 | 155 | |
82476a90 | 156 | cfd = open("/", O_RDONLY); |
fcb495b1 | 157 | |
711ea730 | 158 | if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { |
dd41e067 | 159 | close(cfd); |
0fbd4c85 | 160 | warn(_("failed to mount moving %s to /"), newroot); |
558417e8 | 161 | return -1; |
711ea730 KZ |
162 | } |
163 | ||
164 | if (chroot(".")) { | |
dd41e067 | 165 | close(cfd); |
0fbd4c85 | 166 | warn(_("failed to change root")); |
558417e8 | 167 | return -1; |
711ea730 | 168 | } |
2a7ccc65 PJ |
169 | |
170 | if (cfd >= 0) { | |
171 | pid = fork(); | |
172 | if (pid <= 0) { | |
173 | recursiveRemove(cfd); | |
174 | if (pid == 0) | |
175 | exit(EXIT_SUCCESS); | |
176 | } | |
177 | close(cfd); | |
178 | } | |
558417e8 | 179 | return 0; |
711ea730 KZ |
180 | } |
181 | ||
ed8d2938 | 182 | static void __attribute__((__noreturn__)) usage(FILE *output) |
711ea730 | 183 | { |
ed8d2938 | 184 | fputs(USAGE_HEADER, output); |
0fbd4c85 | 185 | fprintf(output, _(" %s [options] <newrootdir> <init> <args to init>\n"), |
ed8d2938 SK |
186 | program_invocation_short_name); |
187 | fputs(USAGE_OPTIONS, output); | |
188 | fputs(USAGE_HELP, output); | |
189 | fputs(USAGE_VERSION, output); | |
190 | fprintf(output, USAGE_MAN_TAIL("switch_root(8)")); | |
8b6457d0 | 191 | |
ed8d2938 | 192 | exit(output == stderr ? EXIT_FAILURE : EXIT_SUCCESS); |
711ea730 KZ |
193 | } |
194 | ||
195 | int main(int argc, char *argv[]) | |
196 | { | |
8b6457d0 | 197 | char *newroot, *init, **initargs; |
efb8854f | 198 | atexit(close_stdout); |
711ea730 | 199 | |
8b6457d0 KZ |
200 | if (argv[1] && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) |
201 | usage(stdout); | |
ed8d2938 SK |
202 | if (argv[1] && (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-V"))) { |
203 | printf(UTIL_LINUX_VERSION); | |
204 | return EXIT_SUCCESS; | |
205 | } | |
8b6457d0 KZ |
206 | if (argc < 3) |
207 | usage(stderr); | |
208 | ||
209 | newroot = argv[1]; | |
210 | init = argv[2]; | |
211 | initargs = &argv[2]; | |
212 | ||
213 | if (!*newroot || !*init) | |
711ea730 | 214 | usage(stderr); |
711ea730 | 215 | |
fcb495b1 | 216 | if (switchroot(newroot)) |
0fbd4c85 | 217 | errx(EXIT_FAILURE, _("failed. Sorry.")); |
fcb495b1 | 218 | |
8b6457d0 | 219 | if (access(init, X_OK)) |
0fbd4c85 | 220 | warn(_("cannot access %s"), init); |
711ea730 | 221 | |
8b6457d0 | 222 | execv(init, initargs); |
0fbd4c85 | 223 | err(EXIT_FAILURE, _("failed to execute %s"), init); |
711ea730 KZ |
224 | } |
225 |