]>
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> | |
c7832fb8 | 26 | #include <sys/statfs.h> |
711ea730 KZ |
27 | #include <sys/param.h> |
28 | #include <fcntl.h> | |
29 | #include <stdio.h> | |
30 | #include <stdlib.h> | |
31 | #include <unistd.h> | |
32 | #include <string.h> | |
33 | #include <errno.h> | |
34 | #include <ctype.h> | |
35 | #include <dirent.h> | |
105bb857 | 36 | #include <getopt.h> |
ed8d2938 | 37 | |
eb76ca98 | 38 | #include "c.h" |
ed8d2938 | 39 | #include "nls.h" |
efb8854f | 40 | #include "closestream.h" |
c7832fb8 | 41 | #include "statfs_magic.h" |
711ea730 KZ |
42 | |
43 | #ifndef MS_MOVE | |
44 | #define MS_MOVE 8192 | |
45 | #endif | |
46 | ||
944de78b HH |
47 | #ifndef MNT_DETACH |
48 | #define MNT_DETACH 0x00000002 /* Just detach from the tree */ | |
49 | #endif | |
50 | ||
711ea730 | 51 | /* remove all files/directories below dirName -- don't cross mountpoints */ |
82476a90 | 52 | static int recursiveRemove(int fd) |
3ddbe4d2 | 53 | { |
a6fc8b07 KZ |
54 | struct stat rb; |
55 | DIR *dir; | |
56 | int rc = -1; | |
57 | int dfd; | |
3ddbe4d2 | 58 | |
82476a90 | 59 | if (!(dir = fdopendir(fd))) { |
0fbd4c85 | 60 | warn(_("failed to open directory")); |
a6fc8b07 | 61 | goto done; |
3ddbe4d2 KZ |
62 | } |
63 | ||
82476a90 | 64 | /* fdopendir() precludes us from continuing to use the input fd */ |
a6fc8b07 KZ |
65 | dfd = dirfd(dir); |
66 | ||
67 | if (fstat(dfd, &rb)) { | |
add1b8af | 68 | warn(_("stat failed")); |
a6fc8b07 | 69 | goto done; |
3ddbe4d2 KZ |
70 | } |
71 | ||
a6fc8b07 KZ |
72 | while(1) { |
73 | struct dirent *d; | |
7ad19a3f | 74 | int isdir = 0; |
3ddbe4d2 | 75 | |
3ddbe4d2 | 76 | errno = 0; |
a6fc8b07 KZ |
77 | if (!(d = readdir(dir))) { |
78 | if (errno) { | |
0fbd4c85 | 79 | warn(_("failed to read directory")); |
a6fc8b07 KZ |
80 | goto done; |
81 | } | |
82 | break; /* end of directory */ | |
83 | } | |
3ddbe4d2 | 84 | |
a6fc8b07 | 85 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) |
3ddbe4d2 | 86 | continue; |
7ad19a3f KZ |
87 | #ifdef _DIRENT_HAVE_D_TYPE |
88 | if (d->d_type == DT_DIR || d->d_type == DT_UNKNOWN) | |
89 | #endif | |
90 | { | |
a6fc8b07 | 91 | struct stat sb; |
3ddbe4d2 | 92 | |
a6fc8b07 | 93 | if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) { |
fc14ceba | 94 | warn(_("stat of %s failed"), d->d_name); |
a6fc8b07 KZ |
95 | continue; |
96 | } | |
3ddbe4d2 | 97 | |
85bfb519 PS |
98 | /* skip if device is not the same */ |
99 | if (sb.st_dev != rb.st_dev) | |
100 | continue; | |
101 | ||
102 | /* remove subdirectories */ | |
103 | if (S_ISDIR(sb.st_mode)) { | |
82476a90 | 104 | int cfd; |
a6fc8b07 | 105 | |
82476a90 PJ |
106 | cfd = openat(dfd, d->d_name, O_RDONLY); |
107 | if (cfd >= 0) { | |
108 | recursiveRemove(cfd); | |
109 | close(cfd); | |
110 | } | |
7ad19a3f | 111 | isdir = 1; |
85bfb519 | 112 | } |
3ddbe4d2 | 113 | } |
3ddbe4d2 | 114 | |
7ad19a3f | 115 | if (unlinkat(dfd, d->d_name, isdir ? AT_REMOVEDIR : 0)) |
0fbd4c85 | 116 | warn(_("failed to unlink %s"), d->d_name); |
3ddbe4d2 KZ |
117 | } |
118 | ||
a6fc8b07 KZ |
119 | rc = 0; /* success */ |
120 | ||
121 | done: | |
122 | if (dir) | |
123 | closedir(dir); | |
124 | return rc; | |
3ddbe4d2 | 125 | } |
711ea730 KZ |
126 | |
127 | static int switchroot(const char *newroot) | |
128 | { | |
129 | /* Don't try to unmount the old "/", there's no way to do it. */ | |
acb03ad4 | 130 | const char *umounts[] = { "/dev", "/proc", "/sys", "/run", NULL }; |
711ea730 | 131 | int i; |
558417e8 | 132 | int cfd; |
4c2d96e6 | 133 | pid_t pid; |
acb03ad4 HH |
134 | struct stat newroot_stat, sb; |
135 | ||
136 | if (stat(newroot, &newroot_stat) != 0) { | |
fc14ceba | 137 | warn(_("stat of %s failed"), newroot); |
acb03ad4 HH |
138 | return -1; |
139 | } | |
711ea730 KZ |
140 | |
141 | for (i = 0; umounts[i] != NULL; i++) { | |
142 | char newmount[PATH_MAX]; | |
8f24e52e KZ |
143 | |
144 | snprintf(newmount, sizeof(newmount), "%s%s", newroot, umounts[i]); | |
145 | ||
acb03ad4 HH |
146 | if ((stat(newmount, &sb) != 0) || (sb.st_dev != newroot_stat.st_dev)) { |
147 | /* mount point seems to be mounted already or stat failed */ | |
944de78b | 148 | umount2(umounts[i], MNT_DETACH); |
acb03ad4 HH |
149 | continue; |
150 | } | |
151 | ||
711ea730 | 152 | if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { |
0fbd4c85 | 153 | warn(_("failed to mount moving %s to %s"), |
711ea730 | 154 | umounts[i], newmount); |
0fbd4c85 | 155 | warnx(_("forcing unmount of %s"), umounts[i]); |
711ea730 KZ |
156 | umount2(umounts[i], MNT_FORCE); |
157 | } | |
158 | } | |
159 | ||
fcb495b1 | 160 | if (chdir(newroot)) { |
0fbd4c85 | 161 | warn(_("failed to change directory to %s"), newroot); |
558417e8 | 162 | return -1; |
711ea730 | 163 | } |
fcb495b1 | 164 | |
82476a90 | 165 | cfd = open("/", O_RDONLY); |
17df84df KZ |
166 | if (cfd < 0) { |
167 | warn(_("cannot open %s"), "/"); | |
168 | return -1; | |
169 | } | |
fcb495b1 | 170 | |
711ea730 | 171 | if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { |
dd41e067 | 172 | close(cfd); |
0fbd4c85 | 173 | warn(_("failed to mount moving %s to /"), newroot); |
558417e8 | 174 | return -1; |
711ea730 KZ |
175 | } |
176 | ||
177 | if (chroot(".")) { | |
dd41e067 | 178 | close(cfd); |
0fbd4c85 | 179 | warn(_("failed to change root")); |
558417e8 | 180 | return -1; |
711ea730 | 181 | } |
2a7ccc65 | 182 | |
341154da SK |
183 | pid = fork(); |
184 | if (pid <= 0) { | |
185 | struct statfs stfs; | |
186 | ||
187 | if (fstatfs(cfd, &stfs) == 0 && | |
188 | (F_TYPE_EQUAL(stfs.f_type, STATFS_RAMFS_MAGIC) || | |
189 | F_TYPE_EQUAL(stfs.f_type, STATFS_TMPFS_MAGIC))) | |
190 | recursiveRemove(cfd); | |
191 | else | |
192 | warn(_("old root filesystem is not an initramfs")); | |
193 | if (pid == 0) | |
194 | exit(EXIT_SUCCESS); | |
2a7ccc65 | 195 | } |
341154da SK |
196 | |
197 | close(cfd); | |
558417e8 | 198 | return 0; |
711ea730 KZ |
199 | } |
200 | ||
6e1eda6f | 201 | static void __attribute__((__noreturn__)) usage(void) |
711ea730 | 202 | { |
6e1eda6f | 203 | FILE *output = stdout; |
ed8d2938 | 204 | fputs(USAGE_HEADER, output); |
0fbd4c85 | 205 | fprintf(output, _(" %s [options] <newrootdir> <init> <args to init>\n"), |
ed8d2938 | 206 | program_invocation_short_name); |
451dbcfa BS |
207 | |
208 | fputs(USAGE_SEPARATOR, output); | |
209 | fputs(_("Switch to another filesystem as the root of the mount tree.\n"), output); | |
210 | ||
ed8d2938 | 211 | fputs(USAGE_OPTIONS, output); |
f45f3ec3 RM |
212 | printf(USAGE_HELP_OPTIONS(16)); |
213 | printf(USAGE_MAN_TAIL("switch_root(8)")); | |
8b6457d0 | 214 | |
6e1eda6f | 215 | exit(EXIT_SUCCESS); |
711ea730 KZ |
216 | } |
217 | ||
218 | int main(int argc, char *argv[]) | |
219 | { | |
8b6457d0 | 220 | char *newroot, *init, **initargs; |
105bb857 SK |
221 | int c; |
222 | static const struct option longopts[] = { | |
223 | {"version", no_argument, NULL, 'V'}, | |
224 | {"help", no_argument, NULL, 'h'}, | |
225 | {NULL, 0, NULL, 0} | |
226 | }; | |
227 | ||
2c308875 | 228 | close_stdout_atexit(); |
711ea730 | 229 | |
9737a167 | 230 | while ((c = getopt_long(argc, argv, "+Vh", longopts, NULL)) != -1) |
105bb857 SK |
231 | switch (c) { |
232 | case 'V': | |
2c308875 | 233 | print_version(EXIT_SUCCESS); |
105bb857 | 234 | case 'h': |
6e1eda6f | 235 | usage(); |
105bb857 SK |
236 | default: |
237 | errtryhelp(EXIT_FAILURE); | |
238 | } | |
6e1eda6f RM |
239 | if (argc < 3) { |
240 | warnx(_("not enough arguments")); | |
241 | errtryhelp(EXIT_FAILURE); | |
242 | } | |
8b6457d0 KZ |
243 | |
244 | newroot = argv[1]; | |
245 | init = argv[2]; | |
246 | initargs = &argv[2]; | |
247 | ||
6e1eda6f RM |
248 | if (!*newroot || !*init) { |
249 | warnx(_("bad usage")); | |
250 | errtryhelp(EXIT_FAILURE); | |
251 | } | |
711ea730 | 252 | |
fcb495b1 | 253 | if (switchroot(newroot)) |
0fbd4c85 | 254 | errx(EXIT_FAILURE, _("failed. Sorry.")); |
fcb495b1 | 255 | |
8b6457d0 | 256 | if (access(init, X_OK)) |
0fbd4c85 | 257 | warn(_("cannot access %s"), init); |
711ea730 | 258 | |
8b6457d0 | 259 | execv(init, initargs); |
fd777151 | 260 | errexec(init); |
711ea730 KZ |
261 | } |
262 |