xref: /linux/samples/vfs/mountinfo.c (revision f79e6eb84d4d2bff99e3ca6c1f140b2af827e904)
1c6640d46SJeff Layton // SPDX-License-Identifier: GPL-2.0-or-later
2c6640d46SJeff Layton 
3c6640d46SJeff Layton /*
4c6640d46SJeff Layton  * Use pidfds, nsfds, listmount() and statmount() mimic the
5c6640d46SJeff Layton  * contents of /proc/self/mountinfo.
6c6640d46SJeff Layton  */
7c6640d46SJeff Layton #define _GNU_SOURCE
8c6640d46SJeff Layton #define __SANE_USERSPACE_TYPES__
9c6640d46SJeff Layton #include <stdio.h>
10c6640d46SJeff Layton #include <stdint.h>
11c6640d46SJeff Layton #include <sys/ioctl.h>
12c6640d46SJeff Layton #include <sys/syscall.h>
13c6640d46SJeff Layton #include <linux/pidfd.h>
14c6640d46SJeff Layton #include <linux/mount.h>
15c6640d46SJeff Layton #include <linux/nsfs.h>
16c6640d46SJeff Layton #include <unistd.h>
17c6640d46SJeff Layton #include <alloca.h>
18c6640d46SJeff Layton #include <getopt.h>
19c6640d46SJeff Layton #include <stdlib.h>
20c6640d46SJeff Layton #include <stdbool.h>
21c6640d46SJeff Layton #include <errno.h>
22c6640d46SJeff Layton 
23c6640d46SJeff Layton /* max mounts per listmount call */
24c6640d46SJeff Layton #define MAXMOUNTS		1024
25c6640d46SJeff Layton 
26c6640d46SJeff Layton /* size of struct statmount (including trailing string buffer) */
27c6640d46SJeff Layton #define STATMOUNT_BUFSIZE	4096
28c6640d46SJeff Layton 
29c6640d46SJeff Layton static bool ext_format;
30c6640d46SJeff Layton 
31c6640d46SJeff Layton /*
32c6640d46SJeff Layton  * There are no bindings in glibc for listmount() and statmount() (yet),
33c6640d46SJeff Layton  * make our own here.
34c6640d46SJeff Layton  */
35*f79e6eb8SGeert Uytterhoeven static int statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask,
36c6640d46SJeff Layton 		     struct statmount *buf, size_t bufsize,
37c6640d46SJeff Layton 		     unsigned int flags)
38c6640d46SJeff Layton {
39c6640d46SJeff Layton 	struct mnt_id_req req = {
40c6640d46SJeff Layton 		.size = MNT_ID_REQ_SIZE_VER0,
41c6640d46SJeff Layton 		.mnt_id = mnt_id,
42c6640d46SJeff Layton 		.param = mask,
43c6640d46SJeff Layton 	};
44c6640d46SJeff Layton 
45c6640d46SJeff Layton 	if (mnt_ns_id) {
46c6640d46SJeff Layton 		req.size = MNT_ID_REQ_SIZE_VER1;
47c6640d46SJeff Layton 		req.mnt_ns_id = mnt_ns_id;
48c6640d46SJeff Layton 	}
49c6640d46SJeff Layton 
50c6640d46SJeff Layton 	return syscall(__NR_statmount, &req, buf, bufsize, flags);
51c6640d46SJeff Layton }
52c6640d46SJeff Layton 
53*f79e6eb8SGeert Uytterhoeven static ssize_t listmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 last_mnt_id,
54*f79e6eb8SGeert Uytterhoeven 			 __u64 list[], size_t num, unsigned int flags)
55c6640d46SJeff Layton {
56c6640d46SJeff Layton 	struct mnt_id_req req = {
57c6640d46SJeff Layton 		.size = MNT_ID_REQ_SIZE_VER0,
58c6640d46SJeff Layton 		.mnt_id = mnt_id,
59c6640d46SJeff Layton 		.param = last_mnt_id,
60c6640d46SJeff Layton 	};
61c6640d46SJeff Layton 
62c6640d46SJeff Layton 	if (mnt_ns_id) {
63c6640d46SJeff Layton 		req.size = MNT_ID_REQ_SIZE_VER1;
64c6640d46SJeff Layton 		req.mnt_ns_id = mnt_ns_id;
65c6640d46SJeff Layton 	}
66c6640d46SJeff Layton 
67c6640d46SJeff Layton 	return syscall(__NR_listmount, &req, list, num, flags);
68c6640d46SJeff Layton }
69c6640d46SJeff Layton 
70*f79e6eb8SGeert Uytterhoeven static void show_mnt_attrs(__u64 flags)
71c6640d46SJeff Layton {
72c6640d46SJeff Layton 	printf("%s", flags & MOUNT_ATTR_RDONLY ? "ro" : "rw");
73c6640d46SJeff Layton 
74c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_NOSUID)
75c6640d46SJeff Layton 		printf(",nosuid");
76c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_NODEV)
77c6640d46SJeff Layton 		printf(",nodev");
78c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_NOEXEC)
79c6640d46SJeff Layton 		printf(",noexec");
80c6640d46SJeff Layton 
81c6640d46SJeff Layton 	switch (flags & MOUNT_ATTR__ATIME) {
82c6640d46SJeff Layton 	case MOUNT_ATTR_RELATIME:
83c6640d46SJeff Layton 		printf(",relatime");
84c6640d46SJeff Layton 		break;
85c6640d46SJeff Layton 	case MOUNT_ATTR_NOATIME:
86c6640d46SJeff Layton 		printf(",noatime");
87c6640d46SJeff Layton 		break;
88c6640d46SJeff Layton 	case MOUNT_ATTR_STRICTATIME:
89c6640d46SJeff Layton 		/* print nothing */
90c6640d46SJeff Layton 		break;
91c6640d46SJeff Layton 	}
92c6640d46SJeff Layton 
93c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_NODIRATIME)
94c6640d46SJeff Layton 		printf(",nodiratime");
95c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_NOSYMFOLLOW)
96c6640d46SJeff Layton 		printf(",nosymfollow");
97c6640d46SJeff Layton 	if (flags & MOUNT_ATTR_IDMAP)
98c6640d46SJeff Layton 		printf(",idmapped");
99c6640d46SJeff Layton }
100c6640d46SJeff Layton 
101c6640d46SJeff Layton static void show_propagation(struct statmount *sm)
102c6640d46SJeff Layton {
103c6640d46SJeff Layton 	if (sm->mnt_propagation & MS_SHARED)
104c6640d46SJeff Layton 		printf(" shared:%llu", sm->mnt_peer_group);
105c6640d46SJeff Layton 	if (sm->mnt_propagation & MS_SLAVE) {
106c6640d46SJeff Layton 		printf(" master:%llu", sm->mnt_master);
107c6640d46SJeff Layton 		if (sm->propagate_from && sm->propagate_from != sm->mnt_master)
108c6640d46SJeff Layton 			printf(" propagate_from:%llu", sm->propagate_from);
109c6640d46SJeff Layton 	}
110c6640d46SJeff Layton 	if (sm->mnt_propagation & MS_UNBINDABLE)
111c6640d46SJeff Layton 		printf(" unbindable");
112c6640d46SJeff Layton }
113c6640d46SJeff Layton 
114*f79e6eb8SGeert Uytterhoeven static void show_sb_flags(__u64 flags)
115c6640d46SJeff Layton {
116c6640d46SJeff Layton 	printf("%s", flags & MS_RDONLY ? "ro" : "rw");
117c6640d46SJeff Layton 	if (flags & MS_SYNCHRONOUS)
118c6640d46SJeff Layton 		printf(",sync");
119c6640d46SJeff Layton 	if (flags & MS_DIRSYNC)
120c6640d46SJeff Layton 		printf(",dirsync");
121c6640d46SJeff Layton 	if (flags & MS_MANDLOCK)
122c6640d46SJeff Layton 		printf(",mand");
123c6640d46SJeff Layton 	if (flags & MS_LAZYTIME)
124c6640d46SJeff Layton 		printf(",lazytime");
125c6640d46SJeff Layton }
126c6640d46SJeff Layton 
127*f79e6eb8SGeert Uytterhoeven static int dump_mountinfo(__u64 mnt_id, __u64 mnt_ns_id)
128c6640d46SJeff Layton {
129c6640d46SJeff Layton 	int ret;
130c6640d46SJeff Layton 	struct statmount *buf = alloca(STATMOUNT_BUFSIZE);
131*f79e6eb8SGeert Uytterhoeven 	const __u64 mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
132c6640d46SJeff Layton 			   STATMOUNT_PROPAGATE_FROM | STATMOUNT_FS_TYPE |
133c6640d46SJeff Layton 			   STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT |
134c6640d46SJeff Layton 			   STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE |
135c6640d46SJeff Layton 			   STATMOUNT_SB_SOURCE;
136c6640d46SJeff Layton 
137c6640d46SJeff Layton 	ret = statmount(mnt_id, mnt_ns_id, mask, buf, STATMOUNT_BUFSIZE, 0);
138c6640d46SJeff Layton 	if (ret < 0) {
139c6640d46SJeff Layton 		perror("statmount");
140c6640d46SJeff Layton 		return 1;
141c6640d46SJeff Layton 	}
142c6640d46SJeff Layton 
143c6640d46SJeff Layton 	if (ext_format)
144*f79e6eb8SGeert Uytterhoeven 		printf("0x%llx 0x%llx 0x%llx ", mnt_ns_id, mnt_id, buf->mnt_parent_id);
145c6640d46SJeff Layton 
146c6640d46SJeff Layton 	printf("%u %u %u:%u %s %s ", buf->mnt_id_old, buf->mnt_parent_id_old,
147c6640d46SJeff Layton 				   buf->sb_dev_major, buf->sb_dev_minor,
148c6640d46SJeff Layton 				   &buf->str[buf->mnt_root],
149c6640d46SJeff Layton 				   &buf->str[buf->mnt_point]);
150c6640d46SJeff Layton 	show_mnt_attrs(buf->mnt_attr);
151c6640d46SJeff Layton 	show_propagation(buf);
152c6640d46SJeff Layton 
153c6640d46SJeff Layton 	printf(" - %s", &buf->str[buf->fs_type]);
154c6640d46SJeff Layton 	if (buf->mask & STATMOUNT_FS_SUBTYPE)
155c6640d46SJeff Layton 		printf(".%s", &buf->str[buf->fs_subtype]);
156c6640d46SJeff Layton 	if (buf->mask & STATMOUNT_SB_SOURCE)
157c6640d46SJeff Layton 		printf(" %s ", &buf->str[buf->sb_source]);
158c6640d46SJeff Layton 	else
159c6640d46SJeff Layton 		printf(" :none ");
160c6640d46SJeff Layton 
161c6640d46SJeff Layton 	show_sb_flags(buf->sb_flags);
162c6640d46SJeff Layton 	if (buf->mask & STATMOUNT_MNT_OPTS)
163c6640d46SJeff Layton 		printf(",%s", &buf->str[buf->mnt_opts]);
164c6640d46SJeff Layton 	printf("\n");
165c6640d46SJeff Layton 	return 0;
166c6640d46SJeff Layton }
167c6640d46SJeff Layton 
168*f79e6eb8SGeert Uytterhoeven static int dump_mounts(__u64 mnt_ns_id)
169c6640d46SJeff Layton {
170*f79e6eb8SGeert Uytterhoeven 	__u64 mntid[MAXMOUNTS];
171*f79e6eb8SGeert Uytterhoeven 	__u64 last_mnt_id = 0;
172c6640d46SJeff Layton 	ssize_t count;
173c6640d46SJeff Layton 	int i;
174c6640d46SJeff Layton 
175c6640d46SJeff Layton 	/*
176c6640d46SJeff Layton 	 * Get a list of all mntids in mnt_ns_id. If it returns MAXMOUNTS
177c6640d46SJeff Layton 	 * mounts, then go again until we get everything.
178c6640d46SJeff Layton 	 */
179c6640d46SJeff Layton 	do {
180c6640d46SJeff Layton 		count = listmount(LSMT_ROOT, mnt_ns_id, last_mnt_id, mntid, MAXMOUNTS, 0);
181c6640d46SJeff Layton 		if (count < 0 || count > MAXMOUNTS) {
182c6640d46SJeff Layton 			errno = count < 0 ? errno : count;
183c6640d46SJeff Layton 			perror("listmount");
184c6640d46SJeff Layton 			return 1;
185c6640d46SJeff Layton 		}
186c6640d46SJeff Layton 
187c6640d46SJeff Layton 		/* Walk the returned mntids and print info about each */
188c6640d46SJeff Layton 		for (i = 0; i < count; ++i) {
189c6640d46SJeff Layton 			int ret = dump_mountinfo(mntid[i], mnt_ns_id);
190c6640d46SJeff Layton 
191c6640d46SJeff Layton 			if (ret != 0)
192c6640d46SJeff Layton 				return ret;
193c6640d46SJeff Layton 		}
194c6640d46SJeff Layton 		/* Set up last_mnt_id to pick up where we left off */
195c6640d46SJeff Layton 		last_mnt_id = mntid[count - 1];
196c6640d46SJeff Layton 	} while (count == MAXMOUNTS);
197c6640d46SJeff Layton 	return 0;
198c6640d46SJeff Layton }
199c6640d46SJeff Layton 
200c6640d46SJeff Layton static void usage(const char * const prog)
201c6640d46SJeff Layton {
202c6640d46SJeff Layton 	printf("Usage:\n");
203c6640d46SJeff Layton 	printf("%s [-e] [-p pid] [-r] [-h]\n", prog);
204c6640d46SJeff Layton 	printf("    -e: extended format\n");
205c6640d46SJeff Layton 	printf("    -h: print usage message\n");
206c6640d46SJeff Layton 	printf("    -p: get mount namespace from given pid\n");
207c6640d46SJeff Layton 	printf("    -r: recursively print all mounts in all child namespaces\n");
208c6640d46SJeff Layton }
209c6640d46SJeff Layton 
210c6640d46SJeff Layton int main(int argc, char * const *argv)
211c6640d46SJeff Layton {
212c6640d46SJeff Layton 	struct mnt_ns_info mni = { .size = MNT_NS_INFO_SIZE_VER0 };
213c6640d46SJeff Layton 	int pidfd, mntns, ret, opt;
214c6640d46SJeff Layton 	pid_t pid = getpid();
215c6640d46SJeff Layton 	bool recursive = false;
216c6640d46SJeff Layton 
217c6640d46SJeff Layton 	while ((opt = getopt(argc, argv, "ehp:r")) != -1) {
218c6640d46SJeff Layton 		switch (opt) {
219c6640d46SJeff Layton 		case 'e':
220c6640d46SJeff Layton 			ext_format = true;
221c6640d46SJeff Layton 			break;
222c6640d46SJeff Layton 		case 'h':
223c6640d46SJeff Layton 			usage(argv[0]);
224c6640d46SJeff Layton 			return 0;
225c6640d46SJeff Layton 		case 'p':
226c6640d46SJeff Layton 			pid = atoi(optarg);
227c6640d46SJeff Layton 			break;
228c6640d46SJeff Layton 		case 'r':
229c6640d46SJeff Layton 			recursive = true;
230c6640d46SJeff Layton 			break;
231c6640d46SJeff Layton 		}
232c6640d46SJeff Layton 	}
233c6640d46SJeff Layton 
234c6640d46SJeff Layton 	/* Get a pidfd for pid */
235c6640d46SJeff Layton 	pidfd = syscall(SYS_pidfd_open, pid, 0);
236c6640d46SJeff Layton 	if (pidfd < 0) {
237c6640d46SJeff Layton 		perror("pidfd_open");
238c6640d46SJeff Layton 		return 1;
239c6640d46SJeff Layton 	}
240c6640d46SJeff Layton 
241c6640d46SJeff Layton 	/* Get the mnt namespace for pidfd */
242c6640d46SJeff Layton 	mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, NULL);
243c6640d46SJeff Layton 	if (mntns < 0) {
244c6640d46SJeff Layton 		perror("PIDFD_GET_MNT_NAMESPACE");
245c6640d46SJeff Layton 		return 1;
246c6640d46SJeff Layton 	}
247c6640d46SJeff Layton 	close(pidfd);
248c6640d46SJeff Layton 
249c6640d46SJeff Layton 	/* get info about mntns. In particular, the mnt_ns_id */
250c6640d46SJeff Layton 	ret = ioctl(mntns, NS_MNT_GET_INFO, &mni);
251c6640d46SJeff Layton 	if (ret < 0) {
252c6640d46SJeff Layton 		perror("NS_MNT_GET_INFO");
253c6640d46SJeff Layton 		return 1;
254c6640d46SJeff Layton 	}
255c6640d46SJeff Layton 
256c6640d46SJeff Layton 	do {
257c6640d46SJeff Layton 		int ret;
258c6640d46SJeff Layton 
259c6640d46SJeff Layton 		ret = dump_mounts(mni.mnt_ns_id);
260c6640d46SJeff Layton 		if (ret)
261c6640d46SJeff Layton 			return ret;
262c6640d46SJeff Layton 
263c6640d46SJeff Layton 		if (!recursive)
264c6640d46SJeff Layton 			break;
265c6640d46SJeff Layton 
266c6640d46SJeff Layton 		/* get the next mntns (and overwrite the old mount ns info) */
267c6640d46SJeff Layton 		ret = ioctl(mntns, NS_MNT_GET_NEXT, &mni);
268c6640d46SJeff Layton 		close(mntns);
269c6640d46SJeff Layton 		mntns = ret;
270c6640d46SJeff Layton 	} while (mntns >= 0);
271c6640d46SJeff Layton 
272c6640d46SJeff Layton 	return 0;
273c6640d46SJeff Layton }
274