Skip to content

Commit 75d0dd1

Browse files
committed
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 <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent d3238e8 commit 75d0dd1

File tree

3 files changed

+237
-1
lines changed

3 files changed

+237
-1
lines changed

samples/vfs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
/test-fsmount
3+
/test-list-all-mounts
34
/test-statx
45
/mountinfo

samples/vfs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# 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
33

44
userccflags += -I usr/include

samples/vfs/test-list-all-mounts.c

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (c) 2024 Christian Brauner <[email protected]>
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+
}

0 commit comments

Comments
 (0)