175d0dd10SChristian Brauner // SPDX-License-Identifier: GPL-2.0-or-later 275d0dd10SChristian Brauner // Copyright (c) 2024 Christian Brauner <brauner@kernel.org> 375d0dd10SChristian Brauner 475d0dd10SChristian Brauner #define _GNU_SOURCE 575d0dd10SChristian Brauner #include <errno.h> 675d0dd10SChristian Brauner #include <limits.h> 775d0dd10SChristian Brauner #include <linux/types.h> 875d0dd10SChristian Brauner #include <stdio.h> 975d0dd10SChristian Brauner 1075d0dd10SChristian Brauner #include "../../tools/testing/selftests/pidfd/pidfd.h" 11*f9d94f78SChristian Brauner #include "samples-vfs.h" 1275d0dd10SChristian Brauner 1375d0dd10SChristian Brauner static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask, 1475d0dd10SChristian Brauner struct statmount *stmnt, size_t bufsize, 1575d0dd10SChristian Brauner unsigned int flags) 1675d0dd10SChristian Brauner { 1775d0dd10SChristian Brauner struct mnt_id_req req = { 1875d0dd10SChristian Brauner .size = MNT_ID_REQ_SIZE_VER1, 1975d0dd10SChristian Brauner .mnt_id = mnt_id, 2075d0dd10SChristian Brauner .param = mask, 2175d0dd10SChristian Brauner .mnt_ns_id = mnt_ns_id, 2275d0dd10SChristian Brauner }; 2375d0dd10SChristian Brauner 2475d0dd10SChristian Brauner return syscall(__NR_statmount, &req, stmnt, bufsize, flags); 2575d0dd10SChristian Brauner } 2675d0dd10SChristian Brauner 2775d0dd10SChristian Brauner static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id, 2875d0dd10SChristian Brauner __u64 mask, unsigned int flags) 2975d0dd10SChristian Brauner { 3075d0dd10SChristian Brauner size_t bufsize = 1 << 15; 3175d0dd10SChristian Brauner struct statmount *stmnt = NULL, *tmp = NULL; 3275d0dd10SChristian Brauner int ret; 3375d0dd10SChristian Brauner 3475d0dd10SChristian Brauner for (;;) { 3575d0dd10SChristian Brauner tmp = realloc(stmnt, bufsize); 3675d0dd10SChristian Brauner if (!tmp) 3775d0dd10SChristian Brauner goto out; 3875d0dd10SChristian Brauner 3975d0dd10SChristian Brauner stmnt = tmp; 4075d0dd10SChristian Brauner ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags); 4175d0dd10SChristian Brauner if (!ret) 4275d0dd10SChristian Brauner return stmnt; 4375d0dd10SChristian Brauner 4475d0dd10SChristian Brauner if (errno != EOVERFLOW) 4575d0dd10SChristian Brauner goto out; 4675d0dd10SChristian Brauner 4775d0dd10SChristian Brauner bufsize <<= 1; 4875d0dd10SChristian Brauner if (bufsize >= UINT_MAX / 2) 4975d0dd10SChristian Brauner goto out; 5075d0dd10SChristian Brauner } 5175d0dd10SChristian Brauner 5275d0dd10SChristian Brauner out: 5375d0dd10SChristian Brauner free(stmnt); 5475d0dd10SChristian Brauner return NULL; 5575d0dd10SChristian Brauner } 5675d0dd10SChristian Brauner 5775d0dd10SChristian Brauner static ssize_t sys_listmount(__u64 mnt_id, __u64 last_mnt_id, __u64 mnt_ns_id, 5875d0dd10SChristian Brauner __u64 list[], size_t num, unsigned int flags) 5975d0dd10SChristian Brauner { 6075d0dd10SChristian Brauner struct mnt_id_req req = { 6175d0dd10SChristian Brauner .size = MNT_ID_REQ_SIZE_VER1, 6275d0dd10SChristian Brauner .mnt_id = mnt_id, 6375d0dd10SChristian Brauner .param = last_mnt_id, 6475d0dd10SChristian Brauner .mnt_ns_id = mnt_ns_id, 6575d0dd10SChristian Brauner }; 6675d0dd10SChristian Brauner 6775d0dd10SChristian Brauner return syscall(__NR_listmount, &req, list, num, flags); 6875d0dd10SChristian Brauner } 6975d0dd10SChristian Brauner 7075d0dd10SChristian Brauner int main(int argc, char *argv[]) 7175d0dd10SChristian Brauner { 7275d0dd10SChristian Brauner #define LISTMNT_BUFFER 10 7375d0dd10SChristian Brauner __u64 list[LISTMNT_BUFFER], last_mnt_id = 0; 7475d0dd10SChristian Brauner int ret, pidfd, fd_mntns; 7575d0dd10SChristian Brauner struct mnt_ns_info info = {}; 7675d0dd10SChristian Brauner 7775d0dd10SChristian Brauner pidfd = sys_pidfd_open(getpid(), 0); 7875d0dd10SChristian Brauner if (pidfd < 0) 7975d0dd10SChristian Brauner die_errno("pidfd_open failed"); 8075d0dd10SChristian Brauner 8175d0dd10SChristian Brauner fd_mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, 0); 8275d0dd10SChristian Brauner if (fd_mntns < 0) 8375d0dd10SChristian Brauner die_errno("ioctl(PIDFD_GET_MNT_NAMESPACE) failed"); 8475d0dd10SChristian Brauner 8575d0dd10SChristian Brauner ret = ioctl(fd_mntns, NS_MNT_GET_INFO, &info); 8675d0dd10SChristian Brauner if (ret < 0) 8775d0dd10SChristian Brauner die_errno("ioctl(NS_GET_MNTNS_ID) failed"); 8875d0dd10SChristian Brauner 8975d0dd10SChristian Brauner printf("Listing %u mounts for mount namespace %llu\n", 9075d0dd10SChristian Brauner info.nr_mounts, info.mnt_ns_id); 9175d0dd10SChristian Brauner for (;;) { 9275d0dd10SChristian Brauner ssize_t nr_mounts; 9375d0dd10SChristian Brauner next: 9475d0dd10SChristian Brauner nr_mounts = sys_listmount(LSMT_ROOT, last_mnt_id, 9575d0dd10SChristian Brauner info.mnt_ns_id, list, LISTMNT_BUFFER, 9675d0dd10SChristian Brauner 0); 9775d0dd10SChristian Brauner if (nr_mounts <= 0) { 9875d0dd10SChristian Brauner int fd_mntns_next; 9975d0dd10SChristian Brauner 10075d0dd10SChristian Brauner printf("Finished listing %u mounts for mount namespace %llu\n\n", 10175d0dd10SChristian Brauner info.nr_mounts, info.mnt_ns_id); 10275d0dd10SChristian Brauner fd_mntns_next = ioctl(fd_mntns, NS_MNT_GET_NEXT, &info); 10375d0dd10SChristian Brauner if (fd_mntns_next < 0) { 10475d0dd10SChristian Brauner if (errno == ENOENT) { 10575d0dd10SChristian Brauner printf("Finished listing all mount namespaces\n"); 10675d0dd10SChristian Brauner exit(0); 10775d0dd10SChristian Brauner } 10875d0dd10SChristian Brauner die_errno("ioctl(NS_MNT_GET_NEXT) failed"); 10975d0dd10SChristian Brauner } 11075d0dd10SChristian Brauner close(fd_mntns); 11175d0dd10SChristian Brauner fd_mntns = fd_mntns_next; 11275d0dd10SChristian Brauner last_mnt_id = 0; 11375d0dd10SChristian Brauner printf("Listing %u mounts for mount namespace %llu\n", 11475d0dd10SChristian Brauner info.nr_mounts, info.mnt_ns_id); 11575d0dd10SChristian Brauner goto next; 11675d0dd10SChristian Brauner } 11775d0dd10SChristian Brauner 11875d0dd10SChristian Brauner for (size_t cur = 0; cur < nr_mounts; cur++) { 11975d0dd10SChristian Brauner struct statmount *stmnt; 12075d0dd10SChristian Brauner 12175d0dd10SChristian Brauner last_mnt_id = list[cur]; 12275d0dd10SChristian Brauner 12375d0dd10SChristian Brauner stmnt = sys_statmount(last_mnt_id, info.mnt_ns_id, 12475d0dd10SChristian Brauner STATMOUNT_SB_BASIC | 12575d0dd10SChristian Brauner STATMOUNT_MNT_BASIC | 12675d0dd10SChristian Brauner STATMOUNT_MNT_ROOT | 12775d0dd10SChristian Brauner STATMOUNT_MNT_POINT | 12875d0dd10SChristian Brauner STATMOUNT_MNT_NS_ID | 12975d0dd10SChristian Brauner STATMOUNT_MNT_OPTS | 13075d0dd10SChristian Brauner STATMOUNT_FS_TYPE, 0); 13175d0dd10SChristian Brauner if (!stmnt) { 13275d0dd10SChristian Brauner printf("Failed to statmount(%llu) in mount namespace(%llu)\n", 13375d0dd10SChristian Brauner last_mnt_id, info.mnt_ns_id); 13475d0dd10SChristian Brauner continue; 13575d0dd10SChristian Brauner } 13675d0dd10SChristian Brauner 13775d0dd10SChristian Brauner 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", 13875d0dd10SChristian Brauner stmnt->mnt_id, 13975d0dd10SChristian Brauner stmnt->mnt_parent_id, 14075d0dd10SChristian Brauner stmnt->str + stmnt->fs_type, 14175d0dd10SChristian Brauner stmnt->str + stmnt->mnt_root, 14275d0dd10SChristian Brauner stmnt->str + stmnt->mnt_point, 14375d0dd10SChristian Brauner stmnt->str + stmnt->mnt_opts); 14475d0dd10SChristian Brauner free(stmnt); 14575d0dd10SChristian Brauner } 14675d0dd10SChristian Brauner } 14775d0dd10SChristian Brauner 14875d0dd10SChristian Brauner exit(0); 14975d0dd10SChristian Brauner } 150