Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

samples: add test-list-all-mounts

Add a sample program illustrating how to list all mounts in all mount
namespaces.

Link: https://lore.kernel.org/r/20241213-work-mount-rbtree-lockless-v3-10-6e3cdaf9b280@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>

+237 -1
+1
samples/vfs/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 /test-fsmount 3 + /test-list-all-mounts 3 4 /test-statx 4 5 /mountinfo
+1 -1
samples/vfs/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 - userprogs-always-y += test-fsmount test-statx mountinfo 2 + userprogs-always-y += test-fsmount test-statx mountinfo test-list-all-mounts 3 3 4 4 userccflags += -I usr/include
+235
samples/vfs/test-list-all-mounts.c
··· 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 + #include <sys/ioctl.h> 10 + #include <sys/syscall.h> 11 + 12 + #include "../../tools/testing/selftests/pidfd/pidfd.h" 13 + 14 + #define die_errno(format, ...) \ 15 + do { \ 16 + fprintf(stderr, "%m | %s: %d: %s: " format "\n", __FILE__, \ 17 + __LINE__, __func__, ##__VA_ARGS__); \ 18 + exit(EXIT_FAILURE); \ 19 + } while (0) 20 + 21 + /* Get the id for a mount namespace */ 22 + #define NS_GET_MNTNS_ID _IO(0xb7, 0x5) 23 + /* Get next mount namespace. */ 24 + 25 + struct mnt_ns_info { 26 + __u32 size; 27 + __u32 nr_mounts; 28 + __u64 mnt_ns_id; 29 + }; 30 + 31 + #define MNT_NS_INFO_SIZE_VER0 16 /* size of first published struct */ 32 + 33 + /* Get information about namespace. */ 34 + #define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info) 35 + /* Get next namespace. */ 36 + #define NS_MNT_GET_NEXT _IOR(0xb7, 11, struct mnt_ns_info) 37 + /* Get previous namespace. */ 38 + #define NS_MNT_GET_PREV _IOR(0xb7, 12, struct mnt_ns_info) 39 + 40 + #define PIDFD_GET_MNT_NAMESPACE _IO(0xFF, 3) 41 + 42 + #ifndef __NR_listmount 43 + #define __NR_listmount 458 44 + #endif 45 + 46 + #ifndef __NR_statmount 47 + #define __NR_statmount 457 48 + #endif 49 + 50 + /* @mask bits for statmount(2) */ 51 + #define STATMOUNT_SB_BASIC 0x00000001U /* Want/got sb_... */ 52 + #define STATMOUNT_MNT_BASIC 0x00000002U /* Want/got mnt_... */ 53 + #define STATMOUNT_PROPAGATE_FROM 0x00000004U /* Want/got propagate_from */ 54 + #define STATMOUNT_MNT_ROOT 0x00000008U /* Want/got mnt_root */ 55 + #define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */ 56 + #define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */ 57 + #define STATMOUNT_MNT_NS_ID 0x00000040U /* Want/got mnt_ns_id */ 58 + #define STATMOUNT_MNT_OPTS 0x00000080U /* Want/got mnt_opts */ 59 + 60 + #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ 61 + 62 + struct statmount { 63 + __u32 size; 64 + __u32 mnt_opts; 65 + __u64 mask; 66 + __u32 sb_dev_major; 67 + __u32 sb_dev_minor; 68 + __u64 sb_magic; 69 + __u32 sb_flags; 70 + __u32 fs_type; 71 + __u64 mnt_id; 72 + __u64 mnt_parent_id; 73 + __u32 mnt_id_old; 74 + __u32 mnt_parent_id_old; 75 + __u64 mnt_attr; 76 + __u64 mnt_propagation; 77 + __u64 mnt_peer_group; 78 + __u64 mnt_master; 79 + __u64 propagate_from; 80 + __u32 mnt_root; 81 + __u32 mnt_point; 82 + __u64 mnt_ns_id; 83 + __u64 __spare2[49]; 84 + char str[]; 85 + }; 86 + 87 + struct mnt_id_req { 88 + __u32 size; 89 + __u32 spare; 90 + __u64 mnt_id; 91 + __u64 param; 92 + __u64 mnt_ns_id; 93 + }; 94 + 95 + #define MNT_ID_REQ_SIZE_VER1 32 /* sizeof second published struct */ 96 + 97 + #define LSMT_ROOT 0xffffffffffffffff /* root mount */ 98 + 99 + static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask, 100 + struct statmount *stmnt, size_t bufsize, 101 + unsigned int flags) 102 + { 103 + struct mnt_id_req req = { 104 + .size = MNT_ID_REQ_SIZE_VER1, 105 + .mnt_id = mnt_id, 106 + .param = mask, 107 + .mnt_ns_id = mnt_ns_id, 108 + }; 109 + 110 + return syscall(__NR_statmount, &req, stmnt, bufsize, flags); 111 + } 112 + 113 + static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id, 114 + __u64 mask, unsigned int flags) 115 + { 116 + size_t bufsize = 1 << 15; 117 + struct statmount *stmnt = NULL, *tmp = NULL; 118 + int ret; 119 + 120 + for (;;) { 121 + tmp = realloc(stmnt, bufsize); 122 + if (!tmp) 123 + goto out; 124 + 125 + stmnt = tmp; 126 + ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags); 127 + if (!ret) 128 + return stmnt; 129 + 130 + if (errno != EOVERFLOW) 131 + goto out; 132 + 133 + bufsize <<= 1; 134 + if (bufsize >= UINT_MAX / 2) 135 + goto out; 136 + } 137 + 138 + out: 139 + free(stmnt); 140 + return NULL; 141 + } 142 + 143 + static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id, 144 + __u64 list[], size_t num, unsigned int flags) 145 + { 146 + struct mnt_id_req req = { 147 + .size = MNT_ID_REQ_SIZE_VER1, 148 + .mnt_id = mnt_id, 149 + .param = last_mnt_id, 150 + .mnt_ns_id = mnt_ns_id, 151 + }; 152 + 153 + return syscall(__NR_listmount, &req, list, num, flags); 154 + } 155 + 156 + int main(int argc, char *argv[]) 157 + { 158 + #define LISTMNT_BUFFER 10 159 + __u64 list[LISTMNT_BUFFER], last_mnt_id = 0; 160 + int ret, pidfd, fd_mntns; 161 + struct mnt_ns_info info = {}; 162 + 163 + pidfd = sys_pidfd_open(getpid(), 0); 164 + if (pidfd < 0) 165 + die_errno("pidfd_open failed"); 166 + 167 + fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0); 168 + if (fd_mntns < 0) 169 + die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed"); 170 + 171 + ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info); 172 + if (ret < 0) 173 + die_errno("ioctl(NS_GET_MNTNS_ID) failed"); 174 + 175 + printf("Listing %u mounts for mount namespace %llu\n", 176 + info.nr_mounts, info.mnt_ns_id); 177 + for (;;) { 178 + ssize_t nr_mounts; 179 + next: 180 + nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id, 181 + info.mnt_ns_id, list, LISTMNT_BUFFER, 182 + 0); 183 + if (nr_mounts <= 0) { 184 + int fd_mntns_next; 185 + 186 + printf("Finished listing %u mounts for mount namespace %llu\n\n", 187 + info.nr_mounts, info.mnt_ns_id); 188 + fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info); 189 + if (fd_mntns_next < 0) { 190 + if (errno == ENOENT) { 191 + printf("Finished listing all mount namespaces\n"); 192 + exit(0); 193 + } 194 + die_errno("ioctl(NS_MNT_GET_NEXT) failed"); 195 + } 196 + close(fd_mntns); 197 + fd_mntns = fd_mntns_next; 198 + last_mnt_id = 0; 199 + printf("Listing %u mounts for mount namespace %llu\n", 200 + info.nr_mounts, info.mnt_ns_id); 201 + goto next; 202 + } 203 + 204 + for (size_t cur = 0; cur < nr_mounts; cur++) { 205 + struct statmount *stmnt; 206 + 207 + last_mnt_id = list[cur]; 208 + 209 + stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id, 210 + STATMOUNT_SB_BASIC | 211 + STATMOUNT_MNT_BASIC | 212 + STATMOUNT_MNT_ROOT | 213 + STATMOUNT_MNT_POINT | 214 + STATMOUNT_MNT_NS_ID | 215 + STATMOUNT_MNT_OPTS | 216 + STATMOUNT_FS_TYPE, 0); 217 + if (!stmnt) { 218 + printf("Failed to statmount(%llu) in mount namespace(%llu)\n", 219 + last_mnt_id, info.mnt_ns_id); 220 + continue; 221 + } 222 + 223 + 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", 224 + stmnt->mnt_id, 225 + stmnt->mnt_parent_id, 226 + stmnt->str + stmnt->fs_type, 227 + stmnt->str + stmnt->mnt_root, 228 + stmnt->str + stmnt->mnt_point, 229 + stmnt->str + stmnt->mnt_opts); 230 + free(stmnt); 231 + } 232 + } 233 + 234 + exit(0); 235 + }