[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