diff --git a/LICENSE b/LICENSE index a1078df..91107e6 100644 --- a/LICENSE +++ b/LICENSE @@ -6,6 +6,7 @@ MIT/X Consortium License © 2013 Jakob Kramer © 2014 Carlos J. Torres © 2014 Hiltjo Posthuma +© 2014 Laslo Hunhold Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/Makefile b/Makefile index c139777..0e9d26e 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ SRC = \ su.c \ swapoff.c \ swapon.c \ + switch_root.c \ truncate.c \ umount.c \ unshare.c \ diff --git a/switch_root.c b/switch_root.c new file mode 100644 index 0000000..ede292e --- /dev/null +++ b/switch_root.c @@ -0,0 +1,137 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +static void +delete_content(const char *dir, dev_t curdevice) +{ + char path[PATH_MAX]; + DIR *d; + struct stat st; + struct dirent *dent; + + /* don't dive into other filesystems */ + if (lstat(dir, &st) || st.st_dev != curdevice){ + return; + } + /* delete contents recursively */ + if (S_ISDIR(st.st_mode)) { + d = opendir(dir); + if (d) { + for(; (dent = readdir(d)) ;) { + /* skip ".." and "." */ + if (dent->d_name[0] == '.' + && ((dent->d_name[1] == '.' && dent->d_name[2] == 0) + || (dent->d_name[1] == 0))) + { + continue; + } + + /* build path and dive deeper */ + strlcat(path, dir, sizeof(path)); + strlcat(path, dent->d_name, sizeof(path)); + + delete_content(path, curdevice); + path[0] = 0; + } + closedir(d); + + /* remove now empty dir */ + rmdir(dir); + } + } else { + /* unlink non-directory */ + unlink(dir); + } +} + +static void +usage(void) +{ + eprintf("usage: %s [-c console] [newroot] [init] (PID 1)\n", argv0); +} + +int +main(int argc, char **argv) +{ + char *console = NULL; + dev_t curdev; + struct stat st; + struct statfs stfs; + + ARGBEGIN { + case 'c': + console = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + /* check number of args and if we are PID 1 */ + if (argc != 2 || getpid() != 1){ + usage(); + } + + /* chdir to newroot and make sure it's a different fs */ + if (chdir(argv[0])) { + eprintf("chdir %s:", argv[0]); + } + if (stat("/", &st)) { + eprintf("stat %s:", "/"); + } + curdev = st.st_dev; + if (stat(".", &st)) { + eprintf("stat %s:", "."); + } + if (st.st_dev == curdev) { + usage(); + } + + /* further checks */ + if (stat("/init", &st) || !S_ISREG(st.st_mode)) { + /* avoids trouble with real filesystems */ + eprintf("/init is not a regular file\n"); + } + statfs("/", &stfs); + if ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC){ + eprintf("current filesystem is not a RAMFS or TMPFS\n"); + } + + /* wipe / */ + delete_content("/", curdev); + + /* overmount / with newroot and chroot into it */ + if (mount(".", "/", NULL, MS_MOVE, NULL)) { + eprintf("mount %s:", "."); + } + if (chroot(".")) { + eprintf("chroot failed\n"); + } + + /* if -c is set, redirect stdin/stdout/stderr to console */ + if (console) { + close(0); + if(open(console, O_RDWR) == -1){ + eprintf("open %s:", console); + } + if (dup2(0,1) == -1){ + eprintf("dup2 %s:", "0,1"); + } + if (dup2(0,2) == -1){ + eprintf("dup2 %s:", "0,2"); + } + } + + /* execute init */ + execv(argv[1], argv); + eprintf("can't execute '%s'\n", argv[1]); +}