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