ubase

suckless linux base utils
git clone git://git.2f30.org/ubase.git
Log | Files | Refs | README | LICENSE

commit c7d72a8a56312057d41c4033683395121748e132
parent 1943d5d62f495f451638611f08cad6ee94674c8f
Author: FRIGN <dev@frign.de>
Date:   Mon Apr 14 00:07:06 +0200

Implement switch_root

Diffstat:
LICENSE | 1+
Makefile | 1+
switch_root.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 139 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE @@ -6,6 +6,7 @@ MIT/X Consortium License © 2013 Jakob Kramer <jakob.kramer@gmx.de> © 2014 Carlos J. Torres <vlaadbrain@gmail.com> © 2014 Hiltjo Posthuma <hiltjo@codemadness.org> +© 2014 Laslo Hunhold <dev@frign.de> 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 @@ -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 @@ -0,0 +1,137 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdio.h> +#include <stdlib.h> +#include <linux/magic.h> +#include <linux/limits.h> +#include <unistd.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/vfs.h> +#include <fcntl.h> +#include <dirent.h> +#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]); +}