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