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 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 }