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 }