#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/nsfs.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../wrappers.h"
#include "../statmount/statmount.h"
#include "../utils.h"
#include "../../kselftest_harness.h"
#ifndef OPEN_TREE_NAMESPACE
#define OPEN_TREE_NAMESPACE (1 << 1)
#endif
static int get_mnt_ns_id(int fd, uint64_t *mnt_ns_id)
{
if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0)
return -errno;
return 0;
}
static int get_mnt_ns_id_from_path(const char *path, uint64_t *mnt_ns_id)
{
int fd, ret;
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
ret = get_mnt_ns_id(fd, mnt_ns_id);
close(fd);
return ret;
}
#define STATMOUNT_BUFSIZE (1 << 15)
static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask)
{
struct statmount *buf;
size_t bufsize = STATMOUNT_BUFSIZE;
int ret;
for (;;) {
buf = malloc(bufsize);
if (!buf)
return NULL;
ret = statmount(mnt_id, mnt_ns_id, mask, buf, bufsize, 0);
if (ret == 0)
return buf;
free(buf);
if (errno != EOVERFLOW)
return NULL;
bufsize <<= 1;
}
}
static void log_mount(struct __test_metadata *_metadata, struct statmount *sm)
{
const char *fs_type = "";
const char *mnt_root = "";
const char *mnt_point = "";
if (sm->mask & STATMOUNT_FS_TYPE)
fs_type = sm->str + sm->fs_type;
if (sm->mask & STATMOUNT_MNT_ROOT)
mnt_root = sm->str + sm->mnt_root;
if (sm->mask & STATMOUNT_MNT_POINT)
mnt_point = sm->str + sm->mnt_point;
TH_LOG(" mnt_id: %llu, parent_id: %llu, fs_type: %s, root: %s, point: %s",
(unsigned long long)sm->mnt_id,
(unsigned long long)sm->mnt_parent_id,
fs_type, mnt_root, mnt_point);
}
static void dump_mounts(struct __test_metadata *_metadata, uint64_t mnt_ns_id)
{
uint64_t list[256];
ssize_t nr_mounts;
nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);
if (nr_mounts < 0) {
TH_LOG("listmount failed: %s", strerror(errno));
return;
}
TH_LOG("Mount namespace %llu contains %zd mount(s):",
(unsigned long long)mnt_ns_id, nr_mounts);
for (ssize_t i = 0; i < nr_mounts; i++) {
struct statmount *sm;
sm = statmount_alloc(list[i], mnt_ns_id,
STATMOUNT_MNT_BASIC |
STATMOUNT_FS_TYPE |
STATMOUNT_MNT_ROOT |
STATMOUNT_MNT_POINT);
if (!sm) {
TH_LOG(" [%zd] mnt_id %llu: statmount failed: %s",
i, (unsigned long long)list[i], strerror(errno));
continue;
}
log_mount(_metadata, sm);
free(sm);
}
}
FIXTURE(open_tree_ns)
{
int fd;
uint64_t current_ns_id;
};
FIXTURE_VARIANT(open_tree_ns)
{
const char *path;
unsigned int flags;
bool expect_success;
bool expect_different_ns;
int min_mounts;
};
FIXTURE_VARIANT_ADD(open_tree_ns, basic_root)
{
.path = "/",
.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, recursive_root)
{
.path = "/",
.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, subdir_tmp)
{
.path = "/tmp",
.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, subdir_proc)
{
.path = "/proc",
.flags = OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, recursive_tmp)
{
.path = "/tmp",
.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, recursive_run)
{
.path = "/run",
.flags = OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC,
.expect_success = true,
.expect_different_ns = true,
.min_mounts = 1,
};
FIXTURE_VARIANT_ADD(open_tree_ns, invalid_recursive_alone)
{
.path = "/",
.flags = AT_RECURSIVE | OPEN_TREE_CLOEXEC,
.expect_success = false,
.expect_different_ns = false,
.min_mounts = 0,
};
FIXTURE_SETUP(open_tree_ns)
{
int ret;
self->fd = -1;
ret = sys_open_tree(-1, NULL, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "open_tree() syscall not supported");
ret = statmount(0, 0, 0, NULL, 0, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "statmount() syscall not supported");
ret = get_mnt_ns_id_from_path("/proc/self/ns/mnt", &self->current_ns_id);
if (ret < 0)
SKIP(return, "Failed to get current mount namespace ID");
}
FIXTURE_TEARDOWN(open_tree_ns)
{
if (self->fd >= 0)
close(self->fd);
}
TEST_F(open_tree_ns, create_namespace)
{
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int ret;
self->fd = sys_open_tree(AT_FDCWD, variant->path, variant->flags);
if (!variant->expect_success) {
ASSERT_LT(self->fd, 0);
ASSERT_EQ(errno, EINVAL);
return;
}
if (self->fd < 0 && errno == EINVAL)
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
ASSERT_GE(self->fd, 0);
ret = get_mnt_ns_id(self->fd, &new_ns_id);
ASSERT_EQ(ret, 0);
if (variant->expect_different_ns)
ASSERT_NE(new_ns_id, self->current_ns_id);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
ASSERT_GE(nr_mounts, 0) {
TH_LOG("%m - listmount failed");
}
ASSERT_GE(nr_mounts, variant->min_mounts);
TH_LOG("Namespace contains %zd mounts", nr_mounts);
}
TEST_F(open_tree_ns, setns_into_namespace)
{
uint64_t new_ns_id;
pid_t pid;
int status;
int ret;
if (!(variant->flags & OPEN_TREE_NAMESPACE))
SKIP(return, "setns test only for basic / case");
self->fd = sys_open_tree(AT_FDCWD, variant->path, variant->flags);
if (self->fd < 0 && errno == EINVAL)
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
ASSERT_GE(self->fd, 0);
ret = get_mnt_ns_id(self->fd, &new_ns_id);
ASSERT_EQ(ret, 0);
dump_mounts(_metadata, new_ns_id);
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
if (setns(self->fd, CLONE_NEWNS) < 0)
_exit(1);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
ASSERT_EQ(WEXITSTATUS(status), 0);
}
TEST_F(open_tree_ns, verify_mount_properties)
{
struct statmount sm;
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int ret;
if (variant->flags != (OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC) ||
strcmp(variant->path, "/") != 0)
SKIP(return, "mount properties test only for basic / case");
self->fd = sys_open_tree(AT_FDCWD, "/", OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
if (self->fd < 0 && errno == EINVAL)
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
ASSERT_GE(self->fd, 0);
ret = get_mnt_ns_id(self->fd, &new_ns_id);
ASSERT_EQ(ret, 0);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
ASSERT_GE(nr_mounts, 1);
ret = statmount(list[0], new_ns_id, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
ASSERT_EQ(ret, 0);
ASSERT_NE(sm.mnt_id, sm.mnt_parent_id);
TH_LOG("Root mount id: %llu, parent: %llu",
(unsigned long long)sm.mnt_id,
(unsigned long long)sm.mnt_parent_id);
}
FIXTURE(open_tree_ns_caps)
{
bool has_caps;
};
FIXTURE_SETUP(open_tree_ns_caps)
{
int ret;
ret = sys_open_tree(-1, NULL, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "open_tree() syscall not supported");
self->has_caps = (geteuid() == 0);
}
FIXTURE_TEARDOWN(open_tree_ns_caps)
{
}
TEST_F(open_tree_ns_caps, requires_cap_sys_admin)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
int fd;
if (enter_userns() != 0)
_exit(2);
if (caps_down() == 0)
_exit(3);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
if (fd >= 0) {
close(fd);
_exit(1);
}
if (errno == EPERM)
_exit(0);
if (errno == EINVAL)
_exit(4);
_exit(5);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("OPEN_TREE_NAMESPACE succeeded without caps");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 3:
SKIP(return, "caps_down failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
FIXTURE(open_tree_ns_userns)
{
int fd;
};
FIXTURE_SETUP(open_tree_ns_userns)
{
int ret;
self->fd = -1;
ret = sys_open_tree(-1, NULL, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "open_tree() syscall not supported");
ret = statmount(0, 0, 0, NULL, 0, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "statmount() syscall not supported");
}
FIXTURE_TEARDOWN(open_tree_ns_userns)
{
if (self->fd >= 0)
close(self->fd);
}
TEST_F(open_tree_ns_userns, create_in_userns)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int fd;
if (enter_userns() != 0)
_exit(2);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
if (fd < 0) {
if (errno == EINVAL)
_exit(4);
_exit(1);
}
if (get_mnt_ns_id(fd, &new_ns_id) != 0)
_exit(5);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
if (nr_mounts < 0)
_exit(6);
if (nr_mounts < 1)
_exit(7);
close(fd);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed in userns");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
case 5:
ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
break;
case 6:
ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace");
break;
case 7:
ASSERT_FALSE(true) TH_LOG("New namespace has no mounts");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
TEST_F(open_tree_ns_userns, setns_in_userns)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
uint64_t new_ns_id;
int fd;
pid_t inner_pid;
int inner_status;
if (enter_userns() != 0)
_exit(2);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
if (fd < 0) {
if (errno == EINVAL)
_exit(4);
_exit(1);
}
if (get_mnt_ns_id(fd, &new_ns_id) != 0)
_exit(5);
inner_pid = fork();
if (inner_pid < 0)
_exit(8);
if (inner_pid == 0) {
if (setns(fd, CLONE_NEWNS) < 0)
_exit(1);
_exit(0);
}
if (waitpid(inner_pid, &inner_status, 0) != inner_pid)
_exit(9);
if (!WIFEXITED(inner_status) || WEXITSTATUS(inner_status) != 0)
_exit(10);
close(fd);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("open_tree or setns failed in userns");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
case 5:
ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
break;
case 8:
ASSERT_FALSE(true) TH_LOG("Inner fork failed");
break;
case 9:
ASSERT_FALSE(true) TH_LOG("Inner waitpid failed");
break;
case 10:
ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
TEST_F(open_tree_ns_userns, recursive_in_userns)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int fd;
if (enter_userns() != 0)
_exit(2);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
if (fd < 0) {
if (errno == EINVAL)
_exit(4);
_exit(1);
}
if (get_mnt_ns_id(fd, &new_ns_id) != 0)
_exit(5);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
if (nr_mounts < 0)
_exit(6);
if (nr_mounts < 1)
_exit(7);
close(fd);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE|AT_RECURSIVE) failed in userns");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
case 5:
ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
break;
case 6:
ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace");
break;
case 7:
ASSERT_FALSE(true) TH_LOG("New namespace has no mounts");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
TEST_F(open_tree_ns_userns, umount_fails_einval)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int fd;
ssize_t i;
if (enter_userns() != 0)
_exit(2);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
if (fd < 0) {
if (errno == EINVAL)
_exit(4);
_exit(1);
}
if (get_mnt_ns_id(fd, &new_ns_id) != 0)
_exit(5);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
if (nr_mounts < 0)
_exit(9);
if (nr_mounts < 1)
_exit(10);
if (setns(fd, CLONE_NEWNS) < 0)
_exit(6);
for (i = 0; i < nr_mounts; i++) {
struct statmount *sm;
const char *mnt_point;
sm = statmount_alloc(list[i], new_ns_id,
STATMOUNT_MNT_POINT);
if (!sm)
_exit(11);
mnt_point = sm->str + sm->mnt_point;
TH_LOG("Trying to umount %s", mnt_point);
if (umount2(mnt_point, MNT_DETACH) == 0) {
free(sm);
_exit(7);
}
if (errno != EINVAL) {
free(sm);
_exit(8);
}
free(sm);
}
close(fd);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
case 5:
ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
break;
case 6:
ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
break;
case 7:
ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL");
break;
case 8:
ASSERT_FALSE(true) TH_LOG("umount failed with wrong error (expected EINVAL)");
break;
case 9:
ASSERT_FALSE(true) TH_LOG("listmount failed");
break;
case 10:
ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
break;
case 11:
ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
TEST_F(open_tree_ns_userns, umount_succeeds)
{
pid_t pid;
int status;
pid = fork();
ASSERT_GE(pid, 0);
if (pid == 0) {
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int fd;
ssize_t i;
if (unshare(CLONE_NEWNS))
_exit(1);
if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) != 0)
_exit(1);
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
if (fd < 0) {
if (errno == EINVAL)
_exit(4);
_exit(1);
}
if (get_mnt_ns_id(fd, &new_ns_id) != 0)
_exit(5);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
if (nr_mounts < 0)
_exit(9);
if (nr_mounts < 1)
_exit(10);
if (setns(fd, CLONE_NEWNS) < 0)
_exit(6);
for (i = 0; i < nr_mounts; i++) {
struct statmount *sm;
const char *mnt_point;
sm = statmount_alloc(list[i], new_ns_id,
STATMOUNT_MNT_POINT);
if (!sm)
_exit(11);
mnt_point = sm->str + sm->mnt_point;
TH_LOG("Trying to umount %s", mnt_point);
if (umount2(mnt_point, MNT_DETACH) != 0) {
free(sm);
_exit(7);
}
free(sm);
}
close(fd);
_exit(0);
}
ASSERT_EQ(waitpid(pid, &status, 0), pid);
ASSERT_TRUE(WIFEXITED(status));
switch (WEXITSTATUS(status)) {
case 0:
break;
case 1:
ASSERT_FALSE(true) TH_LOG("open_tree(OPEN_TREE_NAMESPACE) failed");
break;
case 2:
SKIP(return, "setup_userns failed");
break;
case 4:
SKIP(return, "OPEN_TREE_NAMESPACE not supported");
break;
case 5:
ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
break;
case 6:
ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
break;
case 7:
ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL");
break;
case 9:
ASSERT_FALSE(true) TH_LOG("listmount failed");
break;
case 10:
ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
break;
case 11:
ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
break;
default:
ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
WEXITSTATUS(status));
break;
}
}
FIXTURE(open_tree_ns_unbindable)
{
char tmpdir[PATH_MAX];
bool mounted;
};
FIXTURE_SETUP(open_tree_ns_unbindable)
{
int ret;
self->mounted = false;
ret = sys_open_tree(-1, NULL, 0);
if (ret == -1 && errno == ENOSYS)
SKIP(return, "open_tree() syscall not supported");
snprintf(self->tmpdir, sizeof(self->tmpdir),
"/tmp/open_tree_ns_test.XXXXXX");
ASSERT_NE(mkdtemp(self->tmpdir), NULL);
ret = mount("tmpfs", self->tmpdir, "tmpfs", 0, NULL);
if (ret < 0) {
rmdir(self->tmpdir);
SKIP(return, "Failed to mount tmpfs");
}
self->mounted = true;
ret = mount(NULL, self->tmpdir, NULL, MS_UNBINDABLE, NULL);
if (ret < 0) {
rmdir(self->tmpdir);
SKIP(return, "Failed to make tmpfs unbindable");
}
}
FIXTURE_TEARDOWN(open_tree_ns_unbindable)
{
if (self->mounted)
umount2(self->tmpdir, MNT_DETACH);
rmdir(self->tmpdir);
}
TEST_F(open_tree_ns_unbindable, fails_on_unbindable)
{
int fd;
fd = sys_open_tree(AT_FDCWD, self->tmpdir,
OPEN_TREE_NAMESPACE | OPEN_TREE_CLOEXEC);
ASSERT_LT(fd, 0);
}
TEST_F(open_tree_ns_unbindable, recursive_skips_on_unbindable)
{
uint64_t new_ns_id;
uint64_t list[256];
ssize_t nr_mounts;
int fd;
ssize_t i;
bool found_unbindable = false;
fd = sys_open_tree(AT_FDCWD, "/",
OPEN_TREE_NAMESPACE | AT_RECURSIVE | OPEN_TREE_CLOEXEC);
ASSERT_GT(fd, 0);
ASSERT_EQ(get_mnt_ns_id(fd, &new_ns_id), 0);
nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
ASSERT_GE(nr_mounts, 0) {
TH_LOG("listmount failed: %m");
}
for (i = 0; i < nr_mounts; i++) {
struct statmount *sm;
const char *mnt_point;
sm = statmount_alloc(list[i], new_ns_id, STATMOUNT_MNT_POINT);
ASSERT_NE(sm, NULL) {
TH_LOG("statmount_alloc failed for mnt_id %llu",
(unsigned long long)list[i]);
}
mnt_point = sm->str + sm->mnt_point;
if (strcmp(mnt_point, self->tmpdir) == 0) {
TH_LOG("Found unbindable mount at %s (should have been dropped)",
mnt_point);
found_unbindable = true;
}
free(sm);
}
ASSERT_FALSE(found_unbindable) {
TH_LOG("Unbindable mount at %s was not dropped", self->tmpdir);
}
close(fd);
}
TEST_HARNESS_MAIN