From: Bhavik Sachdev <b.sachdev1904@gmail.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Aleksa Sarai <cyphar@cyphar.com>,
Bhavik Sachdev <b.sachdev1904@gmail.com>,
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>,
Jan Kara <jack@suse.cz>, John Garry <john.g.garry@oracle.com>,
Arnaldo Carvalho de Melo <acme@redhat.com>,
"Darrick J . Wong" <djwong@kernel.org>,
Namhyung Kim <namhyung@kernel.org>,
Ingo Molnar <mingo@kernel.org>, Andrei Vagin <avagin@gmail.com>,
Alexander Mikhalitsyn <alexander@mihalicyn.com>
Subject: [PATCH v2 1/1] statmount: accept fd as a parameter
Date: Sat, 11 Oct 2025 18:16:11 +0530 [thread overview]
Message-ID: <20251011124753.1820802-2-b.sachdev1904@gmail.com> (raw)
In-Reply-To: <20251011124753.1820802-1-b.sachdev1904@gmail.com>
Extend `struct mnt_id_req` to take in a fd and introduce STATMOUNT_FD
flag. When a valid fd is provided and STATMOUNT_FD is set, statmount
will return mountinfo about the mount the fd is on.
This even works for "unmounted" mounts (mounts that have been umounted
using umount2(mnt, MNT_DETACH)), if you have access to a file descriptor
on that mount. These "umounted" mounts will have no mountpoint hence we
return "[detached]" and the mnt_ns_id to be 0.
Co-developed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Bhavik Sachdev <b.sachdev1904@gmail.com>
---
fs/namespace.c | 80 ++++++++++++++++++++++++++++----------
include/uapi/linux/mount.h | 8 ++++
2 files changed, 67 insertions(+), 21 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index d82910f33dc4..eb82a22cffd5 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -5207,6 +5207,12 @@ static int statmount_mnt_root(struct kstatmount *s, struct seq_file *seq)
return 0;
}
+static int statmount_mnt_point_detached(struct kstatmount *s, struct seq_file *seq)
+{
+ seq_puts(seq, "[detached]");
+ return 0;
+}
+
static int statmount_mnt_point(struct kstatmount *s, struct seq_file *seq)
{
struct vfsmount *mnt = s->mnt;
@@ -5262,7 +5268,10 @@ static int statmount_sb_source(struct kstatmount *s, struct seq_file *seq)
static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
{
s->sm.mask |= STATMOUNT_MNT_NS_ID;
- s->sm.mnt_ns_id = ns->ns.ns_id;
+ if (ns)
+ s->sm.mnt_ns_id = ns->ns.ns_id;
+ else
+ s->sm.mnt_ns_id = 0;
}
static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
@@ -5431,7 +5440,10 @@ static int statmount_string(struct kstatmount *s, u64 flag)
break;
case STATMOUNT_MNT_POINT:
offp = &sm->mnt_point;
- ret = statmount_mnt_point(s, seq);
+ if (!s->root.mnt && !s->root.dentry)
+ ret = statmount_mnt_point_detached(s, seq);
+ else
+ ret = statmount_mnt_point(s, seq);
break;
case STATMOUNT_MNT_OPTS:
offp = &sm->mnt_opts;
@@ -5572,29 +5584,33 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
/* locks: namespace_shared */
static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
- struct mnt_namespace *ns)
+ struct mnt_namespace *ns, unsigned int flags)
{
struct mount *m;
int err;
/* Has the namespace already been emptied? */
- if (mnt_ns_id && mnt_ns_empty(ns))
+ if (!(flags & STATMOUNT_FD) && mnt_ns_id && mnt_ns_empty(ns))
return -ENOENT;
- s->mnt = lookup_mnt_in_ns(mnt_id, ns);
- if (!s->mnt)
- return -ENOENT;
+ if (!(flags & STATMOUNT_FD)) {
+ s->mnt = lookup_mnt_in_ns(mnt_id, ns);
+ if (!s->mnt)
+ return -ENOENT;
+ }
- err = grab_requested_root(ns, &s->root);
- if (err)
- return err;
+ if (ns) {
+ err = grab_requested_root(ns, &s->root);
+ if (err)
+ return err;
+ }
/*
* Don't trigger audit denials. We just want to determine what
* mounts to show users.
*/
m = real_mount(s->mnt);
- if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
+ if (ns && !is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
@@ -5718,12 +5734,12 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
}
static int copy_mnt_id_req(const struct mnt_id_req __user *req,
- struct mnt_id_req *kreq)
+ struct mnt_id_req *kreq, unsigned int flags)
{
int ret;
size_t usize;
- BUILD_BUG_ON(sizeof(struct mnt_id_req) != MNT_ID_REQ_SIZE_VER1);
+ BUILD_BUG_ON(sizeof(struct mnt_id_req) != MNT_ID_REQ_SIZE_VER2);
ret = get_user(usize, &req->size);
if (ret)
@@ -5738,6 +5754,11 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
return ret;
if (kreq->spare != 0)
return -EINVAL;
+ if (flags & STATMOUNT_FD) {
+ if (kreq->fd < 0)
+ return -EINVAL;
+ return 0;
+ }
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET)
return -EINVAL;
@@ -5788,23 +5809,37 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
{
struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
struct kstatmount *ks __free(kfree) = NULL;
+ struct vfsmount *fd_mnt;
struct mnt_id_req kreq;
/* We currently support retrieval of 3 strings. */
size_t seq_size = 3 * PATH_MAX;
int ret;
- if (flags)
+ if (flags & ~STATMOUNT_FD)
return -EINVAL;
- ret = copy_mnt_id_req(req, &kreq);
+ ret = copy_mnt_id_req(req, &kreq, flags);
if (ret)
return ret;
- ns = grab_requested_mnt_ns(&kreq);
- if (!ns)
- return -ENOENT;
+ if (flags & STATMOUNT_FD) {
+ CLASS(fd_raw, f)(kreq.fd);
+ if (fd_empty(f))
+ return -EBADF;
+ fd_mnt = fd_file(f)->f_path.mnt;
+ ns = real_mount(fd_mnt)->mnt_ns;
+ if (ns)
+ refcount_inc(&ns->passive);
+ else
+ if (!ns_capable_noaudit(fd_file(f)->f_cred->user_ns, CAP_SYS_ADMIN))
+ return -ENOENT;
+ } else {
+ ns = grab_requested_mnt_ns(&kreq);
+ if (!ns)
+ return -ENOENT;
+ }
- if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
+ if (ns && (ns != current->nsproxy->mnt_ns) &&
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
return -ENOENT;
@@ -5817,8 +5852,11 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
if (ret)
return ret;
+ if (flags & STATMOUNT_FD)
+ ks->mnt = fd_mnt;
+
scoped_guard(namespace_shared)
- ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns);
+ ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns, flags);
if (!ret)
ret = copy_statmount_to_user(ks);
@@ -5957,7 +5995,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids)))
return -EFAULT;
- ret = copy_mnt_id_req(req, &kreq);
+ ret = copy_mnt_id_req(req, &kreq, 0);
if (ret)
return ret;
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index 7fa67c2031a5..dfe8b8e7fa8d 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -201,11 +201,14 @@ struct mnt_id_req {
__u64 mnt_id;
__u64 param;
__u64 mnt_ns_id;
+ __s32 fd;
+ __u32 spare2;
};
/* List of all mnt_id_req versions. */
#define MNT_ID_REQ_SIZE_VER0 24 /* sizeof first published struct */
#define MNT_ID_REQ_SIZE_VER1 32 /* sizeof second published struct */
+#define MNT_ID_REQ_SIZE_VER2 40 /* sizeof third published struct */
/*
* @mask bits for statmount(2)
@@ -232,4 +235,9 @@ struct mnt_id_req {
#define LSMT_ROOT 0xffffffffffffffff /* root mount */
#define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */
+/*
+ * @flag bits for statmount(2)
+ */
+#define STATMOUNT_FD 0x0000001U /* want mountinfo for given fd */
+
#endif /* _UAPI_LINUX_MOUNT_H */
--
2.51.0
next prev parent reply other threads:[~2025-10-11 12:48 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-11 12:46 [PATCH v2 0/1] statmount: mountinfo for "unmounted" mounts Bhavik Sachdev
2025-10-11 12:46 ` Bhavik Sachdev [this message]
2025-10-21 12:11 ` [PATCH v2 1/1] statmount: accept fd as a parameter Christian Brauner
2025-10-22 15:39 ` Bhavik Sachdev
2025-10-22 16:32 ` Miklos Szeredi
2025-10-22 18:12 ` Bhavik Sachdev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251011124753.1820802-2-b.sachdev1904@gmail.com \
--to=b.sachdev1904@gmail.com \
--cc=acme@redhat.com \
--cc=alexander@mihalicyn.com \
--cc=avagin@gmail.com \
--cc=brauner@kernel.org \
--cc=cyphar@cyphar.com \
--cc=djwong@kernel.org \
--cc=jack@suse.cz \
--cc=john.g.garry@oracle.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=namhyung@kernel.org \
--cc=ptikhomirov@virtuozzo.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.