[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