[CRIU] [PATCH 1/2] test: Rework the ext mount plugin test

Pavel Emelyanov xemul at parallels.com
Thu Apr 9 08:28:20 PDT 2015


The existing set of shell scripts do hard-to-debug things and mess
with the root filesystem. We can make it better.

First, not to play with the system / the process that will be run in
a new mount namespace is statically compiled .c file. And this "init"
does a very simple thing -- waits for SIGTERM and check that the
given filepath contains the given string.

Second, the namespace's root will be some subdir, instead of system
/ bind-mount-ed into a subdir. This makes it easier to keep things
together and makes 100% sure the external bind mount cannot be
accessed by custom path.

Signed-off-by: Pavel Emelyanov <xemul at parallels.com>
---
 test/mounts/ext/Makefile    |   8 ++-
 test/mounts/ext/ext-mount.c |   8 +--
 test/mounts/ext/ns_init.c   | 143 ++++++++++++++++++++++++++++++++++++++++++++
 test/mounts/ext/run.sh      | 139 ++++++++++++++++++++++++++++--------------
 test/mounts/ext/run_ns.sh   |  53 ----------------
 test/mounts/ext/run_wait.sh |  20 -------
 6 files changed, 248 insertions(+), 123 deletions(-)
 create mode 100644 test/mounts/ext/ns_init.c
 delete mode 100755 test/mounts/ext/run_ns.sh
 delete mode 100755 test/mounts/ext/run_wait.sh

diff --git a/test/mounts/ext/Makefile b/test/mounts/ext/Makefile
index 6a9df47..282fba0 100644
--- a/test/mounts/ext/Makefile
+++ b/test/mounts/ext/Makefile
@@ -1,7 +1,13 @@
-all: ext-mount.so
+all: ext-mount.so ns_init
 
 ext-mount.so: ext-mount.c
 	gcc -g -Werror -Wall -shared -nostartfiles ext-mount.c -o ext-mount.so -iquote ../../../include -fPIC
 
+ns_init: ns_init.o
+	gcc -static $< -o $@
+
+ns_init.o: ns_init.c
+	gcc -c $< -o $@
+
 run: all
 	./run.sh
diff --git a/test/mounts/ext/ext-mount.c b/test/mounts/ext/ext-mount.c
index 3ad3b25..e5e974b 100644
--- a/test/mounts/ext/ext-mount.c
+++ b/test/mounts/ext/ext-mount.c
@@ -34,9 +34,9 @@ int cr_plugin_dump_ext_mount(char *mountpoint, int id)
 		return -ENOTSUP;
 	}
 
-	dst = getenv("dfile");
+	dst = getenv("EMP_MOUNTPOINT");
 	if (!dst) {
-		pr_err("No dfile env\n");
+		pr_err("No EMP_MOUNTPOINT env\n");
 		return -1;
 	}
 
@@ -74,9 +74,9 @@ int cr_plugin_restore_ext_mount(int id, char *mountpoint, char *old_root, int *i
 	}
 	close(fd);
 
-	src_file = getenv("sfpath");
+	src_file = getenv("EMP_ROOT_P");
 	if (!src_file) {
-		pr_err("Can't get sfpath env\n");
+		pr_err("Can't get EMP_ROOT_P env\n");
 		return -1;
 	}
 
diff --git a/test/mounts/ext/ns_init.c b/test/mounts/ext/ns_init.c
new file mode 100644
index 0000000..e85bf9d
--- /dev/null
+++ b/test/mounts/ext/ns_init.c
@@ -0,0 +1,143 @@
+#define _GNU_SOURCE
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+
+static void sigh(int sig)
+{
+}
+
+int main(int argc, char **argv)
+{
+	int start[2];
+	char res;
+	pid_t pid;
+
+	/*
+	 * Usage:
+	 * run <pidfile> <root> <log-file-name> <file-to-check> <contents-to-check>
+	 */
+
+	if (getpid() == 1) {
+		int fd;
+		struct sigaction sa = {};
+		sigset_t mask;
+
+		if (setsid() == -1) {
+			fprintf(stderr, "setsid: %m\n");
+			return 1;
+		}
+
+		sa.sa_handler = sigh;
+		sigaction(SIGTERM, &sa, NULL);
+
+		if (chdir(argv[2]))
+			return 1;
+
+		fd = open(argv[3], O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
+		if (fd < 0)
+			return 1;
+
+		dup2(fd, 1);
+		dup2(fd, 2);
+		close(fd);
+		close(0);
+
+		if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
+			fprintf(stderr, "mount(/, S_REC | MS_PRIVATE)): %m");
+			return 1;
+		}
+
+		mkdir("oldm");
+		if (pivot_root(".", "./oldm") < 0)
+			return 1;
+
+		umount2("/oldm", MNT_DETACH);
+
+		mkdir("/proc");
+		if (mount("zdtm_proc", "/proc", "proc", 0, NULL)) {
+			fprintf(stderr, "mount(/proc): %m");
+			return 1;
+		}
+
+		sigemptyset(&mask);
+		sigaddset(&mask, SIGTERM);
+		sigprocmask(SIG_BLOCK, &mask, NULL);
+
+		fd = atoi(argv[1]);
+		write(fd, "!", 1);
+		close(fd);
+
+		sigemptyset(&mask);
+		sigsuspend(&mask);
+
+		printf("Woken UP\n");
+		printf("Reading %s for [%s]\n", argv[4], argv[5]);
+		{
+			FILE *f;
+			char buf[128];
+
+			f = fopen(argv[4], "r");
+			if (!f)
+				perror("No file with message");
+			else {
+				memset(buf, 0, sizeof(buf));
+				fgets(buf, sizeof(buf), f);
+				fclose(f);
+				printf("Got [%s]\n", buf);
+
+				if (!strcmp(buf, argv[5]))
+					printf("PASS\n");
+			}
+		}
+
+		exit(0);
+	}
+
+	if (unshare(CLONE_NEWNS | CLONE_NEWPID))
+		return 1;
+
+	pipe(start);
+	pid = fork();
+	if (pid == 0) {
+		char *nargv[7], aux[10];
+
+		close(start[0]);
+		sprintf(aux, "%d", start[1]);
+		nargv[0] = argv[0];
+		nargv[1] = aux;
+		nargv[2] = argv[2];
+		nargv[3] = argv[3];
+		nargv[4] = argv[4];
+		nargv[5] = argv[5];
+		nargv[6] = NULL;
+
+		execv(argv[0], nargv);
+		exit(0);
+	}
+
+	close(start[1]);
+	res = 'F';
+	read(start[0], &res, 1);
+	if (res != '!') {
+		printf("Failed to start\n");
+		return 1;
+	}
+
+	printf("Container w/ tests started\n");
+	{
+		FILE *pidf;
+		pidf = fopen(argv[1], "w");
+		fprintf(pidf, "%d", pid);
+		fclose(pidf);
+	}
+
+	return 0;
+}
diff --git a/test/mounts/ext/run.sh b/test/mounts/ext/run.sh
index 886295a..4fe729b 100755
--- a/test/mounts/ext/run.sh
+++ b/test/mounts/ext/run.sh
@@ -7,51 +7,100 @@ function fail {
 	exit 1
 }
 
-make || fail "Can't compile library"
+make || fail "Can't compile library or ns init"
 
 criu="../../../criu"
 
-finf="finish"
-outf="run_output"
-pidfile="pid_wait"
-tempd="temp_dir"
-sfile="source_file"
-tdir="test_dir"
-dfile="dest_file"
-mesg="msg-$((RANDOM % 128))"
-export finf
-export outf
-export pidfile
-export sfile
-export dfile
-export tempd
-export mesg
-export tdir
-
-mkdir dump/
-mkdir $tdir
-mount --bind "/" ${tdir} || fail "Can't bind root"
-mount --make-rprivate "${tdir}"
-
-unshare --mount ./run_ns.sh || fail "Can't unshare ns"
-cat $pidfile
-
-sleep 2
-$criu dump -t $(cat $pidfile) -D dump/ -o dump.log -v4 --lib $(pwd) && echo OK
-sleep 1
-
-mkdir $tempd
-mount -t tmpfs none "$tempd"
-echo "$mesg" > "$tempd/$sfile"
-sfpath="/$(pwd)/$tempd/$sfile"
-export sfpath
-
-$criu restore -D dump/ -o restore.log -v4 --lib $(pwd) --root "$(pwd)/$tdir" -d && echo OK
-
-umount "$tempd"
-
-touch $finf
-sleep 1 # Shitty, but...
-tail $outf
-
-umount ${tdir}
+# New root for namespace
+NSROOT="nsroot"
+# External file with contents (exported for plugin.restore)
+EMP_ROOT="external_file"
+export EMP_ROOT_P="$(pwd)/$EMP_ROOT"
+# Internal file as seen from namespace (exported for plugin.dump)
+export EMP_MOUNTPOINT="file"
+# Message in a file to check visibility
+FMESSAGE="tram-pam-pam"
+# Binary of namespace's init
+NS_INIT="ns_init"
+# File with namespace init pid
+PIDF="pidf"
+
+start_ns()
+{
+	#
+	# Prepare the namespace's FS layout
+	#
+	mkdir $NSROOT
+	echo -n "$FMESSAGE" > "$EMP_ROOT"
+	mount --bind "$NSROOT" "$NSROOT"
+	mount --make-private "$NSROOT"
+	touch "$NSROOT/$EMP_MOUNTPOINT"
+	mount --bind "$EMP_ROOT" "$NSROOT/$EMP_MOUNTPOINT" || fail "Can't prepare fs for ns"
+
+	#
+	# Start the namespace's init
+	#
+	cp $NS_INIT "$NSROOT/"
+	"./$NSROOT/$NS_INIT" "$PIDF" "$NSROOT" "log" "$EMP_MOUNTPOINT" "$FMESSAGE" || fail "Can't start namespace"
+	umount "$NSROOT/$EMP_MOUNTPOINT"
+
+	echo "Namespace started, pid $(cat $PIDF)"
+}
+
+stop_ns()
+{
+	#
+	# Kill the init
+	#
+
+	kill -TERM $(cat $PIDF)
+	sleep 2 # Shitty, but...
+	umount $NSROOT
+
+	if [ -z "$1" ]; then
+		rm -f "$NSROOT/log"
+	else
+		mv "$NSROOT/log" "$1"
+	fi
+
+	rm -f "$PIDF" "$EMP_ROOT" "$NSROOT/$NS_INIT" "$NSROOT/log" "$NSROOT/$EMP_MOUNTPOINT"
+	rmdir "$NSROOT/oldm"
+	rmdir "$NSROOT/proc"
+	rmdir "$NSROOT"
+}
+
+DDIR="dump"
+rm -rf $DDIR
+mkdir $DDIR
+
+chk_pass()
+{
+	tail -n1 $1 | fgrep -q "PASS"
+}
+
+#
+# Test 1: handle external mount with plugin
+#
+
+test_plugin()
+{
+	echo "=== Testing how plugin works"
+	mkdir "$DDIR/plugin/"
+	start_ns
+
+	$criu dump    -D "$DDIR/plugin/" -v4 -o "dump.log" --lib=$(pwd) \
+			-t $(cat pidf) || { stop_ns; return 1; }
+
+	$criu restore -D "$DDIR/plugin/" -v4 -o "rstr.log" --lib=$(pwd) \
+			-d --root="$(pwd)/$NSROOT" --pidfile=$PIDF || { stop_ns; return 1; }
+
+	echo "Restored, checking results"
+	mv "$DDIR/plugin/$PIDF" .
+	stop_ns "$DDIR/plugin/ns.log"
+	chk_pass "$DDIR/plugin/ns.log"
+}
+
+test_plugin || exit 1
+
+echo "All tests passed"
+exit 0
diff --git a/test/mounts/ext/run_ns.sh b/test/mounts/ext/run_ns.sh
deleted file mode 100755
index 6527765..0000000
--- a/test/mounts/ext/run_ns.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-
-set -x
-set -e
-
-odir="mexold"
-# finf & outf came from parent
-cur="$(pwd)"
-
-function fail {
-	echo $@
-	exit 1
-}
-
-# Don't mirror further bind mounts in the original namespace
-mount --make-rprivate "/"
-
-# Clean previous stuff
-rm -rf "$tempd" "$finf" "$outf" "/$odir"
-mkdir "$tempd"
-touch "$tdir/$cur/$tdir/$dfile"
-
-# Create source file. Make it on a new mountpoint to "hide"
-# it in the target mount tree (see below)
-mount -t tmpfs none "$tempd"
-echo "$mesg" > "$tempd/$sfile"
-
-# Create destination file. It's a bind mount to the source one.
-mount --bind "$tempd/$sfile" "$tdir/$cur/$tdir/$dfile"
-
-# Make clean and small mounts set
-cd "$tdir"
-mkdir "$odir"
-pivot_root "." "./$odir"
-mount -t proc none "/proc"
-umount -lif "/$odir"
-
-# This would show root, proc and the bind mount to some "unknown"
-# file. Unknown, since it's on a tempfs mount that is not seen
-cat "/proc/self/mountinfo"
-
-set +e
-
-cd "$cur"
-
-# Will be in "logs" so that caller can do "sanity eye-check"
-ls
-cat "$tempd/$sfile"
-cat "$tdir/$dfile"
-
-# Start waiting for C/R on us
-# Exec also fixes the maps/exe/fd links relative to new mounts
-exec setsid "./run_wait.sh" "$tdir/$dfile" "$mesg" < /dev/null > "$outf" 2>&1 &
diff --git a/test/mounts/ext/run_wait.sh b/test/mounts/ext/run_wait.sh
deleted file mode 100755
index 3374ca7..0000000
--- a/test/mounts/ext/run_wait.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-echo $$ > $pidfile
-
-echo "My mounts (before)"
-cat "/proc/self/mountinfo"
-
-while [ ! -e "$finf" ]; do
-	echo "WAIT"
-	sleep 1;
-done
-
-echo "My mounts (after)"
-cat "/proc/self/mountinfo"
-
-if fgrep "$2" "$1" ; then
-	echo "PASS"
-else
-	echo "FAIL"
-fi
-- 
1.9.3




More information about the CRIU mailing list