zdtm: add mnt_ext_collision test

This test creates two mount namespaces, one "root" with external mount
at /mnt_ext_collision.test/dst and one "nested" with different internal
mount at /mnt_ext_collision.test/dst instead.

This case is important for nested containers, if we dump a container
with some external mount in /mnt we should not also replace mounts in
/mnt for nested containers with the external one. (One example is docker
containers inside Virtuozzo containers.)

Without previous patch which restricts external mounts resolution to
only root mntns of container this test fails as internal mount is
replaced by external one after migration.

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
This commit is contained in:
Pavel Tikhomirov 2022-01-12 16:00:56 +03:00 committed by Andrei Vagin
parent a963ceb770
commit e50abbd3b3
4 changed files with 212 additions and 0 deletions

View file

@ -400,6 +400,7 @@ TST_DIR = \
mnt_ext_master \
mnt_ext_dev \
mnt_ext_root \
mnt_ext_collision \
mnt_tracefs \
mntns_deleted \
unlink_regular00 \

View file

@ -0,0 +1,194 @@
#include <sched.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <linux/limits.h>
#include "zdtmtst.h"
#include "lock.h"
const char *test_doc = "Check external mount mountpoint collide with different mount in nested mntns";
const char *test_author = "Pavel Tikhomirov <ptikhomirov@virtuozzo.com>";
char *dirname = "mnt_ext_collision.test";
TEST_OPTION(dirname, string, "directory name", 1);
char *source = "zdtm_ext_collision";
char *source2 = "zdtm_ext_collision_2";
enum {
TEST_INIT = 0,
TEST_CHILD,
TEST_CHECK,
TEST_EXIT,
EMERGENCY_ABORT,
};
futex_t *futex;
#define BUF_SIZE 4096
static int child(void)
{
char dst[PATH_MAX], dst_file[PATH_MAX];
int fd;
if (unshare(CLONE_NEWNS)) {
pr_perror("unshare");
goto err;
}
/*
* Umount external mount copy
*/
sprintf(dst, "/%s/dst", dirname);
if (umount(dst)) {
pr_perror("umount");
goto err;
}
/*
* Mount tmpfs in its place
*/
if (mount(source2, dst, "tmpfs", 0, NULL)) {
pr_perror("mount tmpfs");
goto err;
}
sprintf(dst_file, "/%s/dst/file", dirname);
fd = open(dst_file, O_RDWR | O_CREAT | O_EXCL, 0666);
if (fd < 0) {
pr_perror("open");
goto err;
}
close(fd);
futex_set_and_wake(futex, TEST_CHILD);
futex_wait_while_lt(futex, TEST_CHECK);
if (access(dst_file, F_OK)) {
pr_perror("access");
goto err;
}
futex_set_and_wake(futex, TEST_EXIT);
return 0;
err:
futex_set_and_wake(futex, EMERGENCY_ABORT);
return 1;
}
int main(int argc, char **argv)
{
char *root, testdir[PATH_MAX];
char lckd[PATH_MAX], dst[PATH_MAX];
char *tmp = "/tmp/zdtm_ext_collision.tmp";
char *zdtm_newns = getenv("ZDTM_NEWNS");
int pid;
root = getenv("ZDTM_ROOT");
if (root == NULL) {
pr_perror("root");
return 1;
}
if (!zdtm_newns) {
pr_perror("ZDTM_NEWNS is not set");
return 1;
} else if (strcmp(zdtm_newns, "1")) {
goto test;
}
/* Prepare directories in test root */
sprintf(testdir, "%s/%s", root, dirname);
mkdir(testdir, 0755);
sprintf(lckd, "%s/%s/lckd", root, dirname);
mkdir(lckd, 0755);
sprintf(dst, "%s/%s/dst", root, dirname);
mkdir(dst, 0755);
/* Prepare mount in criu root */
mkdir(tmp, 0755);
if (mount(source, tmp, "tmpfs", 0, NULL)) {
pr_perror("mount tmpfs");
return 1;
}
if (mount(NULL, tmp, NULL, MS_PRIVATE, NULL)) {
pr_perror("make private");
return 1;
}
/*
* Create temporary mntns, next mounts will not show up in criu mntns
*/
if (unshare(CLONE_NEWNS)) {
pr_perror("unshare");
return 1;
}
/*
* Populate external mount to the tests mntns root
* (in uns flavour this would become locked)
*/
if (mount(tmp, lckd, NULL, MS_BIND, NULL)) {
pr_perror("bind");
return 1;
}
test:
test_init(argc, argv);
/*
* Hack to create unlocked external mount without pivot_root+bind thing
*/
sprintf(lckd, "/%s/lckd", dirname);
sprintf(dst, "/%s/dst", dirname);
if (mount(lckd, dst, NULL, MS_BIND, NULL)) {
pr_perror("bind");
return 1;
}
/*
* Setup futex for processes syncronization
*/
futex = mmap(NULL, sizeof(futex), PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (futex == MAP_FAILED) {
pr_perror("mmap");
return 1;
}
futex_init(futex);
/*
* Fork child which would have nested mntns
*/
pid = fork();
if (pid < 0) {
pr_perror("fork");
return 1;
} else if (pid == 0) {
exit(child());
}
futex_wait_while_lt(futex, TEST_CHILD);
if (futex_get(futex) == EMERGENCY_ABORT) {
pr_err("Fail in child\n");
return 1;
}
test_daemon();
test_waitsig();
futex_set_and_wake(futex, TEST_CHECK);
futex_wait_while_lt(futex, TEST_EXIT);
if (futex_get(futex) == EMERGENCY_ABORT) {
fail("Fail in child on check stage");
return 1;
}
waitpid(pid, NULL, 0);
pass();
return 0;
}

View file

@ -0,0 +1,5 @@
{ 'dopts': '--external mnt[/mnt_ext_collision.test/dst]:ZDTM',
'feature': 'mnt_id',
'flavor': 'ns uns',
'flags': 'suid',
'ropts': '--external mnt[ZDTM]:/tmp/zdtm_ext_collision.tmp'}

View file

@ -0,0 +1,12 @@
#!/bin/bash
[ "$1" == "--clean" ] || exit 0
TMP="/tmp/zdtm_ext_collision.tmp"
echo "Cleanup mnt_ext_collision"
umount "$TMP"
rm -rf $TMP
rm -rf "mnt_ext_collision.test"
exit 0