xref: /linux/samples/vfs/test-list-all-mounts.c (revision 75d0dd101fbf0173f046bf55a8d583aa8d6a1ce5)
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 <errno.h>
6 #include <limits.h>
7 #include <linux/types.h>
8 #include <stdio.h>
9 #include <sys/ioctl.h>
10 #include <sys/syscall.h>
11 
12 #include "../../tools/testing/selftests/pidfd/pidfd.h"
13 
14 #define die_errno(format, ...)                                             \
15 	do {                                                               \
16 		fprintf(stderr, "%m | %s: %d: %s: " format "\n", __FILE__, \
17 			__LINE__, __func__, ##__VA_ARGS__);                \
18 		exit(EXIT_FAILURE);                                        \
19 	} while (0)
20 
21 /* Get the id for a mount namespace */
22 #define NS_GET_MNTNS_ID _IO(0xb7, 0x5)
23 /* Get next mount namespace. */
24 
25 struct mnt_ns_info {
26 	__u32 size;
27 	__u32 nr_mounts;
28 	__u64 mnt_ns_id;
29 };
30 
31 #define MNT_NS_INFO_SIZE_VER0 16 /* size of first published struct */
32 
33 /* Get information about namespace. */
34 #define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info)
35 /* Get next namespace. */
36 #define NS_MNT_GET_NEXT _IOR(0xb7, 11, struct mnt_ns_info)
37 /* Get previous namespace. */
38 #define NS_MNT_GET_PREV _IOR(0xb7, 12, struct mnt_ns_info)
39 
40 #define PIDFD_GET_MNT_NAMESPACE _IO(0xFF, 3)
41 
42 #ifndef __NR_listmount
43 #define __NR_listmount 458
44 #endif
45 
46 #ifndef __NR_statmount
47 #define __NR_statmount 457
48 #endif
49 
50 /* @mask bits for statmount(2) */
51 #define STATMOUNT_SB_BASIC		0x00000001U /* Want/got sb_... */
52 #define STATMOUNT_MNT_BASIC		0x00000002U /* Want/got mnt_... */
53 #define STATMOUNT_PROPAGATE_FROM	0x00000004U /* Want/got propagate_from */
54 #define STATMOUNT_MNT_ROOT		0x00000008U /* Want/got mnt_root  */
55 #define STATMOUNT_MNT_POINT		0x00000010U /* Want/got mnt_point */
56 #define STATMOUNT_FS_TYPE		0x00000020U /* Want/got fs_type */
57 #define STATMOUNT_MNT_NS_ID		0x00000040U /* Want/got mnt_ns_id */
58 #define STATMOUNT_MNT_OPTS		0x00000080U /* Want/got mnt_opts */
59 
60 #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */
61 
62 struct statmount {
63 	__u32 size;
64 	__u32 mnt_opts;
65 	__u64 mask;
66 	__u32 sb_dev_major;
67 	__u32 sb_dev_minor;
68 	__u64 sb_magic;
69 	__u32 sb_flags;
70 	__u32 fs_type;
71 	__u64 mnt_id;
72 	__u64 mnt_parent_id;
73 	__u32 mnt_id_old;
74 	__u32 mnt_parent_id_old;
75 	__u64 mnt_attr;
76 	__u64 mnt_propagation;
77 	__u64 mnt_peer_group;
78 	__u64 mnt_master;
79 	__u64 propagate_from;
80 	__u32 mnt_root;
81 	__u32 mnt_point;
82 	__u64 mnt_ns_id;
83 	__u64 __spare2[49];
84 	char str[];
85 };
86 
87 struct mnt_id_req {
88 	__u32 size;
89 	__u32 spare;
90 	__u64 mnt_id;
91 	__u64 param;
92 	__u64 mnt_ns_id;
93 };
94 
95 #define MNT_ID_REQ_SIZE_VER1 32 /* sizeof second published struct */
96 
97 #define LSMT_ROOT 0xffffffffffffffff /* root mount */
98 
99 static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask,
100 		       struct statmount *stmnt, size_t bufsize,
101 		       unsigned int flags)
102 {
103 	struct mnt_id_req req = {
104 		.size		= MNT_ID_REQ_SIZE_VER1,
105 		.mnt_id		= mnt_id,
106 		.param		= mask,
107 		.mnt_ns_id	= mnt_ns_id,
108 	};
109 
110 	return syscall(__NR_statmount, &req, stmnt, bufsize, flags);
111 }
112 
113 static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id,
114 				       __u64 mask, unsigned int flags)
115 {
116 	size_t bufsize = 1 << 15;
117 	struct statmount *stmnt = NULL, *tmp = NULL;
118 	int ret;
119 
120 	for (;;) {
121 		tmp = realloc(stmnt, bufsize);
122 		if (!tmp)
123 			goto out;
124 
125 		stmnt = tmp;
126 		ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags);
127 		if (!ret)
128 			return stmnt;
129 
130 		if (errno != EOVERFLOW)
131 			goto out;
132 
133 		bufsize <<= 1;
134 		if (bufsize >= UINT_MAX / 2)
135 			goto out;
136 	}
137 
138 out:
139 	free(stmnt);
140 	return NULL;
141 }
142 
143 static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id,
144 			     __u64 list[], size_t num, unsigned int flags)
145 {
146 	struct mnt_id_req req = {
147 		.size		= MNT_ID_REQ_SIZE_VER1,
148 		.mnt_id		= mnt_id,
149 		.param		= last_mnt_id,
150 		.mnt_ns_id	= mnt_ns_id,
151 	};
152 
153 	return syscall(__NR_listmount, &req, list, num, flags);
154 }
155 
156 int main(int argc, char *argv[])
157 {
158 #define LISTMNT_BUFFER 10
159 	__u64 list[LISTMNT_BUFFER], last_mnt_id = 0;
160 	int ret, pidfd, fd_mntns;
161 	struct mnt_ns_info info = {};
162 
163 	pidfd = sys_pidfd_open(getpid(), 0);
164 	if (pidfd < 0)
165 		die_errno("pidfd_open failed");
166 
167 	fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0);
168 	if (fd_mntns < 0)
169 		die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed");
170 
171 	ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info);
172 	if (ret < 0)
173 		die_errno("ioctl(NS_GET_MNTNS_ID) failed");
174 
175 	printf("Listing %u mounts for mount namespace %llu\n",
176 	       info.nr_mounts, info.mnt_ns_id);
177 	for (;;) {
178 		ssize_t nr_mounts;
179 next:
180 		nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id,
181 					  info.mnt_ns_id, list, LISTMNT_BUFFER,
182 					  0);
183 		if (nr_mounts <= 0) {
184 			int fd_mntns_next;
185 
186 			printf("Finished listing %u mounts for mount namespace %llu\n\n",
187 			       info.nr_mounts, info.mnt_ns_id);
188 			fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info);
189 			if (fd_mntns_next < 0) {
190 				if (errno == ENOENT) {
191 					printf("Finished listing all mount namespaces\n");
192 					exit(0);
193 				}
194 				die_errno("ioctl(NS_MNT_GET_NEXT) failed");
195 			}
196 			close(fd_mntns);
197 			fd_mntns = fd_mntns_next;
198 			last_mnt_id = 0;
199 			printf("Listing %u mounts for mount namespace %llu\n",
200 			       info.nr_mounts, info.mnt_ns_id);
201 			goto next;
202 		}
203 
204 		for (size_t cur = 0; cur < nr_mounts; cur++) {
205 			struct statmount *stmnt;
206 
207 			last_mnt_id = list[cur];
208 
209 			stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id,
210 					      STATMOUNT_SB_BASIC |
211 					      STATMOUNT_MNT_BASIC |
212 					      STATMOUNT_MNT_ROOT |
213 					      STATMOUNT_MNT_POINT |
214 					      STATMOUNT_MNT_NS_ID |
215 					      STATMOUNT_MNT_OPTS |
216 					      STATMOUNT_FS_TYPE, 0);
217 			if (!stmnt) {
218 				printf("Failed to statmount(%llu) in mount namespace(%llu)\n",
219 				       last_mnt_id, info.mnt_ns_id);
220 				continue;
221 			}
222 
223 			printf("mnt_id:\t\t%llu\nmnt_parent_id:\t%llu\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n\n",
224 			       stmnt->mnt_id,
225 			       stmnt->mnt_parent_id,
226 			       stmnt->str + stmnt->fs_type,
227 			       stmnt->str + stmnt->mnt_root,
228 			       stmnt->str + stmnt->mnt_point,
229 			       stmnt->str + stmnt->mnt_opts);
230 			free(stmnt);
231 		}
232 	}
233 
234 	exit(0);
235 }
236