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