]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
samples: add test-list-all-mounts
authorChristian Brauner <brauner@kernel.org>
Thu, 12 Dec 2024 23:03:49 +0000 (00:03 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 9 Jan 2025 15:58:53 +0000 (16:58 +0100)
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>
samples/vfs/.gitignore
samples/vfs/Makefile
samples/vfs/test-list-all-mounts.c [new file with mode: 0644]

index 33a03cffe072fe2466c9df30ad47e9c58b0eea7c..8708341bc082a1204f7d4dccd5f3a2eb959af702 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 /test-fsmount
+/test-list-all-mounts
 /test-statx
 /mountinfo
index fb9bb33fdc751556e806aa897f0dbd48f7e3a4d8..6554b73a75c8e2cd02f9ef243bc9e250c4d84a24 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
-userprogs-always-y += test-fsmount test-statx mountinfo
+userprogs-always-y += test-fsmount test-statx mountinfo test-list-all-mounts
 
 userccflags += -I usr/include
diff --git a/samples/vfs/test-list-all-mounts.c b/samples/vfs/test-list-all-mounts.c
new file mode 100644 (file)
index 0000000..f372d5a
--- /dev/null
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <limits.h>
+#include <linux/types.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+
+#include "../../tools/testing/selftests/pidfd/pidfd.h"
+
+#define die_errno(format, ...)                                             \
+       do {                                                               \
+               fprintf(stderr, "%m | %s: %d: %s: " format "\n", __FILE__, \
+                       __LINE__, __func__, ##__VA_ARGS__);                \
+               exit(EXIT_FAILURE);                                        \
+       } while (0)
+
+/* Get the id for a mount namespace */
+#define NS_GET_MNTNS_ID _IO(0xb7, 0x5)
+/* Get next mount namespace. */
+
+struct mnt_ns_info {
+       __u32 size;
+       __u32 nr_mounts;
+       __u64 mnt_ns_id;
+};
+
+#define MNT_NS_INFO_SIZE_VER0 16 /* size of first published struct */
+
+/* Get information about namespace. */
+#define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info)
+/* Get next namespace. */
+#define NS_MNT_GET_NEXT _IOR(0xb7, 11, struct mnt_ns_info)
+/* Get previous namespace. */
+#define NS_MNT_GET_PREV _IOR(0xb7, 12, struct mnt_ns_info)
+
+#define PIDFD_GET_MNT_NAMESPACE _IO(0xFF, 3)
+
+#ifndef __NR_listmount
+#define __NR_listmount 458
+#endif
+
+#ifndef __NR_statmount
+#define __NR_statmount 457
+#endif
+
+/* @mask bits for statmount(2) */
+#define STATMOUNT_SB_BASIC             0x00000001U /* Want/got sb_... */
+#define STATMOUNT_MNT_BASIC            0x00000002U /* Want/got mnt_... */
+#define STATMOUNT_PROPAGATE_FROM       0x00000004U /* Want/got propagate_from */
+#define STATMOUNT_MNT_ROOT             0x00000008U /* Want/got mnt_root  */
+#define STATMOUNT_MNT_POINT            0x00000010U /* Want/got mnt_point */
+#define STATMOUNT_FS_TYPE              0x00000020U /* Want/got fs_type */
+#define STATMOUNT_MNT_NS_ID            0x00000040U /* Want/got mnt_ns_id */
+#define STATMOUNT_MNT_OPTS             0x00000080U /* Want/got mnt_opts */
+
+#define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */
+
+struct statmount {
+       __u32 size;
+       __u32 mnt_opts;
+       __u64 mask;
+       __u32 sb_dev_major;
+       __u32 sb_dev_minor;
+       __u64 sb_magic;
+       __u32 sb_flags;
+       __u32 fs_type;
+       __u64 mnt_id;
+       __u64 mnt_parent_id;
+       __u32 mnt_id_old;
+       __u32 mnt_parent_id_old;
+       __u64 mnt_attr;
+       __u64 mnt_propagation;
+       __u64 mnt_peer_group;
+       __u64 mnt_master;
+       __u64 propagate_from;
+       __u32 mnt_root;
+       __u32 mnt_point;
+       __u64 mnt_ns_id;
+       __u64 __spare2[49];
+       char str[];
+};
+
+struct mnt_id_req {
+       __u32 size;
+       __u32 spare;
+       __u64 mnt_id;
+       __u64 param;
+       __u64 mnt_ns_id;
+};
+
+#define MNT_ID_REQ_SIZE_VER1 32 /* sizeof second published struct */
+
+#define LSMT_ROOT 0xffffffffffffffff /* root mount */
+
+static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask,
+                      struct statmount *stmnt, size_t bufsize,
+                      unsigned int flags)
+{
+       struct mnt_id_req req = {
+               .size           = MNT_ID_REQ_SIZE_VER1,
+               .mnt_id         = mnt_id,
+               .param          = mask,
+               .mnt_ns_id      = mnt_ns_id,
+       };
+
+       return syscall(__NR_statmount, &req, stmnt, bufsize, flags);
+}
+
+static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id,
+                                      __u64 mask, unsigned int flags)
+{
+       size_t bufsize = 1 << 15;
+       struct statmount *stmnt = NULL, *tmp = NULL;
+       int ret;
+
+       for (;;) {
+               tmp = realloc(stmnt, bufsize);
+               if (!tmp)
+                       goto out;
+
+               stmnt = tmp;
+               ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags);
+               if (!ret)
+                       return stmnt;
+
+               if (errno != EOVERFLOW)
+                       goto out;
+
+               bufsize <<= 1;
+               if (bufsize >= UINT_MAX / 2)
+                       goto out;
+       }
+
+out:
+       free(stmnt);
+       return NULL;
+}
+
+static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id,
+                            __u64 list[], size_t num, unsigned int flags)
+{
+       struct mnt_id_req req = {
+               .size           = MNT_ID_REQ_SIZE_VER1,
+               .mnt_id         = mnt_id,
+               .param          = last_mnt_id,
+               .mnt_ns_id      = mnt_ns_id,
+       };
+
+       return syscall(__NR_listmount, &req, list, num, flags);
+}
+
+int main(int argc, char *argv[])
+{
+#define LISTMNT_BUFFER 10
+       __u64 list[LISTMNT_BUFFER], last_mnt_id = 0;
+       int ret, pidfd, fd_mntns;
+       struct mnt_ns_info info = {};
+
+       pidfd = sys_pidfd_open(getpid(), 0);
+       if (pidfd < 0)
+               die_errno("pidfd_open failed");
+
+       fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0);
+       if (fd_mntns < 0)
+               die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed");
+
+       ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info);
+       if (ret < 0)
+               die_errno("ioctl(NS_GET_MNTNS_ID) failed");
+
+       printf("Listing %u mounts for mount namespace %llu\n",
+              info.nr_mounts, info.mnt_ns_id);
+       for (;;) {
+               ssize_t nr_mounts;
+next:
+               nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id,
+                                         info.mnt_ns_id, list, LISTMNT_BUFFER,
+                                         0);
+               if (nr_mounts <= 0) {
+                       int fd_mntns_next;
+
+                       printf("Finished listing %u mounts for mount namespace %llu\n\n",
+                              info.nr_mounts, info.mnt_ns_id);
+                       fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info);
+                       if (fd_mntns_next < 0) {
+                               if (errno == ENOENT) {
+                                       printf("Finished listing all mount namespaces\n");
+                                       exit(0);
+                               }
+                               die_errno("ioctl(NS_MNT_GET_NEXT) failed");
+                       }
+                       close(fd_mntns);
+                       fd_mntns = fd_mntns_next;
+                       last_mnt_id = 0;
+                       printf("Listing %u mounts for mount namespace %llu\n",
+                              info.nr_mounts, info.mnt_ns_id);
+                       goto next;
+               }
+
+               for (size_t cur = 0; cur < nr_mounts; cur++) {
+                       struct statmount *stmnt;
+
+                       last_mnt_id = list[cur];
+
+                       stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id,
+                                             STATMOUNT_SB_BASIC |
+                                             STATMOUNT_MNT_BASIC |
+                                             STATMOUNT_MNT_ROOT |
+                                             STATMOUNT_MNT_POINT |
+                                             STATMOUNT_MNT_NS_ID |
+                                             STATMOUNT_MNT_OPTS |
+                                             STATMOUNT_FS_TYPE, 0);
+                       if (!stmnt) {
+                               printf("Failed to statmount(%llu) in mount namespace(%llu)\n",
+                                      last_mnt_id, info.mnt_ns_id);
+                               continue;
+                       }
+
+                       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",
+                              stmnt->mnt_id,
+                              stmnt->mnt_parent_id,
+                              stmnt->str + stmnt->fs_type,
+                              stmnt->str + stmnt->mnt_root,
+                              stmnt->str + stmnt->mnt_point,
+                              stmnt->str + stmnt->mnt_opts);
+                       free(stmnt);
+               }
+       }
+
+       exit(0);
+}