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

Andrew Vagin avagin at odin.com
Mon Apr 13 01:34:41 PDT 2015


On Thu, Apr 09, 2015 at 06:28:20PM +0300, Pavel Emelyanov wrote:
> 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.

Why can't we use zdtm framework for this test?

> 
> 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