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 }