switch_root.c - ubase - suckless linux base utils
(HTM) git clone git://git.suckless.org/ubase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
switch_root.c (2867B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/mount.h>
3 #include <sys/stat.h>
4 #include <sys/vfs.h>
5
6 #include <dirent.h>
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "util.h"
15
16 #define RAMFS_MAGIC 0x858458f6 /* some random number */
17 #define TMPFS_MAGIC 0x01021994
18
19 static void
20 delete_content(const char *dir, dev_t curdevice)
21 {
22 char path[PATH_MAX];
23 DIR *d;
24 struct stat st;
25 struct dirent *dent;
26
27 /* don't dive into other filesystems */
28 if (lstat(dir, &st) < 0 || st.st_dev != curdevice)
29 return;
30 if (!(d = opendir(dir)))
31 return;
32 while ((dent = readdir(d))) {
33 if (strcmp(dent->d_name, ".") == 0 ||
34 strcmp(dent->d_name, "..") == 0)
35 continue;
36
37 /* build path and dive deeper */
38 if (strlcpy(path, dir, sizeof(path)) >= sizeof(path))
39 eprintf("path too long\n");
40 if (path[strlen(path) - 1] != '/')
41 if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
42 eprintf("path too long\n");
43 if (strlcat(path, dent->d_name, sizeof(path)) >= sizeof(path))
44 eprintf("path too long\n");
45
46 if (lstat(path, &st) < 0)
47 weprintf("lstat %s:", path);
48
49 if (S_ISDIR(st.st_mode)) {
50 delete_content(path, curdevice);
51 if (rmdir(path) < 0)
52 weprintf("rmdir %s:", path);
53 } else {
54 if (unlink(path) < 0)
55 weprintf("unlink %s:", path);
56 }
57 }
58 closedir(d);
59 }
60
61 static void
62 usage(void)
63 {
64 eprintf("usage: %s [-c console] [newroot] [init] (PID 1)\n", argv0);
65 }
66
67 int
68 main(int argc, char *argv[])
69 {
70 char *console = NULL;
71 dev_t curdev;
72 struct stat st;
73 struct statfs stfs;
74
75 ARGBEGIN {
76 case 'c':
77 console = EARGF(usage());
78 break;
79 default:
80 usage();
81 } ARGEND;
82
83 /* check number of args and if we are PID 1 */
84 if (argc != 2 || getpid() != 1)
85 usage();
86
87 /* chdir to newroot and make sure it's a different fs */
88 if (chdir(argv[0]))
89 eprintf("chdir %s:", argv[0]);
90
91 if (stat("/", &st))
92 eprintf("stat %s:", "/");
93
94 curdev = st.st_dev;
95 if (stat(".", &st))
96 eprintf("stat %s:", ".");
97 if (st.st_dev == curdev)
98 usage();
99
100 /* avoids trouble with real filesystems */
101 if (stat("/init", &st) || !S_ISREG(st.st_mode))
102 eprintf("/init is not a regular file\n");
103
104 statfs("/", &stfs);
105 if ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC)
106 eprintf("current filesystem is not a RAMFS or TMPFS\n");
107
108 /* wipe / */
109 delete_content("/", curdev);
110
111 /* overmount / with newroot and chroot into it */
112 if (mount(".", "/", NULL, MS_MOVE, NULL))
113 eprintf("mount %s:", ".");
114
115 if (chroot("."))
116 eprintf("chroot failed\n");
117
118 /* if -c is set, redirect stdin/stdout/stderr to console */
119 if (console) {
120 close(0);
121 if (open(console, O_RDWR) == -1)
122 eprintf("open %s:", console);
123 dup2(0, 1);
124 dup2(0, 2);
125 }
126
127 /* execute init */
128 execv(argv[1], argv);
129 eprintf("can't execute '%s':", argv[1]);
130 return 1;
131 }