File Index
Files and directories
Sign in
FILE

mountinfo.c

File preview with syntax highlighting.

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