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