* [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program
@ 2024-11-15 15:35 Jeff Layton
2024-11-15 15:35 ` [PATCH v2 1/2] samples: add a mountinfo program to demonstrate statmount()/listmount() Jeff Layton
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Jeff Layton @ 2024-11-15 15:35 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, Jan Kara
Cc: Josef Bacik, Miklos Szeredi, Ian Kent, David Howells,
Amir Goldstein, Paul Moore, Karel Zak, linux-fsdevel,
linux-kernel, Jeff Layton
We had some recent queries internally asking how to use the new
statmount() and listmount() interfaces. I was doing some other work in
this area, so I whipped up this tool.
My hope is that this will represent something of a "rosetta stone" for
how to translate between mountinfo and statmount(), and an example for
other people looking to use the new interfaces.
It may also be possible to use this as the basis for a listmount() and
statmount() testcase. We can call this program, and compare its output
to the mountinfo file.
The second patch adds security mount options to the existing mnt_opts in
the statmount() interface, which I think is the final missing piece
here. The alternative to doing that would be to add a new string field
for that, but I'm not sure that's worthwhile.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Changes in v2:
- fixed off-by-one bug in listmount last_mnt_id handling
- patch to add the security mount options to statmount()
- Link to v1: https://lore.kernel.org/r/20241112-statmount-v1-1-d98090c4c8be@kernel.org
---
Jeff Layton (2):
samples: add a mountinfo program to demonstrate statmount()/listmount()
fs: prepend statmount.mnt_opts string with security_sb_mnt_opts()
fs/namespace.c | 4 +
samples/vfs/.gitignore | 1 +
samples/vfs/Makefile | 2 +-
samples/vfs/mountinfo.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 277 insertions(+), 1 deletion(-)
---
base-commit: 4be4eaeb1a60a7d52e66123f2f52f2da017c9881
change-id: 20241112-statmount-77808bc02302
Best regards,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] samples: add a mountinfo program to demonstrate statmount()/listmount()
2024-11-15 15:35 [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Jeff Layton
@ 2024-11-15 15:35 ` Jeff Layton
2024-11-15 15:35 ` [PATCH v2 2/2] fs: prepend statmount.mnt_opts string with security_sb_mnt_opts() Jeff Layton
2024-11-20 8:23 ` [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Jeff Layton @ 2024-11-15 15:35 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, Jan Kara
Cc: Josef Bacik, Miklos Szeredi, Ian Kent, David Howells,
Amir Goldstein, Paul Moore, Karel Zak, linux-fsdevel,
linux-kernel, Jeff Layton
Add a new "mountinfo" sample userland program that demonstrates how to
use statmount() and listmount() to get at the same info that
/proc/pid/mountinfo provides.
The output of the program tries to mimic the mountinfo procfile
contents. With the -p flag, it can be pointed at an arbitrary pid to
print out info about its mount namespace. With the -r flag it will
attempt to walk all of the namespaces under the pid's mount namespace
and dump out mount info from all of them.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
samples/vfs/.gitignore | 1 +
samples/vfs/Makefile | 2 +-
samples/vfs/mountinfo.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 273 insertions(+), 1 deletion(-)
diff --git a/samples/vfs/.gitignore b/samples/vfs/.gitignore
index 79212d91285bca72b0ff85f28aaccd2e803ac092..33a03cffe072fe2466c9df30ad47e9c58b0eea7c 100644
--- a/samples/vfs/.gitignore
+++ b/samples/vfs/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
/test-fsmount
/test-statx
+/mountinfo
diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile
index 6377a678134acf0d682151d751d2f5042dbf5e0a..fb9bb33fdc751556e806aa897f0dbd48f7e3a4d8 100644
--- a/samples/vfs/Makefile
+++ b/samples/vfs/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-userprogs-always-y += test-fsmount test-statx
+userprogs-always-y += test-fsmount test-statx mountinfo
userccflags += -I usr/include
diff --git a/samples/vfs/mountinfo.c b/samples/vfs/mountinfo.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9f21113a93b7aa7606de2edfce5ad0d5bcf2056
--- /dev/null
+++ b/samples/vfs/mountinfo.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Use pidfds, nsfds, listmount() and statmount() mimic the
+ * contents of /proc/self/mountinfo.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <linux/pidfd.h>
+#include <linux/mount.h>
+#include <linux/nsfs.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+
+/* max mounts per listmount call */
+#define MAXMOUNTS 1024
+
+/* size of struct statmount (including trailing string buffer) */
+#define STATMOUNT_BUFSIZE 4096
+
+static bool ext_format;
+
+/*
+ * There are no bindings in glibc for listmount() and statmount() (yet),
+ * make our own here.
+ */
+static int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask,
+ struct statmount *buf, size_t bufsize,
+ unsigned int flags)
+{
+ struct mnt_id_req req = {
+ .size = MNT_ID_REQ_SIZE_VER0,
+ .mnt_id = mnt_id,
+ .param = mask,
+ };
+
+ if (mnt_ns_id) {
+ req.size = MNT_ID_REQ_SIZE_VER1;
+ req.mnt_ns_id = mnt_ns_id;
+ }
+
+ return syscall(__NR_statmount, &req, buf, bufsize, flags);
+}
+
+static ssize_t listmount(uint64_t mnt_id, uint64_t mnt_ns_id,
+ uint64_t last_mnt_id, uint64_t list[], size_t num,
+ unsigned int flags)
+{
+ struct mnt_id_req req = {
+ .size = MNT_ID_REQ_SIZE_VER0,
+ .mnt_id = mnt_id,
+ .param = last_mnt_id,
+ };
+
+ if (mnt_ns_id) {
+ req.size = MNT_ID_REQ_SIZE_VER1;
+ req.mnt_ns_id = mnt_ns_id;
+ }
+
+ return syscall(__NR_listmount, &req, list, num, flags);
+}
+
+static void show_mnt_attrs(uint64_t flags)
+{
+ printf("%s", flags & MOUNT_ATTR_RDONLY ? "ro" : "rw");
+
+ if (flags & MOUNT_ATTR_NOSUID)
+ printf(",nosuid");
+ if (flags & MOUNT_ATTR_NODEV)
+ printf(",nodev");
+ if (flags & MOUNT_ATTR_NOEXEC)
+ printf(",noexec");
+
+ switch (flags & MOUNT_ATTR__ATIME) {
+ case MOUNT_ATTR_RELATIME:
+ printf(",relatime");
+ break;
+ case MOUNT_ATTR_NOATIME:
+ printf(",noatime");
+ break;
+ case MOUNT_ATTR_STRICTATIME:
+ /* print nothing */
+ break;
+ }
+
+ if (flags & MOUNT_ATTR_NOSYMFOLLOW)
+ printf(",nosymfollow");
+ if (flags & MOUNT_ATTR_IDMAP)
+ printf(",idmapped");
+}
+
+static void show_propagation(struct statmount *sm)
+{
+ if (sm->mnt_propagation & MS_SHARED)
+ printf(" shared:%llu", sm->mnt_peer_group);
+ if (sm->mnt_propagation & MS_SLAVE) {
+ printf(" master:%llu", sm->mnt_master);
+ if (sm->mnt_master)
+ printf(" propagate_from:%llu", sm->propagate_from);
+ }
+ if (sm->mnt_propagation & MS_UNBINDABLE)
+ printf(" unbindable");
+}
+
+static void show_sb_flags(uint64_t flags)
+{
+ printf("%s", flags & MS_RDONLY ? "ro" : "rw");
+ if (flags & MS_SYNCHRONOUS)
+ printf(",sync");
+ if (flags & MS_DIRSYNC)
+ printf(",dirsync");
+ if (flags & MS_MANDLOCK)
+ printf(",mand");
+ if (flags & MS_LAZYTIME)
+ printf(",lazytime");
+}
+
+static int dump_mountinfo(uint64_t mnt_id, uint64_t mnt_ns_id)
+{
+ int ret;
+ struct statmount *buf = alloca(STATMOUNT_BUFSIZE);
+ const uint64_t mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
+ STATMOUNT_PROPAGATE_FROM | STATMOUNT_FS_TYPE |
+ STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT |
+ STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE |
+ STATMOUNT_SB_SOURCE;
+
+ ret = statmount(mnt_id, mnt_ns_id, mask, buf, STATMOUNT_BUFSIZE, 0);
+ if (ret < 0) {
+ perror("statmount");
+ return 1;
+ }
+
+ if (ext_format)
+ printf("0x%lx 0x%lx 0x%llx ", mnt_ns_id, mnt_id, buf->mnt_parent_id);
+
+ printf("%u %u %u:%u %s %s ", buf->mnt_id_old, buf->mnt_parent_id_old,
+ buf->sb_dev_major, buf->sb_dev_minor,
+ &buf->str[buf->mnt_root],
+ &buf->str[buf->mnt_point]);
+ show_mnt_attrs(buf->mnt_attr);
+ show_propagation(buf);
+
+ printf(" - %s", &buf->str[buf->fs_type]);
+ if (buf->mask & STATMOUNT_FS_SUBTYPE)
+ printf(".%s", &buf->str[buf->fs_subtype]);
+ if (buf->mask & STATMOUNT_SB_SOURCE)
+ printf(" %s ", &buf->str[buf->sb_source]);
+ else
+ printf(" :none ");
+
+ show_sb_flags(buf->sb_flags);
+ if (buf->mask & STATMOUNT_MNT_OPTS)
+ printf(",%s", &buf->str[buf->mnt_opts]);
+ printf("\n");
+ return 0;
+}
+
+static int dump_mounts(uint64_t mnt_ns_id)
+{
+ uint64_t mntid[MAXMOUNTS];
+ uint64_t last_mnt_id = 0;
+ ssize_t count;
+ int i;
+
+ /*
+ * Get a list of all mntids in mnt_ns_id. If it returns MAXMOUNTS
+ * mounts, then go again until we get everything.
+ */
+ do {
+ count = listmount(LSMT_ROOT, mnt_ns_id, last_mnt_id, mntid, MAXMOUNTS, 0);
+ if (count < 0 || count > MAXMOUNTS) {
+ errno = count < 0 ? errno : count;
+ perror("listmount");
+ return 1;
+ }
+
+ /* Walk the returned mntids and print info about each */
+ for (i = 0; i < count; ++i) {
+ int ret = dump_mountinfo(mntid[i], mnt_ns_id);
+
+ if (ret != 0)
+ return ret;
+ }
+ /* Set up last_mnt_id to pick up where we left off */
+ last_mnt_id = mntid[count - 1];
+ } while (count == MAXMOUNTS);
+ return 0;
+}
+
+static void usage(const char * const prog)
+{
+ printf("Usage:\n");
+ printf("%s [-e] [-p pid] [-r] [-h]\n", prog);
+ printf(" -e: extended format\n");
+ printf(" -h: print usage message\n");
+ printf(" -p: get mount namespace from given pid\n");
+ printf(" -r: recursively print all mounts in all child namespaces\n");
+}
+
+int main(int argc, char * const *argv)
+{
+ struct mnt_ns_info mni = { .size = MNT_NS_INFO_SIZE_VER0 };
+ int pidfd, mntns, ret, opt;
+ pid_t pid = getpid();
+ bool recursive = false;
+
+ while ((opt = getopt(argc, argv, "ehp:r")) != -1) {
+ switch (opt) {
+ case 'e':
+ ext_format = true;
+ break;
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case 'p':
+ pid = atoi(optarg);
+ break;
+ case 'r':
+ recursive = true;
+ break;
+ }
+ }
+
+ /* Get a pidfd for pid */
+ pidfd = syscall(SYS_pidfd_open, pid, 0);
+ if (pidfd < 0) {
+ perror("pidfd_open");
+ return 1;
+ }
+
+ /* Get the mnt namespace for pidfd */
+ mntns = ioctl(pidfd, PIDFD_GET_MNT_NAMESPACE, NULL);
+ if (mntns < 0) {
+ perror("PIDFD_GET_MNT_NAMESPACE");
+ return 1;
+ }
+ close(pidfd);
+
+ /* get info about mntns. In particular, the mnt_ns_id */
+ ret = ioctl(mntns, NS_MNT_GET_INFO, &mni);
+ if (ret < 0) {
+ perror("NS_MNT_GET_INFO");
+ return 1;
+ }
+
+ do {
+ int ret;
+
+ ret = dump_mounts(mni.mnt_ns_id);
+ if (ret)
+ return ret;
+
+ if (!recursive)
+ break;
+
+ /* get the next mntns (and overwrite the old mount ns info) */
+ ret = ioctl(mntns, NS_MNT_GET_NEXT, &mni);
+ close(mntns);
+ mntns = ret;
+ } while (mntns >= 0);
+
+ return 0;
+}
--
2.47.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] fs: prepend statmount.mnt_opts string with security_sb_mnt_opts()
2024-11-15 15:35 [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Jeff Layton
2024-11-15 15:35 ` [PATCH v2 1/2] samples: add a mountinfo program to demonstrate statmount()/listmount() Jeff Layton
@ 2024-11-15 15:35 ` Jeff Layton
2024-11-20 8:23 ` [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Jeff Layton @ 2024-11-15 15:35 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, Jan Kara
Cc: Josef Bacik, Miklos Szeredi, Ian Kent, David Howells,
Amir Goldstein, Paul Moore, Karel Zak, linux-fsdevel,
linux-kernel, Jeff Layton
Currently these mount options aren't accessible via statmount().
The read handler for /proc/#/mountinfo calls security_sb_show_options()
to emit the security options after emitting superblock flag options, but
before calling sb->s_op->show_options.
Have statmount_mnt_opts() call security_sb_show_options() before
calling ->show_options.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/namespace.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/namespace.c b/fs/namespace.c
index 206fc54feeba3e782f49778bcc99774d5a9d50a4..aae04aa10f984c69090bd1017112be17aa709d0c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -5055,6 +5055,10 @@ static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
if (sb->s_op->show_options) {
size_t start = seq->count;
+ err = security_sb_show_options(seq, sb);
+ if (err)
+ return err;
+
err = sb->s_op->show_options(seq, mnt->mnt_root);
if (err)
return err;
--
2.47.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program
2024-11-15 15:35 [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Jeff Layton
2024-11-15 15:35 ` [PATCH v2 1/2] samples: add a mountinfo program to demonstrate statmount()/listmount() Jeff Layton
2024-11-15 15:35 ` [PATCH v2 2/2] fs: prepend statmount.mnt_opts string with security_sb_mnt_opts() Jeff Layton
@ 2024-11-20 8:23 ` Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2024-11-20 8:23 UTC (permalink / raw)
To: Jeff Layton
Cc: Christian Brauner, Josef Bacik, Miklos Szeredi, Ian Kent,
David Howells, Amir Goldstein, Paul Moore, Karel Zak,
linux-fsdevel, linux-kernel, Alexander Viro, Jan Kara
On Fri, 15 Nov 2024 10:35:51 -0500, Jeff Layton wrote:
> We had some recent queries internally asking how to use the new
> statmount() and listmount() interfaces. I was doing some other work in
> this area, so I whipped up this tool.
>
> My hope is that this will represent something of a "rosetta stone" for
> how to translate between mountinfo and statmount(), and an example for
> other people looking to use the new interfaces.
>
> [...]
Applied to the vfs-6.14.misc branch of the vfs/vfs.git tree.
Patches in the vfs-6.14.misc branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-6.14.misc
[1/2] samples: add a mountinfo program to demonstrate statmount()/listmount()
https://git.kernel.org/vfs/vfs/c/ec07dced06b6
[2/2] fs: prepend statmount.mnt_opts string with security_sb_mnt_opts()
https://git.kernel.org/vfs/vfs/c/d2269a2bfe4a
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-11-20 8:23 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-15 15:35 [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Jeff Layton
2024-11-15 15:35 ` [PATCH v2 1/2] samples: add a mountinfo program to demonstrate statmount()/listmount() Jeff Layton
2024-11-15 15:35 ` [PATCH v2 2/2] fs: prepend statmount.mnt_opts string with security_sb_mnt_opts() Jeff Layton
2024-11-20 8:23 ` [PATCH v2 0/2] fs: listmount()/statmount() fix and sample program Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox