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