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 */ 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 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 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 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 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 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 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 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 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