Good evening,

as discussed earlier, I worked on the switch_root-tool and successfully
tested it with my initramfs-setup without problems.
Let me know what you think.

Cheers

FRIGN

-- 
FRIGN <d...@frign.de>
>From e0608f4fe61aa70a270b0168790fdd6994c3e91e Mon Sep 17 00:00:00 2001
From: FRIGN <d...@frign.de>
Date: Mon, 14 Apr 2014 00:07:06 +0200
Subject: [PATCH] Implement switch_root

---
 LICENSE       |   1 +
 Makefile      |   1 +
 switch_root.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+)
 create mode 100644 switch_root.c

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 <jakob.kra...@gmx.de>
 © 2014 Carlos J. Torres <vlaadbr...@gmail.com>
 © 2014 Hiltjo Posthuma <hil...@codemadness.org>
+© 2014 Laslo Hunhold <d...@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
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..cfa37e9
--- /dev/null
+++ b/switch_root.c
@@ -0,0 +1,136 @@
+/* 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]);
+}
-- 
1.8.3.2

Reply via email to