xref: /linux/tools/testing/selftests/filesystems/nsfs/iterate_mntns.c (revision 0b3bb205808195159be633a8cefb602670e856fb)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 // Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
3 
4 #define _GNU_SOURCE
5 #include <fcntl.h>
6 #include <linux/auto_dev-ioctl.h>
7 #include <linux/errno.h>
8 #include <sched.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <sys/mount.h>
13 #include <unistd.h>
14 
15 #include "kselftest_harness.h"
16 
17 #define MNT_NS_COUNT 11
18 #define MNT_NS_LAST_INDEX 10
19 
20 struct mnt_ns_info {
21 	__u32 size;
22 	__u32 nr_mounts;
23 	__u64 mnt_ns_id;
24 };
25 
26 #define MNT_NS_INFO_SIZE_VER0 16 /* size of first published struct */
27 
28 /* Get information about namespace. */
29 #define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info)
30 /* Get next namespace. */
31 #define NS_MNT_GET_NEXT _IOR(0xb7, 11, struct mnt_ns_info)
32 /* Get previous namespace. */
33 #define NS_MNT_GET_PREV _IOR(0xb7, 12, struct mnt_ns_info)
34 
FIXTURE(iterate_mount_namespaces)35 FIXTURE(iterate_mount_namespaces) {
36 	int fd_mnt_ns[MNT_NS_COUNT];
37 	__u64 mnt_ns_id[MNT_NS_COUNT];
38 };
39 
mntns_in_list(__u64 * mnt_ns_id,struct mnt_ns_info * info)40 static inline bool mntns_in_list(__u64 *mnt_ns_id, struct mnt_ns_info *info)
41 {
42 	for (int i = 0; i < MNT_NS_COUNT; i++) {
43 		if (mnt_ns_id[i] == info->mnt_ns_id)
44 			return true;
45 	}
46 	return false;
47 }
48 
FIXTURE_SETUP(iterate_mount_namespaces)49 FIXTURE_SETUP(iterate_mount_namespaces)
50 {
51 	for (int i = 0; i < MNT_NS_COUNT; i++)
52 		self->fd_mnt_ns[i] = -EBADF;
53 
54 	for (int i = 0; i < MNT_NS_COUNT; i++) {
55 		struct mnt_ns_info info = {};
56 
57 		ASSERT_EQ(unshare(CLONE_NEWNS), 0);
58 		self->fd_mnt_ns[i] = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
59 		ASSERT_GE(self->fd_mnt_ns[i], 0);
60 		ASSERT_EQ(ioctl(self->fd_mnt_ns[i], NS_MNT_GET_INFO, &info), 0);
61 		self->mnt_ns_id[i] = info.mnt_ns_id;
62 	}
63 }
64 
FIXTURE_TEARDOWN(iterate_mount_namespaces)65 FIXTURE_TEARDOWN(iterate_mount_namespaces)
66 {
67 	for (int i = 0; i < MNT_NS_COUNT; i++) {
68 		if (self->fd_mnt_ns[i] < 0)
69 			continue;
70 		ASSERT_EQ(close(self->fd_mnt_ns[i]), 0);
71 	}
72 }
73 
TEST_F(iterate_mount_namespaces,iterate_all_forward)74 TEST_F(iterate_mount_namespaces, iterate_all_forward)
75 {
76 	int fd_mnt_ns_cur, count = 0;
77 
78 	fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[0], F_DUPFD_CLOEXEC);
79 	ASSERT_GE(fd_mnt_ns_cur, 0);
80 
81 	for (;;) {
82 		struct mnt_ns_info info = {};
83 		int fd_mnt_ns_next;
84 
85 		fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info);
86 		if (fd_mnt_ns_next < 0 && errno == ENOENT)
87 			break;
88 		if (mntns_in_list(self->mnt_ns_id, &info))
89 			count++;
90 		ASSERT_GE(fd_mnt_ns_next, 0);
91 		ASSERT_EQ(close(fd_mnt_ns_cur), 0);
92 		fd_mnt_ns_cur = fd_mnt_ns_next;
93 	}
94 	ASSERT_EQ(count, MNT_NS_LAST_INDEX);
95 }
96 
TEST_F(iterate_mount_namespaces,iterate_all_backwards)97 TEST_F(iterate_mount_namespaces, iterate_all_backwards)
98 {
99 	int fd_mnt_ns_cur, count = 0;
100 
101 	fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[MNT_NS_LAST_INDEX], F_DUPFD_CLOEXEC);
102 	ASSERT_GE(fd_mnt_ns_cur, 0);
103 
104 	for (;;) {
105 		struct mnt_ns_info info = {};
106 		int fd_mnt_ns_prev;
107 
108 		fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info);
109 		if (fd_mnt_ns_prev < 0 && errno == ENOENT)
110 			break;
111 		if (mntns_in_list(self->mnt_ns_id, &info))
112 			count++;
113 		ASSERT_GE(fd_mnt_ns_prev, 0);
114 		ASSERT_EQ(close(fd_mnt_ns_cur), 0);
115 		fd_mnt_ns_cur = fd_mnt_ns_prev;
116 	}
117 	ASSERT_EQ(count, MNT_NS_LAST_INDEX);
118 }
119 
TEST_F(iterate_mount_namespaces,iterate_forward)120 TEST_F(iterate_mount_namespaces, iterate_forward)
121 {
122 	int fd_mnt_ns_cur;
123 
124 	ASSERT_EQ(setns(self->fd_mnt_ns[0], CLONE_NEWNS), 0);
125 
126 	fd_mnt_ns_cur = self->fd_mnt_ns[0];
127 	for (int i = 1; i < MNT_NS_COUNT; i++) {
128 		struct mnt_ns_info info = {};
129 		int fd_mnt_ns_next;
130 
131 		fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info);
132 		ASSERT_GE(fd_mnt_ns_next, 0);
133 		ASSERT_EQ(close(fd_mnt_ns_cur), 0);
134 		fd_mnt_ns_cur = fd_mnt_ns_next;
135 	}
136 }
137 
TEST_F(iterate_mount_namespaces,iterate_backward)138 TEST_F(iterate_mount_namespaces, iterate_backward)
139 {
140 	int fd_mnt_ns_cur;
141 
142 	ASSERT_EQ(setns(self->fd_mnt_ns[MNT_NS_LAST_INDEX], CLONE_NEWNS), 0);
143 
144 	fd_mnt_ns_cur = self->fd_mnt_ns[MNT_NS_LAST_INDEX];
145 	for (int i = MNT_NS_LAST_INDEX - 1; i >= 0; i--) {
146 		struct mnt_ns_info info = {};
147 		int fd_mnt_ns_prev;
148 
149 		fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info);
150 		ASSERT_GE(fd_mnt_ns_prev, 0);
151 		ASSERT_EQ(close(fd_mnt_ns_cur), 0);
152 		fd_mnt_ns_cur = fd_mnt_ns_prev;
153 	}
154 }
155 
TEST_F(iterate_mount_namespaces,nfs_valid_ioctl)156 TEST_F(iterate_mount_namespaces, nfs_valid_ioctl)
157 {
158 	ASSERT_NE(ioctl(self->fd_mnt_ns[0], AUTOFS_DEV_IOCTL_OPENMOUNT, NULL), 0);
159 	ASSERT_EQ(errno, ENOTTY);
160 
161 	ASSERT_NE(ioctl(self->fd_mnt_ns[0], AUTOFS_DEV_IOCTL_CLOSEMOUNT, NULL), 0);
162 	ASSERT_EQ(errno, ENOTTY);
163 
164 	ASSERT_NE(ioctl(self->fd_mnt_ns[0], AUTOFS_DEV_IOCTL_READY, NULL), 0);
165 	ASSERT_EQ(errno, ENOTTY);
166 }
167 
168 TEST_HARNESS_MAIN
169