fs/proc/base.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
From: Chen Linxuan <chenlinxuan@uniontech.com>
In Linux, an fd (file descriptor) is a non-negative integer
representing an open file or other I/O resource associated with a
process. These resources are located on file systems accessed via
mount points.
A mount ID (mnt_id) is a unique identifier for a specific instance
of a mounted file system within a mount namespace, essential for
understanding the file's context, especially in complex or
containerized environments. The executable (exe), pointed to by
/proc/<pid>/exe, is the process's binary file, which also resides
on a file system.
Knowing the mount ID for both file descriptors and the executable
is valuable for debugging and understanding a process's resource
origins.
We can easily obtain the mnt_id for an open fd by reading
/proc/<pid>/fdinfo/<fd}, where it's explicitly listed.
However, there isn't a direct interface (like a specific field in
/proc/<pid}/ status or a dedicated exeinfo file) to easily get the
mount ID of the executable file without performing additional path
resolution or file operations.
Signed-off-by: Chen Linxuan <chenlinxuan@uniontech.com>
Signed-off-by: WangYuli <wangyuli@uniontech.com>
---
fs/proc/base.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b0d4e1908b22..fe8a2d5b3bc1 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -101,6 +101,7 @@
#include <trace/events/oom.h>
#include "internal.h"
#include "fd.h"
+#include "../mount.h"
#include "../../lib/kstrtox.h"
@@ -1790,6 +1791,28 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
return -ENOENT;
}
+static int proc_exe_mntid(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *task)
+{
+ struct file *exe_file;
+ struct path exe_path;
+
+ exe_file = get_task_exe_file(task);
+
+ if (exe_file) {
+ exe_path = exe_file->f_path;
+ path_get(&exe_file->f_path);
+
+ seq_printf(m, "%i\n", real_mount(exe_path.mnt)->mnt_id);
+
+ path_put(&exe_file->f_path);
+ fput(exe_file);
+
+ return 0;
+ } else
+ return -ENOENT;
+}
+
static const char *proc_pid_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
@@ -3342,6 +3365,7 @@ static const struct pid_entry tgid_base_stuff[] = {
LNK("cwd", proc_cwd_link),
LNK("root", proc_root_link),
LNK("exe", proc_exe_link),
+ ONE("exe_mntid", S_IRUGO, proc_exe_mntid),
REG("mounts", S_IRUGO, proc_mounts_operations),
REG("mountinfo", S_IRUGO, proc_mountinfo_operations),
REG("mountstats", S_IRUSR, proc_mountstats_operations),
--
2.49.0
On Sun, May 11, 2025 at 07:42:43PM +0800, WangYuli wrote:
> From: Chen Linxuan <chenlinxuan@uniontech.com>
>
> In Linux, an fd (file descriptor) is a non-negative integer
> representing an open file or other I/O resource associated with a
> process. These resources are located on file systems accessed via
> mount points.
>
> A mount ID (mnt_id) is a unique identifier for a specific instance
> of a mounted file system within a mount namespace, essential for
> understanding the file's context, especially in complex or
> containerized environments. The executable (exe), pointed to by
> /proc/<pid>/exe, is the process's binary file, which also resides
> on a file system.
>
> Knowing the mount ID for both file descriptors and the executable
> is valuable for debugging and understanding a process's resource
> origins.
>
> We can easily obtain the mnt_id for an open fd by reading
> /proc/<pid>/fdinfo/<fd}, where it's explicitly listed.
>
> However, there isn't a direct interface (like a specific field in
> /proc/<pid}/ status or a dedicated exeinfo file) to easily get the
> mount ID of the executable file without performing additional path
> resolution or file operations.
>
> Signed-off-by: Chen Linxuan <chenlinxuan@uniontech.com>
> Signed-off-by: WangYuli <wangyuli@uniontech.com>
> ---
> fs/proc/base.c | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index b0d4e1908b22..fe8a2d5b3bc1 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -101,6 +101,7 @@
> #include <trace/events/oom.h>
> #include "internal.h"
> #include "fd.h"
> +#include "../mount.h"
>
> #include "../../lib/kstrtox.h"
>
> @@ -1790,6 +1791,28 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
> return -ENOENT;
> }
>
> +static int proc_exe_mntid(struct seq_file *m, struct pid_namespace *ns,
> + struct pid *pid, struct task_struct *task)
> +{
> + struct file *exe_file;
> + struct path exe_path;
> +
> + exe_file = get_task_exe_file(task);
> +
> + if (exe_file) {
> + exe_path = exe_file->f_path;
> + path_get(&exe_file->f_path);
> +
> + seq_printf(m, "%i\n", real_mount(exe_path.mnt)->mnt_id);
You're exposing the legacy mount id. It is only guaranteed to be valid
while the executable is running as legacy mount ids are recycled
quickly.
And no, we're not going to expose either in this way via procfs. You can
get the same information via:
fd_exe = open("/proc/<pid>/exe", O_PATH | O_CLOEXEC);
statx(fd_exe, ...)
or via fdinfo:
read("/proc/self/fdinfo/<fd_exe>");
A separate file for this is just not worth it.
On Sun, May 11, 2025 at 07:42:43PM +0800, WangYuli wrote:
> +static int proc_exe_mntid(struct seq_file *m, struct pid_namespace *ns,
> + struct pid *pid, struct task_struct *task)
> +{
> + struct file *exe_file;
> + struct path exe_path;
> +
> + exe_file = get_task_exe_file(task);
> +
> + if (exe_file) {
> + exe_path = exe_file->f_path;
> + path_get(&exe_file->f_path);
> +
> + seq_printf(m, "%i\n", real_mount(exe_path.mnt)->mnt_id);
> +
> + path_put(&exe_file->f_path);
Excuse me, just what is that path_get/path_put for? If you have
an opened file, you do have its ->f_path pinned and unchanging.
Otherwise this call of path_get() would've itself been unsafe...
And I still wonder about the rationale, TBH...
On Sun, May 11, 2025 at 10:22 PM Al Viro <viro@zeniv.linux.org.uk> wrote: > Excuse me, just what is that path_get/path_put for? If you have > an opened file, you do have its ->f_path pinned and unchanging. > Otherwise this call of path_get() would've itself been unsafe... I am not very familiar with how these functions should be used. I just copied similar logic from proc_exe_link and added a path_put. Maybe I made a stupid mistake...
On Mon, May 12, 2025 at 12:19:01AM +0800, Chen Linxuan wrote: > On Sun, May 11, 2025 at 10:22 PM Al Viro <viro@zeniv.linux.org.uk> wrote: > > > Excuse me, just what is that path_get/path_put for? If you have > > an opened file, you do have its ->f_path pinned and unchanging. > > Otherwise this call of path_get() would've itself been unsafe... > > I am not very familiar with how these functions should be used. > I just copied similar logic from proc_exe_link and added a path_put. > Maybe I made a stupid mistake... path_get() grabs an extra reference to mount and dentry; that is enough to guarantee that their refcount will not drop to zero at least until we drop the references. To use it safely you need to be sure that currently refcounts are non-zero and won't be dropped to zero right under you - for rather obvious reasons. Your code also clearly relies upon ->f_path being unchanging - otherwise you would've risked to drop the references not to the objects you've grabbed earlier. If both conditions are satisfied, what's the point of grabbing and dropping these references in the first place? *IF* there was a chance of mount going away under you, what would prevent that happening right before that path_get()? IOW, these dances with path_get()/path_put() are either nowhere near enough or not needed at all. As it is, ->f_path of an open file *IS* stable and mount and dentry references in it do contribute towards mount and dentry refcounts. But my point is, that code does not pass the basic "how could that possibly be right?" test. PS: since you've mentioned proc_exe_link()... the difference there is that we want the reference to be used by the caller. If the file happens to be closed just as we return (that can't happen until proc_exe_link() drops the reference to struct file it got from get_task_exe_file(), but as soon as fput() had been called, there's nothing to guarantee the safety), file's contributions to refcounts are dropped, so we can't count upon them to stay for as long as we need. So in that case we fetch the references out of ->f_path and pin them before we drop the file reference with fput(). PPS: I'm still not convinced regarding the usefulness of having that information; "just because we can display it" isn't a strong argument on its own...
© 2016 - 2025 Red Hat, Inc.