Skip to content

Commit

Permalink
helpers.linux.fs: support d_path() without vfsmount
Browse files Browse the repository at this point in the history
There are some diagnostic use cases where all one has is a dentry and no
vfsmount. This is technically ambiguous, because a superblock may be
mounted in several places due to bind mounts, filesystem namespaces,
etc. The dentry's full path would depend on the specific mount point.

But when we're doing debugging, we frequently just want any
representative filesystem path for the dentry. It turns out that the
kernel always puts new mountpoints at the end of the superblock's list
of mounts, so the first one is likely to be the most relevant anyway.
Thus, arbitrarily choosing this first mountpoint is a good way to get a
representative path. Update d_path() to accept a single dentry as well.

Signed-off-by: Stephen Brennan <[email protected]>
  • Loading branch information
brenns10 authored and osandov committed Sep 18, 2024
1 parent e2a41bc commit 8488aad
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 7 deletions.
52 changes: 45 additions & 7 deletions drgn/helpers/linux/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,54 @@ def d_path(vfsmnt: Object, dentry: Object) -> bytes:
...


@overload
def d_path(dentry: Object) -> bytes:
"""
Return the full path of a dentry.
Since a mount is not provided, this arbitrarily selects a mount to determine
the path.
:param dentry: ``struct dentry *``
"""
...


def d_path( # type: ignore # Need positional-only arguments.
path_or_vfsmnt: Object, dentry: Optional[Object] = None
arg1: Object, arg2: Optional[Object] = None
) -> bytes:
if dentry is None:
vfsmnt = path_or_vfsmnt.mnt
dentry = path_or_vfsmnt.dentry.read_()
if arg2 is None:
try:
mnt = container_of(arg1.mnt, "struct mount", "mnt")
dentry = arg1.dentry.read_()
except AttributeError:
# Select an arbitrary mount from this dentry's super block. We
# choose the first non-internal mount. Internal mounts exist for
# kernel filesystems (e.g. debugfs) and they are mounted at "/".
# Paths from these mounts aren't usable in userspace and they're
# confusing. If there's no other option, we will use the first
# internal mount we encountered.
#
# The MNT_INTERNAL flag is defined as a macro in the kernel source.
# Introduced in 2.6.34 and has not been modified since.
MNT_INTERNAL = 0x4000
internal_mnt = None
dentry = arg1
for mnt in list_for_each_entry(
"struct mount", dentry.d_sb.s_mounts.address_of_(), "mnt_instance"
):
if mnt.mnt.mnt_flags & MNT_INTERNAL:
internal_mnt = internal_mnt or mnt
continue
break
else:
if internal_mnt is not None:
mnt = internal_mnt
else:
raise ValueError("Could not find a mount for this dentry")
else:
vfsmnt = path_or_vfsmnt
dentry = dentry.read_()
mnt = container_of(vfsmnt, "struct mount", "mnt")
mnt = container_of(arg1, "struct mount", "mnt")
dentry = arg2.read_()

d_op = dentry.d_op.read_()
if d_op and d_op.d_dname:
Expand Down
14 changes: 14 additions & 0 deletions tests/linux_kernel/helpers/test_fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ def test_d_path(self):
task = find_task(self.prog, os.getpid())
self.assertEqual(d_path(task.fs.pwd.address_of_()), os.fsencode(os.getcwd()))

def test_d_path_dentry_only(self):
# This test could fail if we are running inside a container or if we are
# in a bind mount.
task = find_task(self.prog, os.getpid())
self.assertEqual(d_path(task.fs.pwd.dentry), os.fsencode(os.getcwd()))

def test_d_path_no_internal_mount(self):
if not os.path.isdir("/sys/kernel/tracing"):
self.skipTest("The /sys/kernel/tracing directory is not mounted")
path = path_lookup(self.prog, "/sys/kernel/tracing/trace_pipe")
# The first mount for this super block is usually MNT_INTERNAL, but we
# don't want that one. Ensure we skip it.
self.assertEqual(d_path(path.dentry), b"/sys/kernel/tracing/trace_pipe")

def test_dentry_path(self):
pwd = os.fsencode(os.getcwd())
task = find_task(self.prog, os.getpid())
Expand Down

0 comments on commit 8488aad

Please sign in to comment.