1 // SPDX-License-Identifier: GPL-2.0-or-later 2 // Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 3 4 #define _GNU_SOURCE 5 #include <errno.h> 6 #include <limits.h> 7 #include <linux/types.h> 8 #include <stdio.h> 9 10 #include "../../tools/testing/selftests/pidfd/pidfd.h" 11 #include "samples-vfs.h" 12 13 static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask, 14 struct statmount *stmnt, size_t bufsize, 15 unsigned int flags) 16 { 17 struct mnt_id_req req = { 18 .size = MNT_ID_REQ_SIZE_VER1, 19 .mnt_id = mnt_id, 20 .param = mask, 21 .mnt_ns_id = mnt_ns_id, 22 }; 23 24 return syscall(__NR_statmount, &req, stmnt, bufsize, flags); 25 } 26 27 static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id, 28 __u64 mask, unsigned int flags) 29 { 30 size_t bufsize = 1 << 15; 31 struct statmount *stmnt = NULL, *tmp = NULL; 32 int ret; 33 34 for (;;) { 35 tmp = realloc(stmnt, bufsize); 36 if (!tmp) 37 goto out; 38 39 stmnt = tmp; 40 ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags); 41 if (!ret) 42 return stmnt; 43 44 if (errno != EOVERFLOW) 45 goto out; 46 47 bufsize <<= 1; 48 if (bufsize >= UINT_MAX / 2) 49 goto out; 50 } 51 52 out: 53 free(stmnt); 54 return NULL; 55 } 56 57 static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id, 58 __u64 list[], size_t num, unsigned int flags) 59 { 60 struct mnt_id_req req = { 61 .size = MNT_ID_REQ_SIZE_VER1, 62 .mnt_id = mnt_id, 63 .param = last_mnt_id, 64 .mnt_ns_id = mnt_ns_id, 65 }; 66 67 return syscall(__NR_listmount, &req, list, num, flags); 68 } 69 70 int main(int argc, char *argv[]) 71 { 72 #define LISTMNT_BUFFER 10 73 __u64 list[LISTMNT_BUFFER], last_mnt_id = 0; 74 int ret, pidfd, fd_mntns; 75 struct mnt_ns_info info = {}; 76 77 pidfd = sys_pidfd_open(getpid(), 0); 78 if (pidfd < 0) 79 die_errno("pidfd_open failed"); 80 81 fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0); 82 if (fd_mntns < 0) 83 die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed"); 84 85 ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info); 86 if (ret < 0) 87 die_errno("ioctl(NS_GET_MNTNS_ID) failed"); 88 89 printf("Listing %u mounts for mount namespace %llu\n", 90 info.nr_mounts, info.mnt_ns_id); 91 for (;;) { 92 ssize_t nr_mounts; 93 next: 94 nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id, 95 info.mnt_ns_id, list, LISTMNT_BUFFER, 96 0); 97 if (nr_mounts <= 0) { 98 int fd_mntns_next; 99 100 printf("Finished listing %u mounts for mount namespace %llu\n\n", 101 info.nr_mounts, info.mnt_ns_id); 102 fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info); 103 if (fd_mntns_next < 0) { 104 if (errno == ENOENT) { 105 printf("Finished listing all mount namespaces\n"); 106 exit(0); 107 } 108 die_errno("ioctl(NS_MNT_GET_NEXT) failed"); 109 } 110 close(fd_mntns); 111 fd_mntns = fd_mntns_next; 112 last_mnt_id = 0; 113 printf("Listing %u mounts for mount namespace %llu\n", 114 info.nr_mounts, info.mnt_ns_id); 115 goto next; 116 } 117 118 for (size_t cur = 0; cur < nr_mounts; cur++) { 119 struct statmount *stmnt; 120 121 last_mnt_id = list[cur]; 122 123 stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id, 124 STATMOUNT_SB_BASIC | 125 STATMOUNT_MNT_BASIC | 126 STATMOUNT_MNT_ROOT | 127 STATMOUNT_MNT_POINT | 128 STATMOUNT_MNT_NS_ID | 129 STATMOUNT_MNT_OPTS | 130 STATMOUNT_FS_TYPE, 0); 131 if (!stmnt) { 132 printf("Failed to statmount(%llu) in mount namespace(%llu)\n", 133 last_mnt_id, info.mnt_ns_id); 134 continue; 135 } 136 137 printf("mnt_id:\t\t%llu\nmnt_parent_id:\t%llu\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n\n", 138 stmnt->mnt_id, 139 stmnt->mnt_parent_id, 140 stmnt->str + stmnt->fs_type, 141 stmnt->str + stmnt->mnt_root, 142 stmnt->str + stmnt->mnt_point, 143 stmnt->str + stmnt->mnt_opts); 144 free(stmnt); 145 } 146 } 147 148 exit(0); 149 } 150