xref: /linux/samples/vfs/test-list-all-mounts.c (revision f9d94f78a8749e15de8aeb2e281898aa980e62d9)
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 
10 #include "../../tools/testing/selftests/pidfd/pidfd.h"
11 #include "samples-vfs.h"
12 
13 static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask,
14 		       struct statmount *stmnt, size_t bufsize,
15 		       unsigned int flags)
16 {
17 	struct mnt_id_req req = {
18 		.size		= MNT_ID_REQ_SIZE_VER1,
19 		.mnt_id		= mnt_id,
20 		.param		= mask,
21 		.mnt_ns_id	= mnt_ns_id,
22 	};
23 
24 	return syscall(__NR_statmount, &req, stmnt, bufsize, flags);
25 }
26 
27 static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id,
28 				       __u64 mask, unsigned int flags)
29 {
30 	size_t bufsize = 1 << 15;
31 	struct statmount *stmnt = NULL, *tmp = NULL;
32 	int ret;
33 
34 	for (;;) {
35 		tmp = realloc(stmnt, bufsize);
36 		if (!tmp)
37 			goto out;
38 
39 		stmnt = tmp;
40 		ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags);
41 		if (!ret)
42 			return stmnt;
43 
44 		if (errno != EOVERFLOW)
45 			goto out;
46 
47 		bufsize <<= 1;
48 		if (bufsize >= UINT_MAX / 2)
49 			goto out;
50 	}
51 
52 out:
53 	free(stmnt);
54 	return NULL;
55 }
56 
57 static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id,
58 			     __u64 list[], size_t num, unsigned int flags)
59 {
60 	struct mnt_id_req req = {
61 		.size		= MNT_ID_REQ_SIZE_VER1,
62 		.mnt_id		= mnt_id,
63 		.param		= last_mnt_id,
64 		.mnt_ns_id	= mnt_ns_id,
65 	};
66 
67 	return syscall(__NR_listmount, &req, list, num, flags);
68 }
69 
70 int main(int argc, char *argv[])
71 {
72 #define LISTMNT_BUFFER 10
73 	__u64 list[LISTMNT_BUFFER], last_mnt_id = 0;
74 	int ret, pidfd, fd_mntns;
75 	struct mnt_ns_info info = {};
76 
77 	pidfd = sys_pidfd_open(getpid(), 0);
78 	if (pidfd < 0)
79 		die_errno("pidfd_open failed");
80 
81 	fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0);
82 	if (fd_mntns < 0)
83 		die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed");
84 
85 	ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info);
86 	if (ret < 0)
87 		die_errno("ioctl(NS_GET_MNTNS_ID) failed");
88 
89 	printf("Listing %u mounts for mount namespace %llu\n",
90 	       info.nr_mounts, info.mnt_ns_id);
91 	for (;;) {
92 		ssize_t nr_mounts;
93 next:
94 		nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id,
95 					  info.mnt_ns_id, list, LISTMNT_BUFFER,
96 					  0);
97 		if (nr_mounts <= 0) {
98 			int fd_mntns_next;
99 
100 			printf("Finished listing %u mounts for mount namespace %llu\n\n",
101 			       info.nr_mounts, info.mnt_ns_id);
102 			fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info);
103 			if (fd_mntns_next < 0) {
104 				if (errno == ENOENT) {
105 					printf("Finished listing all mount namespaces\n");
106 					exit(0);
107 				}
108 				die_errno("ioctl(NS_MNT_GET_NEXT) failed");
109 			}
110 			close(fd_mntns);
111 			fd_mntns = fd_mntns_next;
112 			last_mnt_id = 0;
113 			printf("Listing %u mounts for mount namespace %llu\n",
114 			       info.nr_mounts, info.mnt_ns_id);
115 			goto next;
116 		}
117 
118 		for (size_t cur = 0; cur < nr_mounts; cur++) {
119 			struct statmount *stmnt;
120 
121 			last_mnt_id = list[cur];
122 
123 			stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id,
124 					      STATMOUNT_SB_BASIC |
125 					      STATMOUNT_MNT_BASIC |
126 					      STATMOUNT_MNT_ROOT |
127 					      STATMOUNT_MNT_POINT |
128 					      STATMOUNT_MNT_NS_ID |
129 					      STATMOUNT_MNT_OPTS |
130 					      STATMOUNT_FS_TYPE, 0);
131 			if (!stmnt) {
132 				printf("Failed to statmount(%llu) in mount namespace(%llu)\n",
133 				       last_mnt_id, info.mnt_ns_id);
134 				continue;
135 			}
136 
137 			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",
138 			       stmnt->mnt_id,
139 			       stmnt->mnt_parent_id,
140 			       stmnt->str + stmnt->fs_type,
141 			       stmnt->str + stmnt->mnt_root,
142 			       stmnt->str + stmnt->mnt_point,
143 			       stmnt->str + stmnt->mnt_opts);
144 			free(stmnt);
145 		}
146 	}
147 
148 	exit(0);
149 }
150