xref: /linux/tools/testing/selftests/filesystems/fsmount_ns/fsmount_ns_test.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
13ac7ea91SChristian Brauner // SPDX-License-Identifier: GPL-2.0
23ac7ea91SChristian Brauner /*
33ac7ea91SChristian Brauner  * Copyright (C) 2026 Christian Brauner <brauner@kernel.org>
43ac7ea91SChristian Brauner  *
53ac7ea91SChristian Brauner  * Test for FSMOUNT_NAMESPACE flag.
63ac7ea91SChristian Brauner  *
73ac7ea91SChristian Brauner  * Test that fsmount() with FSMOUNT_NAMESPACE creates a new mount
83ac7ea91SChristian Brauner  * namespace containing the specified mount.
93ac7ea91SChristian Brauner  */
103ac7ea91SChristian Brauner #define _GNU_SOURCE
113ac7ea91SChristian Brauner 
123ac7ea91SChristian Brauner #include <errno.h>
133ac7ea91SChristian Brauner #include <fcntl.h>
143ac7ea91SChristian Brauner #include <limits.h>
153ac7ea91SChristian Brauner #include <linux/nsfs.h>
163ac7ea91SChristian Brauner #include <sched.h>
173ac7ea91SChristian Brauner #include <stdio.h>
183ac7ea91SChristian Brauner #include <stdlib.h>
193ac7ea91SChristian Brauner #include <string.h>
203ac7ea91SChristian Brauner #include <sys/ioctl.h>
213ac7ea91SChristian Brauner #include <sys/mount.h>
223ac7ea91SChristian Brauner #include <sys/stat.h>
233ac7ea91SChristian Brauner #include <sys/wait.h>
243ac7ea91SChristian Brauner #include <unistd.h>
253ac7ea91SChristian Brauner 
263ac7ea91SChristian Brauner #include "../wrappers.h"
273ac7ea91SChristian Brauner #include "../statmount/statmount.h"
283ac7ea91SChristian Brauner #include "../utils.h"
293ac7ea91SChristian Brauner #include "../../kselftest_harness.h"
303ac7ea91SChristian Brauner 
313ac7ea91SChristian Brauner #ifndef FSMOUNT_NAMESPACE
323ac7ea91SChristian Brauner #define FSMOUNT_NAMESPACE	0x00000002
333ac7ea91SChristian Brauner #endif
343ac7ea91SChristian Brauner 
353ac7ea91SChristian Brauner #ifndef FSMOUNT_CLOEXEC
363ac7ea91SChristian Brauner #define FSMOUNT_CLOEXEC		0x00000001
373ac7ea91SChristian Brauner #endif
383ac7ea91SChristian Brauner 
393ac7ea91SChristian Brauner #ifndef FSCONFIG_CMD_CREATE
403ac7ea91SChristian Brauner #define FSCONFIG_CMD_CREATE	6
413ac7ea91SChristian Brauner #endif
423ac7ea91SChristian Brauner 
433ac7ea91SChristian Brauner static int get_mnt_ns_id(int fd, uint64_t *mnt_ns_id)
443ac7ea91SChristian Brauner {
453ac7ea91SChristian Brauner 	if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0)
463ac7ea91SChristian Brauner 		return -errno;
473ac7ea91SChristian Brauner 	return 0;
483ac7ea91SChristian Brauner }
493ac7ea91SChristian Brauner 
503ac7ea91SChristian Brauner static int get_mnt_ns_id_from_path(const char *path, uint64_t *mnt_ns_id)
513ac7ea91SChristian Brauner {
523ac7ea91SChristian Brauner 	int fd, ret;
533ac7ea91SChristian Brauner 
543ac7ea91SChristian Brauner 	fd = open(path, O_RDONLY);
553ac7ea91SChristian Brauner 	if (fd < 0)
563ac7ea91SChristian Brauner 		return -errno;
573ac7ea91SChristian Brauner 
583ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(fd, mnt_ns_id);
593ac7ea91SChristian Brauner 	close(fd);
603ac7ea91SChristian Brauner 	return ret;
613ac7ea91SChristian Brauner }
623ac7ea91SChristian Brauner 
633ac7ea91SChristian Brauner static void log_mount(struct __test_metadata *_metadata, struct statmount *sm)
643ac7ea91SChristian Brauner {
653ac7ea91SChristian Brauner 	const char *fs_type = "";
663ac7ea91SChristian Brauner 	const char *mnt_root = "";
673ac7ea91SChristian Brauner 	const char *mnt_point = "";
683ac7ea91SChristian Brauner 
693ac7ea91SChristian Brauner 	if (sm->mask & STATMOUNT_FS_TYPE)
703ac7ea91SChristian Brauner 		fs_type = sm->str + sm->fs_type;
713ac7ea91SChristian Brauner 	if (sm->mask & STATMOUNT_MNT_ROOT)
723ac7ea91SChristian Brauner 		mnt_root = sm->str + sm->mnt_root;
733ac7ea91SChristian Brauner 	if (sm->mask & STATMOUNT_MNT_POINT)
743ac7ea91SChristian Brauner 		mnt_point = sm->str + sm->mnt_point;
753ac7ea91SChristian Brauner 
763ac7ea91SChristian Brauner 	TH_LOG("  mnt_id: %llu, parent_id: %llu, fs_type: %s, root: %s, point: %s",
773ac7ea91SChristian Brauner 	       (unsigned long long)sm->mnt_id,
783ac7ea91SChristian Brauner 	       (unsigned long long)sm->mnt_parent_id,
793ac7ea91SChristian Brauner 	       fs_type, mnt_root, mnt_point);
803ac7ea91SChristian Brauner }
813ac7ea91SChristian Brauner 
823ac7ea91SChristian Brauner static void dump_mounts(struct __test_metadata *_metadata, uint64_t mnt_ns_id)
833ac7ea91SChristian Brauner {
843ac7ea91SChristian Brauner 	uint64_t list[256];
853ac7ea91SChristian Brauner 	ssize_t nr_mounts;
863ac7ea91SChristian Brauner 
873ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0);
883ac7ea91SChristian Brauner 	if (nr_mounts < 0) {
893ac7ea91SChristian Brauner 		TH_LOG("listmount failed: %s", strerror(errno));
903ac7ea91SChristian Brauner 		return;
913ac7ea91SChristian Brauner 	}
923ac7ea91SChristian Brauner 
933ac7ea91SChristian Brauner 	TH_LOG("Mount namespace %llu contains %zd mount(s):",
943ac7ea91SChristian Brauner 	       (unsigned long long)mnt_ns_id, nr_mounts);
953ac7ea91SChristian Brauner 
963ac7ea91SChristian Brauner 	for (ssize_t i = 0; i < nr_mounts; i++) {
973ac7ea91SChristian Brauner 		struct statmount *sm;
983ac7ea91SChristian Brauner 
993ac7ea91SChristian Brauner 		sm = statmount_alloc(list[i], mnt_ns_id,
1003ac7ea91SChristian Brauner 				     STATMOUNT_MNT_BASIC |
1013ac7ea91SChristian Brauner 				     STATMOUNT_FS_TYPE |
1023ac7ea91SChristian Brauner 				     STATMOUNT_MNT_ROOT |
1033ac7ea91SChristian Brauner 				     STATMOUNT_MNT_POINT, 0);
1043ac7ea91SChristian Brauner 		if (!sm) {
1053ac7ea91SChristian Brauner 			TH_LOG("  [%zd] mnt_id %llu: statmount failed: %s",
1063ac7ea91SChristian Brauner 			       i, (unsigned long long)list[i], strerror(errno));
1073ac7ea91SChristian Brauner 			continue;
1083ac7ea91SChristian Brauner 		}
1093ac7ea91SChristian Brauner 
1103ac7ea91SChristian Brauner 		log_mount(_metadata, sm);
1113ac7ea91SChristian Brauner 		free(sm);
1123ac7ea91SChristian Brauner 	}
1133ac7ea91SChristian Brauner }
1143ac7ea91SChristian Brauner 
1153ac7ea91SChristian Brauner static int create_tmpfs_fd(void)
1163ac7ea91SChristian Brauner {
1173ac7ea91SChristian Brauner 	int fs_fd, ret;
1183ac7ea91SChristian Brauner 
1193ac7ea91SChristian Brauner 	fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
1203ac7ea91SChristian Brauner 	if (fs_fd < 0)
1213ac7ea91SChristian Brauner 		return -errno;
1223ac7ea91SChristian Brauner 
1233ac7ea91SChristian Brauner 	ret = sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
1243ac7ea91SChristian Brauner 	if (ret < 0) {
1253ac7ea91SChristian Brauner 		close(fs_fd);
1263ac7ea91SChristian Brauner 		return -errno;
1273ac7ea91SChristian Brauner 	}
1283ac7ea91SChristian Brauner 
1293ac7ea91SChristian Brauner 	return fs_fd;
1303ac7ea91SChristian Brauner }
1313ac7ea91SChristian Brauner 
1323ac7ea91SChristian Brauner FIXTURE(fsmount_ns)
1333ac7ea91SChristian Brauner {
1343ac7ea91SChristian Brauner 	int fd;
1353ac7ea91SChristian Brauner 	int fs_fd;
1363ac7ea91SChristian Brauner 	uint64_t current_ns_id;
1373ac7ea91SChristian Brauner };
1383ac7ea91SChristian Brauner 
1393ac7ea91SChristian Brauner FIXTURE_VARIANT(fsmount_ns)
1403ac7ea91SChristian Brauner {
1413ac7ea91SChristian Brauner 	const char *fstype;
1423ac7ea91SChristian Brauner 	unsigned int flags;
1433ac7ea91SChristian Brauner 	bool expect_success;
1443ac7ea91SChristian Brauner 	bool expect_different_ns;
1453ac7ea91SChristian Brauner 	int min_mounts;
1463ac7ea91SChristian Brauner };
1473ac7ea91SChristian Brauner 
1483ac7ea91SChristian Brauner FIXTURE_VARIANT_ADD(fsmount_ns, basic_tmpfs)
1493ac7ea91SChristian Brauner {
1503ac7ea91SChristian Brauner 	.fstype = "tmpfs",
1513ac7ea91SChristian Brauner 	.flags = FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
1523ac7ea91SChristian Brauner 	.expect_success = true,
1533ac7ea91SChristian Brauner 	.expect_different_ns = true,
1543ac7ea91SChristian Brauner 	.min_mounts = 1,
1553ac7ea91SChristian Brauner };
1563ac7ea91SChristian Brauner 
1573ac7ea91SChristian Brauner FIXTURE_VARIANT_ADD(fsmount_ns, cloexec_only)
1583ac7ea91SChristian Brauner {
1593ac7ea91SChristian Brauner 	.fstype = "tmpfs",
1603ac7ea91SChristian Brauner 	.flags = FSMOUNT_CLOEXEC,
1613ac7ea91SChristian Brauner 	.expect_success = true,
1623ac7ea91SChristian Brauner 	.expect_different_ns = false,
1633ac7ea91SChristian Brauner 	.min_mounts = 1,
1643ac7ea91SChristian Brauner };
1653ac7ea91SChristian Brauner 
1663ac7ea91SChristian Brauner FIXTURE_VARIANT_ADD(fsmount_ns, namespace_only)
1673ac7ea91SChristian Brauner {
1683ac7ea91SChristian Brauner 	.fstype = "tmpfs",
1693ac7ea91SChristian Brauner 	.flags = FSMOUNT_NAMESPACE,
1703ac7ea91SChristian Brauner 	.expect_success = true,
1713ac7ea91SChristian Brauner 	.expect_different_ns = true,
1723ac7ea91SChristian Brauner 	.min_mounts = 1,
1733ac7ea91SChristian Brauner };
1743ac7ea91SChristian Brauner 
1753ac7ea91SChristian Brauner FIXTURE_SETUP(fsmount_ns)
1763ac7ea91SChristian Brauner {
1773ac7ea91SChristian Brauner 	int ret;
1783ac7ea91SChristian Brauner 
1793ac7ea91SChristian Brauner 	self->fd = -1;
1803ac7ea91SChristian Brauner 	self->fs_fd = -1;
1813ac7ea91SChristian Brauner 
1823ac7ea91SChristian Brauner 	/* Check if fsopen syscall is supported */
1833ac7ea91SChristian Brauner 	ret = sys_fsopen("tmpfs", 0);
1843ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
1853ac7ea91SChristian Brauner 		SKIP(return, "fsopen() syscall not supported");
1863ac7ea91SChristian Brauner 	if (ret >= 0)
1873ac7ea91SChristian Brauner 		close(ret);
1883ac7ea91SChristian Brauner 
1893ac7ea91SChristian Brauner 	/* Check if statmount/listmount are supported */
1903ac7ea91SChristian Brauner 	ret = statmount(0, 0, 0, 0, NULL, 0, 0);
1913ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
1923ac7ea91SChristian Brauner 		SKIP(return, "statmount() syscall not supported");
1933ac7ea91SChristian Brauner 
1943ac7ea91SChristian Brauner 	/* Get current mount namespace ID for comparison */
1953ac7ea91SChristian Brauner 	ret = get_mnt_ns_id_from_path("/proc/self/ns/mnt", &self->current_ns_id);
1963ac7ea91SChristian Brauner 	if (ret < 0)
1973ac7ea91SChristian Brauner 		SKIP(return, "Failed to get current mount namespace ID");
1983ac7ea91SChristian Brauner }
1993ac7ea91SChristian Brauner 
2003ac7ea91SChristian Brauner FIXTURE_TEARDOWN(fsmount_ns)
2013ac7ea91SChristian Brauner {
2023ac7ea91SChristian Brauner 	if (self->fd >= 0)
2033ac7ea91SChristian Brauner 		close(self->fd);
2043ac7ea91SChristian Brauner 	if (self->fs_fd >= 0)
2053ac7ea91SChristian Brauner 		close(self->fs_fd);
2063ac7ea91SChristian Brauner }
2073ac7ea91SChristian Brauner 
2083ac7ea91SChristian Brauner TEST_F(fsmount_ns, create_namespace)
2093ac7ea91SChristian Brauner {
2103ac7ea91SChristian Brauner 	uint64_t new_ns_id;
2113ac7ea91SChristian Brauner 	uint64_t list[256];
2123ac7ea91SChristian Brauner 	ssize_t nr_mounts;
2133ac7ea91SChristian Brauner 	int ret;
2143ac7ea91SChristian Brauner 
2153ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
2163ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
2173ac7ea91SChristian Brauner 
2183ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, variant->flags, 0);
2193ac7ea91SChristian Brauner 
2203ac7ea91SChristian Brauner 	if (!variant->expect_success) {
2213ac7ea91SChristian Brauner 		ASSERT_LT(self->fd, 0);
2223ac7ea91SChristian Brauner 		return;
2233ac7ea91SChristian Brauner 	}
2243ac7ea91SChristian Brauner 
2253ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
2263ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
2273ac7ea91SChristian Brauner 
2283ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
2293ac7ea91SChristian Brauner 
2303ac7ea91SChristian Brauner 	if (variant->expect_different_ns) {
2313ac7ea91SChristian Brauner 		/* Verify we can get the namespace ID from the fd */
2323ac7ea91SChristian Brauner 		ret = get_mnt_ns_id(self->fd, &new_ns_id);
2333ac7ea91SChristian Brauner 		ASSERT_EQ(ret, 0);
2343ac7ea91SChristian Brauner 
2353ac7ea91SChristian Brauner 		/* Verify it's a different namespace */
2363ac7ea91SChristian Brauner 		ASSERT_NE(new_ns_id, self->current_ns_id);
2373ac7ea91SChristian Brauner 
2383ac7ea91SChristian Brauner 		/* List mounts in the new namespace */
2393ac7ea91SChristian Brauner 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
2403ac7ea91SChristian Brauner 		ASSERT_GE(nr_mounts, 0) {
2413ac7ea91SChristian Brauner 			TH_LOG("%m - listmount failed");
2423ac7ea91SChristian Brauner 		}
2433ac7ea91SChristian Brauner 
2443ac7ea91SChristian Brauner 		/* Verify minimum expected mounts */
2453ac7ea91SChristian Brauner 		ASSERT_GE(nr_mounts, variant->min_mounts);
2463ac7ea91SChristian Brauner 		TH_LOG("Namespace contains %zd mounts", nr_mounts);
2473ac7ea91SChristian Brauner 	}
2483ac7ea91SChristian Brauner }
2493ac7ea91SChristian Brauner 
2503ac7ea91SChristian Brauner TEST_F(fsmount_ns, setns_into_namespace)
2513ac7ea91SChristian Brauner {
2523ac7ea91SChristian Brauner 	uint64_t new_ns_id;
2533ac7ea91SChristian Brauner 	pid_t pid;
2543ac7ea91SChristian Brauner 	int status;
2553ac7ea91SChristian Brauner 	int ret;
2563ac7ea91SChristian Brauner 
2573ac7ea91SChristian Brauner 	/* Only test with FSMOUNT_NAMESPACE flag */
2583ac7ea91SChristian Brauner 	if (!(variant->flags & FSMOUNT_NAMESPACE))
2593ac7ea91SChristian Brauner 		SKIP(return, "setns test only for FSMOUNT_NAMESPACE case");
2603ac7ea91SChristian Brauner 
2613ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
2623ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
2633ac7ea91SChristian Brauner 
2643ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, variant->flags, 0);
2653ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
2663ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
2673ac7ea91SChristian Brauner 
2683ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
2693ac7ea91SChristian Brauner 
2703ac7ea91SChristian Brauner 	/* Get namespace ID and dump all mounts */
2713ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
2723ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
2733ac7ea91SChristian Brauner 
2743ac7ea91SChristian Brauner 	dump_mounts(_metadata, new_ns_id);
2753ac7ea91SChristian Brauner 
2763ac7ea91SChristian Brauner 	pid = fork();
2773ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
2783ac7ea91SChristian Brauner 
2793ac7ea91SChristian Brauner 	if (pid == 0) {
2803ac7ea91SChristian Brauner 		/* Child: try to enter the namespace */
2813ac7ea91SChristian Brauner 		if (setns(self->fd, CLONE_NEWNS) < 0)
2823ac7ea91SChristian Brauner 			_exit(1);
2833ac7ea91SChristian Brauner 		_exit(0);
2843ac7ea91SChristian Brauner 	}
2853ac7ea91SChristian Brauner 
2863ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
2873ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
2883ac7ea91SChristian Brauner 	ASSERT_EQ(WEXITSTATUS(status), 0);
2893ac7ea91SChristian Brauner }
2903ac7ea91SChristian Brauner 
2913ac7ea91SChristian Brauner TEST_F(fsmount_ns, verify_mount_properties)
2923ac7ea91SChristian Brauner {
2933ac7ea91SChristian Brauner 	struct statmount sm;
2943ac7ea91SChristian Brauner 	uint64_t new_ns_id;
2953ac7ea91SChristian Brauner 	uint64_t list[256];
2963ac7ea91SChristian Brauner 	ssize_t nr_mounts;
2973ac7ea91SChristian Brauner 	int ret;
2983ac7ea91SChristian Brauner 
2993ac7ea91SChristian Brauner 	/* Only test with basic FSMOUNT_NAMESPACE flags */
3003ac7ea91SChristian Brauner 	if (variant->flags != (FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC))
3013ac7ea91SChristian Brauner 		SKIP(return, "mount properties test only for basic case");
3023ac7ea91SChristian Brauner 
3033ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
3043ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
3053ac7ea91SChristian Brauner 
3063ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
3073ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
3083ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
3093ac7ea91SChristian Brauner 
3103ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
3113ac7ea91SChristian Brauner 
3123ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
3133ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
3143ac7ea91SChristian Brauner 
3153ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
3163ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
3173ac7ea91SChristian Brauner 
3183ac7ea91SChristian Brauner 	/* Get info about the root mount */
3193ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
3203ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
3213ac7ea91SChristian Brauner 
3223ac7ea91SChristian Brauner 	TH_LOG("Root mount id: %llu, parent: %llu",
3233ac7ea91SChristian Brauner 	       (unsigned long long)sm.mnt_id,
3243ac7ea91SChristian Brauner 	       (unsigned long long)sm.mnt_parent_id);
3253ac7ea91SChristian Brauner }
3263ac7ea91SChristian Brauner 
3273ac7ea91SChristian Brauner TEST_F(fsmount_ns, verify_tmpfs_type)
3283ac7ea91SChristian Brauner {
3293ac7ea91SChristian Brauner 	struct statmount *sm;
3303ac7ea91SChristian Brauner 	uint64_t new_ns_id;
3313ac7ea91SChristian Brauner 	uint64_t list[256];
3323ac7ea91SChristian Brauner 	ssize_t nr_mounts;
3333ac7ea91SChristian Brauner 	const char *fs_type;
3343ac7ea91SChristian Brauner 	int ret;
3353ac7ea91SChristian Brauner 
3363ac7ea91SChristian Brauner 	/* Only test with basic FSMOUNT_NAMESPACE flags */
3373ac7ea91SChristian Brauner 	if (variant->flags != (FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC))
3383ac7ea91SChristian Brauner 		SKIP(return, "fs type test only for basic case");
3393ac7ea91SChristian Brauner 
3403ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
3413ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
3423ac7ea91SChristian Brauner 
3433ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
3443ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
3453ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
3463ac7ea91SChristian Brauner 
3473ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
3483ac7ea91SChristian Brauner 
3493ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
3503ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
3513ac7ea91SChristian Brauner 
3523ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
3533ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
3543ac7ea91SChristian Brauner 
3553ac7ea91SChristian Brauner 	sm = statmount_alloc(list[0], new_ns_id, STATMOUNT_FS_TYPE, 0);
3563ac7ea91SChristian Brauner 	ASSERT_NE(sm, NULL);
3573ac7ea91SChristian Brauner 
3583ac7ea91SChristian Brauner 	fs_type = sm->str + sm->fs_type;
3593ac7ea91SChristian Brauner 	ASSERT_STREQ(fs_type, "tmpfs");
3603ac7ea91SChristian Brauner 
3613ac7ea91SChristian Brauner 	free(sm);
3623ac7ea91SChristian Brauner }
3633ac7ea91SChristian Brauner 
3643ac7ea91SChristian Brauner FIXTURE(fsmount_ns_caps)
3653ac7ea91SChristian Brauner {
3663ac7ea91SChristian Brauner 	bool has_caps;
3673ac7ea91SChristian Brauner };
3683ac7ea91SChristian Brauner 
3693ac7ea91SChristian Brauner FIXTURE_SETUP(fsmount_ns_caps)
3703ac7ea91SChristian Brauner {
3713ac7ea91SChristian Brauner 	int ret;
3723ac7ea91SChristian Brauner 
3733ac7ea91SChristian Brauner 	/* Check if fsopen syscall is supported */
3743ac7ea91SChristian Brauner 	ret = sys_fsopen("tmpfs", 0);
3753ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
3763ac7ea91SChristian Brauner 		SKIP(return, "fsopen() syscall not supported");
3773ac7ea91SChristian Brauner 	if (ret >= 0)
3783ac7ea91SChristian Brauner 		close(ret);
3793ac7ea91SChristian Brauner 
3803ac7ea91SChristian Brauner 	self->has_caps = (geteuid() == 0);
3813ac7ea91SChristian Brauner }
3823ac7ea91SChristian Brauner 
3833ac7ea91SChristian Brauner FIXTURE_TEARDOWN(fsmount_ns_caps)
3843ac7ea91SChristian Brauner {
3853ac7ea91SChristian Brauner }
3863ac7ea91SChristian Brauner 
3873ac7ea91SChristian Brauner TEST_F(fsmount_ns_caps, requires_cap_sys_admin)
3883ac7ea91SChristian Brauner {
3893ac7ea91SChristian Brauner 	pid_t pid;
3903ac7ea91SChristian Brauner 	int status;
391*660c0940SChristian Brauner 	int fs_fd;
392*660c0940SChristian Brauner 
393*660c0940SChristian Brauner 	/*
394*660c0940SChristian Brauner 	 * Prepare the configured filesystem fd as root before forking.
395*660c0940SChristian Brauner 	 * fsopen() requires CAP_SYS_ADMIN in the mount namespace's
396*660c0940SChristian Brauner 	 * user_ns, which won't be available after enter_userns().
397*660c0940SChristian Brauner 	 */
398*660c0940SChristian Brauner 	fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
399*660c0940SChristian Brauner 	ASSERT_GE(fs_fd, 0);
400*660c0940SChristian Brauner 
401*660c0940SChristian Brauner 	ASSERT_EQ(sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
4023ac7ea91SChristian Brauner 
4033ac7ea91SChristian Brauner 	pid = fork();
4043ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
4053ac7ea91SChristian Brauner 
4063ac7ea91SChristian Brauner 	if (pid == 0) {
407*660c0940SChristian Brauner 		int fd;
4083ac7ea91SChristian Brauner 
4093ac7ea91SChristian Brauner 		/* Child: drop privileges using utils.h helper */
4103ac7ea91SChristian Brauner 		if (enter_userns() != 0)
4113ac7ea91SChristian Brauner 			_exit(2);
4123ac7ea91SChristian Brauner 
4133ac7ea91SChristian Brauner 		/* Drop all caps using utils.h helper */
4143ac7ea91SChristian Brauner 		if (caps_down() == 0)
4153ac7ea91SChristian Brauner 			_exit(3);
4163ac7ea91SChristian Brauner 
4173ac7ea91SChristian Brauner 		fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
4183ac7ea91SChristian Brauner 		close(fs_fd);
4193ac7ea91SChristian Brauner 
4203ac7ea91SChristian Brauner 		if (fd >= 0) {
4213ac7ea91SChristian Brauner 			close(fd);
4223ac7ea91SChristian Brauner 			/* Should have failed without caps */
4233ac7ea91SChristian Brauner 			_exit(1);
4243ac7ea91SChristian Brauner 		}
4253ac7ea91SChristian Brauner 
4263ac7ea91SChristian Brauner 		if (errno == EPERM)
4273ac7ea91SChristian Brauner 			_exit(0);
4283ac7ea91SChristian Brauner 
4293ac7ea91SChristian Brauner 		/* EINVAL means FSMOUNT_NAMESPACE not supported */
4303ac7ea91SChristian Brauner 		if (errno == EINVAL)
4313ac7ea91SChristian Brauner 			_exit(6);
4323ac7ea91SChristian Brauner 
4333ac7ea91SChristian Brauner 		/* Unexpected error */
4343ac7ea91SChristian Brauner 		_exit(7);
4353ac7ea91SChristian Brauner 	}
4363ac7ea91SChristian Brauner 
437*660c0940SChristian Brauner 	close(fs_fd);
4383ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
4393ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
4403ac7ea91SChristian Brauner 
4413ac7ea91SChristian Brauner 	switch (WEXITSTATUS(status)) {
4423ac7ea91SChristian Brauner 	case 0:
4433ac7ea91SChristian Brauner 		/* Expected: EPERM without caps */
4443ac7ea91SChristian Brauner 		break;
4453ac7ea91SChristian Brauner 	case 1:
4463ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("FSMOUNT_NAMESPACE succeeded without caps");
4473ac7ea91SChristian Brauner 		break;
4483ac7ea91SChristian Brauner 	case 2:
4493ac7ea91SChristian Brauner 		SKIP(return, "enter_userns failed");
4503ac7ea91SChristian Brauner 		break;
4513ac7ea91SChristian Brauner 	case 3:
4523ac7ea91SChristian Brauner 		SKIP(return, "caps_down failed");
4533ac7ea91SChristian Brauner 		break;
4543ac7ea91SChristian Brauner 	case 6:
4553ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
4563ac7ea91SChristian Brauner 		break;
4573ac7ea91SChristian Brauner 	default:
4583ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
4593ac7ea91SChristian Brauner 					  WEXITSTATUS(status));
4603ac7ea91SChristian Brauner 		break;
4613ac7ea91SChristian Brauner 	}
4623ac7ea91SChristian Brauner }
4633ac7ea91SChristian Brauner 
4643ac7ea91SChristian Brauner FIXTURE(fsmount_ns_userns)
4653ac7ea91SChristian Brauner {
4663ac7ea91SChristian Brauner 	int fd;
4673ac7ea91SChristian Brauner 	int fs_fd;
4683ac7ea91SChristian Brauner };
4693ac7ea91SChristian Brauner 
4703ac7ea91SChristian Brauner FIXTURE_SETUP(fsmount_ns_userns)
4713ac7ea91SChristian Brauner {
4723ac7ea91SChristian Brauner 	int ret;
4733ac7ea91SChristian Brauner 
4743ac7ea91SChristian Brauner 	self->fd = -1;
4753ac7ea91SChristian Brauner 	self->fs_fd = -1;
4763ac7ea91SChristian Brauner 
4773ac7ea91SChristian Brauner 	/* Check if fsopen syscall is supported */
4783ac7ea91SChristian Brauner 	ret = sys_fsopen("tmpfs", 0);
4793ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
4803ac7ea91SChristian Brauner 		SKIP(return, "fsopen() syscall not supported");
4813ac7ea91SChristian Brauner 	if (ret >= 0)
4823ac7ea91SChristian Brauner 		close(ret);
4833ac7ea91SChristian Brauner 
4843ac7ea91SChristian Brauner 	/* Check if statmount/listmount are supported */
4853ac7ea91SChristian Brauner 	ret = statmount(0, 0, 0, 0, NULL, 0, 0);
4863ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
4873ac7ea91SChristian Brauner 		SKIP(return, "statmount() syscall not supported");
4883ac7ea91SChristian Brauner }
4893ac7ea91SChristian Brauner 
4903ac7ea91SChristian Brauner FIXTURE_TEARDOWN(fsmount_ns_userns)
4913ac7ea91SChristian Brauner {
4923ac7ea91SChristian Brauner 	if (self->fd >= 0)
4933ac7ea91SChristian Brauner 		close(self->fd);
4943ac7ea91SChristian Brauner 	if (self->fs_fd >= 0)
4953ac7ea91SChristian Brauner 		close(self->fs_fd);
4963ac7ea91SChristian Brauner }
4973ac7ea91SChristian Brauner 
4983ac7ea91SChristian Brauner TEST_F(fsmount_ns_userns, create_in_userns)
4993ac7ea91SChristian Brauner {
5003ac7ea91SChristian Brauner 	pid_t pid;
5013ac7ea91SChristian Brauner 	int status;
5023ac7ea91SChristian Brauner 
5033ac7ea91SChristian Brauner 	pid = fork();
5043ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
5053ac7ea91SChristian Brauner 
5063ac7ea91SChristian Brauner 	if (pid == 0) {
5073ac7ea91SChristian Brauner 		uint64_t new_ns_id;
5083ac7ea91SChristian Brauner 		uint64_t list[256];
5093ac7ea91SChristian Brauner 		ssize_t nr_mounts;
5103ac7ea91SChristian Brauner 		int fs_fd, fd;
5113ac7ea91SChristian Brauner 
5123ac7ea91SChristian Brauner 		/* Create new user namespace (also creates mount namespace) */
5133ac7ea91SChristian Brauner 		if (setup_userns() != 0)
5143ac7ea91SChristian Brauner 			_exit(2);
5153ac7ea91SChristian Brauner 
5163ac7ea91SChristian Brauner 		/* Now we have CAP_SYS_ADMIN in the user namespace */
5173ac7ea91SChristian Brauner 		fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
5183ac7ea91SChristian Brauner 		if (fs_fd < 0)
5193ac7ea91SChristian Brauner 			_exit(3);
5203ac7ea91SChristian Brauner 
5213ac7ea91SChristian Brauner 		if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
5223ac7ea91SChristian Brauner 			close(fs_fd);
5233ac7ea91SChristian Brauner 			_exit(4);
5243ac7ea91SChristian Brauner 		}
5253ac7ea91SChristian Brauner 
5263ac7ea91SChristian Brauner 		fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
5273ac7ea91SChristian Brauner 		close(fs_fd);
5283ac7ea91SChristian Brauner 
5293ac7ea91SChristian Brauner 		if (fd < 0) {
5303ac7ea91SChristian Brauner 			if (errno == EINVAL)
5313ac7ea91SChristian Brauner 				_exit(6); /* FSMOUNT_NAMESPACE not supported */
5323ac7ea91SChristian Brauner 			_exit(1);
5333ac7ea91SChristian Brauner 		}
5343ac7ea91SChristian Brauner 
5353ac7ea91SChristian Brauner 		/* Verify we can get the namespace ID */
5363ac7ea91SChristian Brauner 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
5373ac7ea91SChristian Brauner 			_exit(7);
5383ac7ea91SChristian Brauner 
5393ac7ea91SChristian Brauner 		/* Verify we can list mounts in the new namespace */
5403ac7ea91SChristian Brauner 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
5413ac7ea91SChristian Brauner 		if (nr_mounts < 0)
5423ac7ea91SChristian Brauner 			_exit(8);
5433ac7ea91SChristian Brauner 
5443ac7ea91SChristian Brauner 		/* Should have at least 1 mount (the tmpfs) */
5453ac7ea91SChristian Brauner 		if (nr_mounts < 1)
5463ac7ea91SChristian Brauner 			_exit(9);
5473ac7ea91SChristian Brauner 
5483ac7ea91SChristian Brauner 		close(fd);
5493ac7ea91SChristian Brauner 		_exit(0);
5503ac7ea91SChristian Brauner 	}
5513ac7ea91SChristian Brauner 
5523ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
5533ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
5543ac7ea91SChristian Brauner 
5553ac7ea91SChristian Brauner 	switch (WEXITSTATUS(status)) {
5563ac7ea91SChristian Brauner 	case 0:
5573ac7ea91SChristian Brauner 		/* Success */
5583ac7ea91SChristian Brauner 		break;
5593ac7ea91SChristian Brauner 	case 1:
5603ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed in userns");
5613ac7ea91SChristian Brauner 		break;
5623ac7ea91SChristian Brauner 	case 2:
5633ac7ea91SChristian Brauner 		SKIP(return, "setup_userns failed");
5643ac7ea91SChristian Brauner 		break;
5653ac7ea91SChristian Brauner 	case 3:
5663ac7ea91SChristian Brauner 		SKIP(return, "fsopen failed in userns");
5673ac7ea91SChristian Brauner 		break;
5683ac7ea91SChristian Brauner 	case 4:
5693ac7ea91SChristian Brauner 		SKIP(return, "fsconfig CMD_CREATE failed in userns");
5703ac7ea91SChristian Brauner 		break;
5713ac7ea91SChristian Brauner 	case 6:
5723ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
5733ac7ea91SChristian Brauner 		break;
5743ac7ea91SChristian Brauner 	case 7:
5753ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
5763ac7ea91SChristian Brauner 		break;
5773ac7ea91SChristian Brauner 	case 8:
5783ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace");
5793ac7ea91SChristian Brauner 		break;
5803ac7ea91SChristian Brauner 	case 9:
5813ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("New namespace has no mounts");
5823ac7ea91SChristian Brauner 		break;
5833ac7ea91SChristian Brauner 	default:
5843ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
5853ac7ea91SChristian Brauner 					  WEXITSTATUS(status));
5863ac7ea91SChristian Brauner 		break;
5873ac7ea91SChristian Brauner 	}
5883ac7ea91SChristian Brauner }
5893ac7ea91SChristian Brauner 
5903ac7ea91SChristian Brauner TEST_F(fsmount_ns_userns, setns_in_userns)
5913ac7ea91SChristian Brauner {
5923ac7ea91SChristian Brauner 	pid_t pid;
5933ac7ea91SChristian Brauner 	int status;
5943ac7ea91SChristian Brauner 
5953ac7ea91SChristian Brauner 	pid = fork();
5963ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
5973ac7ea91SChristian Brauner 
5983ac7ea91SChristian Brauner 	if (pid == 0) {
5993ac7ea91SChristian Brauner 		uint64_t new_ns_id;
6003ac7ea91SChristian Brauner 		int fs_fd, fd;
6013ac7ea91SChristian Brauner 		pid_t inner_pid;
6023ac7ea91SChristian Brauner 		int inner_status;
6033ac7ea91SChristian Brauner 
6043ac7ea91SChristian Brauner 		/* Create new user namespace */
6053ac7ea91SChristian Brauner 		if (setup_userns() != 0)
6063ac7ea91SChristian Brauner 			_exit(2);
6073ac7ea91SChristian Brauner 
6083ac7ea91SChristian Brauner 		fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
6093ac7ea91SChristian Brauner 		if (fs_fd < 0)
6103ac7ea91SChristian Brauner 			_exit(3);
6113ac7ea91SChristian Brauner 
6123ac7ea91SChristian Brauner 		if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
6133ac7ea91SChristian Brauner 			close(fs_fd);
6143ac7ea91SChristian Brauner 			_exit(4);
6153ac7ea91SChristian Brauner 		}
6163ac7ea91SChristian Brauner 
6173ac7ea91SChristian Brauner 		fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
6183ac7ea91SChristian Brauner 		close(fs_fd);
6193ac7ea91SChristian Brauner 
6203ac7ea91SChristian Brauner 		if (fd < 0) {
6213ac7ea91SChristian Brauner 			if (errno == EINVAL)
6223ac7ea91SChristian Brauner 				_exit(6);
6233ac7ea91SChristian Brauner 			_exit(1);
6243ac7ea91SChristian Brauner 		}
6253ac7ea91SChristian Brauner 
6263ac7ea91SChristian Brauner 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
6273ac7ea91SChristian Brauner 			_exit(7);
6283ac7ea91SChristian Brauner 
6293ac7ea91SChristian Brauner 		/* Fork again to test setns into the new namespace */
6303ac7ea91SChristian Brauner 		inner_pid = fork();
6313ac7ea91SChristian Brauner 		if (inner_pid < 0)
6323ac7ea91SChristian Brauner 			_exit(10);
6333ac7ea91SChristian Brauner 
6343ac7ea91SChristian Brauner 		if (inner_pid == 0) {
6353ac7ea91SChristian Brauner 			/* Inner child: enter the new namespace */
6363ac7ea91SChristian Brauner 			if (setns(fd, CLONE_NEWNS) < 0)
6373ac7ea91SChristian Brauner 				_exit(1);
6383ac7ea91SChristian Brauner 			_exit(0);
6393ac7ea91SChristian Brauner 		}
6403ac7ea91SChristian Brauner 
6413ac7ea91SChristian Brauner 		if (waitpid(inner_pid, &inner_status, 0) != inner_pid)
6423ac7ea91SChristian Brauner 			_exit(11);
6433ac7ea91SChristian Brauner 
6443ac7ea91SChristian Brauner 		if (!WIFEXITED(inner_status) || WEXITSTATUS(inner_status) != 0)
6453ac7ea91SChristian Brauner 			_exit(12);
6463ac7ea91SChristian Brauner 
6473ac7ea91SChristian Brauner 		close(fd);
6483ac7ea91SChristian Brauner 		_exit(0);
6493ac7ea91SChristian Brauner 	}
6503ac7ea91SChristian Brauner 
6513ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
6523ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
6533ac7ea91SChristian Brauner 
6543ac7ea91SChristian Brauner 	switch (WEXITSTATUS(status)) {
6553ac7ea91SChristian Brauner 	case 0:
6563ac7ea91SChristian Brauner 		/* Success */
6573ac7ea91SChristian Brauner 		break;
6583ac7ea91SChristian Brauner 	case 1:
6593ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("fsmount or setns failed in userns");
6603ac7ea91SChristian Brauner 		break;
6613ac7ea91SChristian Brauner 	case 2:
6623ac7ea91SChristian Brauner 		SKIP(return, "setup_userns failed");
6633ac7ea91SChristian Brauner 		break;
6643ac7ea91SChristian Brauner 	case 3:
6653ac7ea91SChristian Brauner 		SKIP(return, "fsopen failed in userns");
6663ac7ea91SChristian Brauner 		break;
6673ac7ea91SChristian Brauner 	case 4:
6683ac7ea91SChristian Brauner 		SKIP(return, "fsconfig CMD_CREATE failed in userns");
6693ac7ea91SChristian Brauner 		break;
6703ac7ea91SChristian Brauner 	case 6:
6713ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
6723ac7ea91SChristian Brauner 		break;
6733ac7ea91SChristian Brauner 	case 7:
6743ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
6753ac7ea91SChristian Brauner 		break;
6763ac7ea91SChristian Brauner 	case 10:
6773ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Inner fork failed");
6783ac7ea91SChristian Brauner 		break;
6793ac7ea91SChristian Brauner 	case 11:
6803ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Inner waitpid failed");
6813ac7ea91SChristian Brauner 		break;
6823ac7ea91SChristian Brauner 	case 12:
6833ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
6843ac7ea91SChristian Brauner 		break;
6853ac7ea91SChristian Brauner 	default:
6863ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
6873ac7ea91SChristian Brauner 					  WEXITSTATUS(status));
6883ac7ea91SChristian Brauner 		break;
6893ac7ea91SChristian Brauner 	}
6903ac7ea91SChristian Brauner }
6913ac7ea91SChristian Brauner 
6923ac7ea91SChristian Brauner TEST_F(fsmount_ns_userns, umount_fails_einval)
6933ac7ea91SChristian Brauner {
6943ac7ea91SChristian Brauner 	pid_t pid;
6953ac7ea91SChristian Brauner 	int status;
6963ac7ea91SChristian Brauner 
6973ac7ea91SChristian Brauner 	pid = fork();
6983ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
6993ac7ea91SChristian Brauner 
7003ac7ea91SChristian Brauner 	if (pid == 0) {
7013ac7ea91SChristian Brauner 		uint64_t new_ns_id;
7023ac7ea91SChristian Brauner 		uint64_t list[256];
7033ac7ea91SChristian Brauner 		ssize_t nr_mounts;
7043ac7ea91SChristian Brauner 		int fs_fd, fd;
7053ac7ea91SChristian Brauner 		ssize_t i;
7063ac7ea91SChristian Brauner 
7073ac7ea91SChristian Brauner 		/* Create new user namespace */
7083ac7ea91SChristian Brauner 		if (setup_userns() != 0)
7093ac7ea91SChristian Brauner 			_exit(2);
7103ac7ea91SChristian Brauner 
7113ac7ea91SChristian Brauner 		fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
7123ac7ea91SChristian Brauner 		if (fs_fd < 0)
7133ac7ea91SChristian Brauner 			_exit(3);
7143ac7ea91SChristian Brauner 
7153ac7ea91SChristian Brauner 		if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
7163ac7ea91SChristian Brauner 			close(fs_fd);
7173ac7ea91SChristian Brauner 			_exit(4);
7183ac7ea91SChristian Brauner 		}
7193ac7ea91SChristian Brauner 
7203ac7ea91SChristian Brauner 		fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
7213ac7ea91SChristian Brauner 		close(fs_fd);
7223ac7ea91SChristian Brauner 
7233ac7ea91SChristian Brauner 		if (fd < 0) {
7243ac7ea91SChristian Brauner 			if (errno == EINVAL)
7253ac7ea91SChristian Brauner 				_exit(6);
7263ac7ea91SChristian Brauner 			_exit(1);
7273ac7ea91SChristian Brauner 		}
7283ac7ea91SChristian Brauner 
7293ac7ea91SChristian Brauner 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
7303ac7ea91SChristian Brauner 			_exit(7);
7313ac7ea91SChristian Brauner 
7323ac7ea91SChristian Brauner 		/* Get all mounts in the new namespace */
7333ac7ea91SChristian Brauner 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
7343ac7ea91SChristian Brauner 		if (nr_mounts < 0)
7353ac7ea91SChristian Brauner 			_exit(13);
7363ac7ea91SChristian Brauner 
7373ac7ea91SChristian Brauner 		if (nr_mounts < 1)
7383ac7ea91SChristian Brauner 			_exit(14);
7393ac7ea91SChristian Brauner 
7403ac7ea91SChristian Brauner 		/* Enter the new namespace */
7413ac7ea91SChristian Brauner 		if (setns(fd, CLONE_NEWNS) < 0)
7423ac7ea91SChristian Brauner 			_exit(8);
7433ac7ea91SChristian Brauner 
7443ac7ea91SChristian Brauner 		for (i = 0; i < nr_mounts; i++) {
7453ac7ea91SChristian Brauner 			struct statmount *sm;
7463ac7ea91SChristian Brauner 			const char *mnt_point;
7473ac7ea91SChristian Brauner 
7483ac7ea91SChristian Brauner 			sm = statmount_alloc(list[i], new_ns_id,
7493ac7ea91SChristian Brauner 					     STATMOUNT_MNT_POINT, 0);
7503ac7ea91SChristian Brauner 			if (!sm)
7513ac7ea91SChristian Brauner 				_exit(15);
7523ac7ea91SChristian Brauner 
7533ac7ea91SChristian Brauner 			mnt_point = sm->str + sm->mnt_point;
7543ac7ea91SChristian Brauner 
7553ac7ea91SChristian Brauner 			if (umount2(mnt_point, MNT_DETACH) == 0) {
7563ac7ea91SChristian Brauner 				free(sm);
7573ac7ea91SChristian Brauner 				_exit(9);
7583ac7ea91SChristian Brauner 			}
7593ac7ea91SChristian Brauner 
7603ac7ea91SChristian Brauner 			if (errno != EINVAL) {
7613ac7ea91SChristian Brauner 				/* Wrong error */
7623ac7ea91SChristian Brauner 				free(sm);
7633ac7ea91SChristian Brauner 				_exit(10);
7643ac7ea91SChristian Brauner 			}
7653ac7ea91SChristian Brauner 
7663ac7ea91SChristian Brauner 			free(sm);
7673ac7ea91SChristian Brauner 		}
7683ac7ea91SChristian Brauner 
7693ac7ea91SChristian Brauner 		close(fd);
7703ac7ea91SChristian Brauner 		_exit(0);
7713ac7ea91SChristian Brauner 	}
7723ac7ea91SChristian Brauner 
7733ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
7743ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
7753ac7ea91SChristian Brauner 
7763ac7ea91SChristian Brauner 	switch (WEXITSTATUS(status)) {
7773ac7ea91SChristian Brauner 	case 0:
7783ac7ea91SChristian Brauner 		break;
7793ac7ea91SChristian Brauner 	case 1:
7803ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed");
7813ac7ea91SChristian Brauner 		break;
7823ac7ea91SChristian Brauner 	case 2:
7833ac7ea91SChristian Brauner 		SKIP(return, "setup_userns failed");
7843ac7ea91SChristian Brauner 		break;
7853ac7ea91SChristian Brauner 	case 3:
7863ac7ea91SChristian Brauner 		SKIP(return, "fsopen failed in userns");
7873ac7ea91SChristian Brauner 		break;
7883ac7ea91SChristian Brauner 	case 4:
7893ac7ea91SChristian Brauner 		SKIP(return, "fsconfig CMD_CREATE failed in userns");
7903ac7ea91SChristian Brauner 		break;
7913ac7ea91SChristian Brauner 	case 6:
7923ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
7933ac7ea91SChristian Brauner 		break;
7943ac7ea91SChristian Brauner 	case 7:
7953ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
7963ac7ea91SChristian Brauner 		break;
7973ac7ea91SChristian Brauner 	case 8:
7983ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
7993ac7ea91SChristian Brauner 		break;
8003ac7ea91SChristian Brauner 	case 9:
8013ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL");
8023ac7ea91SChristian Brauner 		break;
8033ac7ea91SChristian Brauner 	case 10:
8043ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("umount failed with wrong error (expected EINVAL)");
8053ac7ea91SChristian Brauner 		break;
8063ac7ea91SChristian Brauner 	case 13:
8073ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("listmount failed");
8083ac7ea91SChristian Brauner 		break;
8093ac7ea91SChristian Brauner 	case 14:
8103ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
8113ac7ea91SChristian Brauner 		break;
8123ac7ea91SChristian Brauner 	case 15:
8133ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
8143ac7ea91SChristian Brauner 		break;
8153ac7ea91SChristian Brauner 	default:
8163ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
8173ac7ea91SChristian Brauner 					  WEXITSTATUS(status));
8183ac7ea91SChristian Brauner 		break;
8193ac7ea91SChristian Brauner 	}
8203ac7ea91SChristian Brauner }
8213ac7ea91SChristian Brauner 
8223ac7ea91SChristian Brauner TEST_F(fsmount_ns_userns, umount_succeeds)
8233ac7ea91SChristian Brauner {
8243ac7ea91SChristian Brauner 	pid_t pid;
8253ac7ea91SChristian Brauner 	int status;
8263ac7ea91SChristian Brauner 
8273ac7ea91SChristian Brauner 	pid = fork();
8283ac7ea91SChristian Brauner 	ASSERT_GE(pid, 0);
8293ac7ea91SChristian Brauner 
8303ac7ea91SChristian Brauner 	if (pid == 0) {
8313ac7ea91SChristian Brauner 		uint64_t new_ns_id;
8323ac7ea91SChristian Brauner 		uint64_t list[256];
8333ac7ea91SChristian Brauner 		ssize_t nr_mounts;
8343ac7ea91SChristian Brauner 		int fs_fd, fd;
8353ac7ea91SChristian Brauner 		ssize_t i;
8363ac7ea91SChristian Brauner 
8373ac7ea91SChristian Brauner 		if (unshare(CLONE_NEWNS))
8383ac7ea91SChristian Brauner 			_exit(1);
8393ac7ea91SChristian Brauner 
8403ac7ea91SChristian Brauner 		if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) != 0)
8413ac7ea91SChristian Brauner 			_exit(1);
8423ac7ea91SChristian Brauner 
8433ac7ea91SChristian Brauner 		fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC);
8443ac7ea91SChristian Brauner 		if (fs_fd < 0)
8453ac7ea91SChristian Brauner 			_exit(3);
8463ac7ea91SChristian Brauner 
8473ac7ea91SChristian Brauner 		if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
8483ac7ea91SChristian Brauner 			close(fs_fd);
8493ac7ea91SChristian Brauner 			_exit(4);
8503ac7ea91SChristian Brauner 		}
8513ac7ea91SChristian Brauner 
8523ac7ea91SChristian Brauner 		fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0);
8533ac7ea91SChristian Brauner 		close(fs_fd);
8543ac7ea91SChristian Brauner 
8553ac7ea91SChristian Brauner 		if (fd < 0) {
8563ac7ea91SChristian Brauner 			if (errno == EINVAL)
8573ac7ea91SChristian Brauner 				_exit(6);
8583ac7ea91SChristian Brauner 			_exit(1);
8593ac7ea91SChristian Brauner 		}
8603ac7ea91SChristian Brauner 
8613ac7ea91SChristian Brauner 		if (get_mnt_ns_id(fd, &new_ns_id) != 0)
8623ac7ea91SChristian Brauner 			_exit(7);
8633ac7ea91SChristian Brauner 
8643ac7ea91SChristian Brauner 		/* Get all mounts in the new namespace */
8653ac7ea91SChristian Brauner 		nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE);
8663ac7ea91SChristian Brauner 		if (nr_mounts < 0)
8673ac7ea91SChristian Brauner 			_exit(13);
8683ac7ea91SChristian Brauner 
8693ac7ea91SChristian Brauner 		if (nr_mounts < 1)
8703ac7ea91SChristian Brauner 			_exit(14);
8713ac7ea91SChristian Brauner 
8723ac7ea91SChristian Brauner 		/* Enter the new namespace */
8733ac7ea91SChristian Brauner 		if (setns(fd, CLONE_NEWNS) < 0)
8743ac7ea91SChristian Brauner 			_exit(8);
8753ac7ea91SChristian Brauner 
8763ac7ea91SChristian Brauner 		for (i = 0; i < nr_mounts; i++) {
8773ac7ea91SChristian Brauner 			struct statmount *sm;
8783ac7ea91SChristian Brauner 			const char *mnt_point;
8793ac7ea91SChristian Brauner 
8803ac7ea91SChristian Brauner 			sm = statmount_alloc(list[i], new_ns_id,
8813ac7ea91SChristian Brauner 					     STATMOUNT_MNT_POINT, 0);
8823ac7ea91SChristian Brauner 			if (!sm)
8833ac7ea91SChristian Brauner 				_exit(15);
8843ac7ea91SChristian Brauner 
8853ac7ea91SChristian Brauner 			mnt_point = sm->str + sm->mnt_point;
8863ac7ea91SChristian Brauner 
8873ac7ea91SChristian Brauner 			if (umount2(mnt_point, MNT_DETACH) != 0) {
8883ac7ea91SChristian Brauner 				free(sm);
8893ac7ea91SChristian Brauner 				_exit(9);
8903ac7ea91SChristian Brauner 			}
8913ac7ea91SChristian Brauner 
8923ac7ea91SChristian Brauner 			free(sm);
8933ac7ea91SChristian Brauner 		}
8943ac7ea91SChristian Brauner 
8953ac7ea91SChristian Brauner 		close(fd);
8963ac7ea91SChristian Brauner 		_exit(0);
8973ac7ea91SChristian Brauner 	}
8983ac7ea91SChristian Brauner 
8993ac7ea91SChristian Brauner 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
9003ac7ea91SChristian Brauner 	ASSERT_TRUE(WIFEXITED(status));
9013ac7ea91SChristian Brauner 
9023ac7ea91SChristian Brauner 	switch (WEXITSTATUS(status)) {
9033ac7ea91SChristian Brauner 	case 0:
9043ac7ea91SChristian Brauner 		break;
9053ac7ea91SChristian Brauner 	case 1:
9063ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed or unshare failed");
9073ac7ea91SChristian Brauner 		break;
9083ac7ea91SChristian Brauner 	case 3:
9093ac7ea91SChristian Brauner 		SKIP(return, "fsopen failed");
9103ac7ea91SChristian Brauner 		break;
9113ac7ea91SChristian Brauner 	case 4:
9123ac7ea91SChristian Brauner 		SKIP(return, "fsconfig CMD_CREATE failed");
9133ac7ea91SChristian Brauner 		break;
9143ac7ea91SChristian Brauner 	case 6:
9153ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
9163ac7ea91SChristian Brauner 		break;
9173ac7ea91SChristian Brauner 	case 7:
9183ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID");
9193ac7ea91SChristian Brauner 		break;
9203ac7ea91SChristian Brauner 	case 8:
9213ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("setns into new namespace failed");
9223ac7ea91SChristian Brauner 		break;
9233ac7ea91SChristian Brauner 	case 9:
9243ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("umount failed but should have succeeded");
9253ac7ea91SChristian Brauner 		break;
9263ac7ea91SChristian Brauner 	case 13:
9273ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("listmount failed");
9283ac7ea91SChristian Brauner 		break;
9293ac7ea91SChristian Brauner 	case 14:
9303ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("No mounts in new namespace");
9313ac7ea91SChristian Brauner 		break;
9323ac7ea91SChristian Brauner 	case 15:
9333ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("statmount_alloc failed");
9343ac7ea91SChristian Brauner 		break;
9353ac7ea91SChristian Brauner 	default:
9363ac7ea91SChristian Brauner 		ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)",
9373ac7ea91SChristian Brauner 					  WEXITSTATUS(status));
9383ac7ea91SChristian Brauner 		break;
9393ac7ea91SChristian Brauner 	}
9403ac7ea91SChristian Brauner }
9413ac7ea91SChristian Brauner 
9423ac7ea91SChristian Brauner FIXTURE(fsmount_ns_mount_attrs)
9433ac7ea91SChristian Brauner {
9443ac7ea91SChristian Brauner 	int fd;
9453ac7ea91SChristian Brauner 	int fs_fd;
9463ac7ea91SChristian Brauner };
9473ac7ea91SChristian Brauner 
9483ac7ea91SChristian Brauner FIXTURE_SETUP(fsmount_ns_mount_attrs)
9493ac7ea91SChristian Brauner {
9503ac7ea91SChristian Brauner 	int ret;
9513ac7ea91SChristian Brauner 
9523ac7ea91SChristian Brauner 	self->fd = -1;
9533ac7ea91SChristian Brauner 	self->fs_fd = -1;
9543ac7ea91SChristian Brauner 
9553ac7ea91SChristian Brauner 	/* Check if fsopen syscall is supported */
9563ac7ea91SChristian Brauner 	ret = sys_fsopen("tmpfs", 0);
9573ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
9583ac7ea91SChristian Brauner 		SKIP(return, "fsopen() syscall not supported");
9593ac7ea91SChristian Brauner 	if (ret >= 0)
9603ac7ea91SChristian Brauner 		close(ret);
9613ac7ea91SChristian Brauner 
9623ac7ea91SChristian Brauner 	/* Check if statmount/listmount are supported */
9633ac7ea91SChristian Brauner 	ret = statmount(0, 0, 0, 0, NULL, 0, 0);
9643ac7ea91SChristian Brauner 	if (ret == -1 && errno == ENOSYS)
9653ac7ea91SChristian Brauner 		SKIP(return, "statmount() syscall not supported");
9663ac7ea91SChristian Brauner }
9673ac7ea91SChristian Brauner 
9683ac7ea91SChristian Brauner FIXTURE_TEARDOWN(fsmount_ns_mount_attrs)
9693ac7ea91SChristian Brauner {
9703ac7ea91SChristian Brauner 	if (self->fd >= 0)
9713ac7ea91SChristian Brauner 		close(self->fd);
9723ac7ea91SChristian Brauner 	if (self->fs_fd >= 0)
9733ac7ea91SChristian Brauner 		close(self->fs_fd);
9743ac7ea91SChristian Brauner }
9753ac7ea91SChristian Brauner 
9763ac7ea91SChristian Brauner TEST_F(fsmount_ns_mount_attrs, readonly)
9773ac7ea91SChristian Brauner {
9783ac7ea91SChristian Brauner 	struct statmount sm;
9793ac7ea91SChristian Brauner 	uint64_t new_ns_id;
9803ac7ea91SChristian Brauner 	uint64_t list[256];
9813ac7ea91SChristian Brauner 	ssize_t nr_mounts;
9823ac7ea91SChristian Brauner 	int ret;
9833ac7ea91SChristian Brauner 
9843ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
9853ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
9863ac7ea91SChristian Brauner 
9873ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
9883ac7ea91SChristian Brauner 			       MOUNT_ATTR_RDONLY);
9893ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
9903ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
9913ac7ea91SChristian Brauner 
9923ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
9933ac7ea91SChristian Brauner 
9943ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
9953ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
9963ac7ea91SChristian Brauner 
9973ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
9983ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
9993ac7ea91SChristian Brauner 
10003ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
10013ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10023ac7ea91SChristian Brauner 
10033ac7ea91SChristian Brauner 	/* Verify the mount is read-only */
10043ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_RDONLY);
10053ac7ea91SChristian Brauner }
10063ac7ea91SChristian Brauner 
10073ac7ea91SChristian Brauner TEST_F(fsmount_ns_mount_attrs, noexec)
10083ac7ea91SChristian Brauner {
10093ac7ea91SChristian Brauner 	struct statmount sm;
10103ac7ea91SChristian Brauner 	uint64_t new_ns_id;
10113ac7ea91SChristian Brauner 	uint64_t list[256];
10123ac7ea91SChristian Brauner 	ssize_t nr_mounts;
10133ac7ea91SChristian Brauner 	int ret;
10143ac7ea91SChristian Brauner 
10153ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
10163ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
10173ac7ea91SChristian Brauner 
10183ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
10193ac7ea91SChristian Brauner 			       MOUNT_ATTR_NOEXEC);
10203ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
10213ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
10223ac7ea91SChristian Brauner 
10233ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
10243ac7ea91SChristian Brauner 
10253ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
10263ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10273ac7ea91SChristian Brauner 
10283ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
10293ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
10303ac7ea91SChristian Brauner 
10313ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
10323ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10333ac7ea91SChristian Brauner 
10343ac7ea91SChristian Brauner 	/* Verify the mount is noexec */
10353ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOEXEC);
10363ac7ea91SChristian Brauner }
10373ac7ea91SChristian Brauner 
10383ac7ea91SChristian Brauner TEST_F(fsmount_ns_mount_attrs, nosuid)
10393ac7ea91SChristian Brauner {
10403ac7ea91SChristian Brauner 	struct statmount sm;
10413ac7ea91SChristian Brauner 	uint64_t new_ns_id;
10423ac7ea91SChristian Brauner 	uint64_t list[256];
10433ac7ea91SChristian Brauner 	ssize_t nr_mounts;
10443ac7ea91SChristian Brauner 	int ret;
10453ac7ea91SChristian Brauner 
10463ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
10473ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
10483ac7ea91SChristian Brauner 
10493ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
10503ac7ea91SChristian Brauner 			       MOUNT_ATTR_NOSUID);
10513ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
10523ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
10533ac7ea91SChristian Brauner 
10543ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
10553ac7ea91SChristian Brauner 
10563ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
10573ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10583ac7ea91SChristian Brauner 
10593ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
10603ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
10613ac7ea91SChristian Brauner 
10623ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
10633ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10643ac7ea91SChristian Brauner 
10653ac7ea91SChristian Brauner 	/* Verify the mount is nosuid */
10663ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOSUID);
10673ac7ea91SChristian Brauner }
10683ac7ea91SChristian Brauner 
10693ac7ea91SChristian Brauner TEST_F(fsmount_ns_mount_attrs, noatime)
10703ac7ea91SChristian Brauner {
10713ac7ea91SChristian Brauner 	struct statmount sm;
10723ac7ea91SChristian Brauner 	uint64_t new_ns_id;
10733ac7ea91SChristian Brauner 	uint64_t list[256];
10743ac7ea91SChristian Brauner 	ssize_t nr_mounts;
10753ac7ea91SChristian Brauner 	int ret;
10763ac7ea91SChristian Brauner 
10773ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
10783ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
10793ac7ea91SChristian Brauner 
10803ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
10813ac7ea91SChristian Brauner 			       MOUNT_ATTR_NOATIME);
10823ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
10833ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
10843ac7ea91SChristian Brauner 
10853ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
10863ac7ea91SChristian Brauner 
10873ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
10883ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10893ac7ea91SChristian Brauner 
10903ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
10913ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
10923ac7ea91SChristian Brauner 
10933ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
10943ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
10953ac7ea91SChristian Brauner 
10963ac7ea91SChristian Brauner 	/* Verify the mount is noatime */
10973ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOATIME);
10983ac7ea91SChristian Brauner }
10993ac7ea91SChristian Brauner 
11003ac7ea91SChristian Brauner TEST_F(fsmount_ns_mount_attrs, combined)
11013ac7ea91SChristian Brauner {
11023ac7ea91SChristian Brauner 	struct statmount sm;
11033ac7ea91SChristian Brauner 	uint64_t new_ns_id;
11043ac7ea91SChristian Brauner 	uint64_t list[256];
11053ac7ea91SChristian Brauner 	ssize_t nr_mounts;
11063ac7ea91SChristian Brauner 	int ret;
11073ac7ea91SChristian Brauner 
11083ac7ea91SChristian Brauner 	self->fs_fd = create_tmpfs_fd();
11093ac7ea91SChristian Brauner 	ASSERT_GE(self->fs_fd, 0);
11103ac7ea91SChristian Brauner 
11113ac7ea91SChristian Brauner 	self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC,
11123ac7ea91SChristian Brauner 			       MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC |
11133ac7ea91SChristian Brauner 			       MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOATIME);
11143ac7ea91SChristian Brauner 	if (self->fd < 0 && errno == EINVAL)
11153ac7ea91SChristian Brauner 		SKIP(return, "FSMOUNT_NAMESPACE not supported");
11163ac7ea91SChristian Brauner 
11173ac7ea91SChristian Brauner 	ASSERT_GE(self->fd, 0);
11183ac7ea91SChristian Brauner 
11193ac7ea91SChristian Brauner 	ret = get_mnt_ns_id(self->fd, &new_ns_id);
11203ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
11213ac7ea91SChristian Brauner 
11223ac7ea91SChristian Brauner 	nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0);
11233ac7ea91SChristian Brauner 	ASSERT_GE(nr_mounts, 1);
11243ac7ea91SChristian Brauner 
11253ac7ea91SChristian Brauner 	ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0);
11263ac7ea91SChristian Brauner 	ASSERT_EQ(ret, 0);
11273ac7ea91SChristian Brauner 
11283ac7ea91SChristian Brauner 	/* Verify all attributes are set */
11293ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_RDONLY);
11303ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOEXEC);
11313ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOSUID);
11323ac7ea91SChristian Brauner 	ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOATIME);
11333ac7ea91SChristian Brauner }
11343ac7ea91SChristian Brauner 
11353ac7ea91SChristian Brauner TEST_HARNESS_MAIN
1136