switch_root.c - ubase - suckless linux base utils | |
git clone git://git.suckless.org/ubase | |
Log | |
Files | |
Refs | |
README | |
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 numb… | |
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(p… | |
42 eprintf("path too long\n"); | |
43 if (strlcat(path, dent->d_name, sizeof(path)) >= sizeof(… | |
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", arg… | |
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_typ… | |
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 } |