Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helpers.linux.kernfs: add kernfs_children() and follow_symlinks support #449

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions drgn/helpers/linux/kernfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""

import os
from typing import Iterator

from drgn import NULL, Object, Path
from drgn.helpers.linux.rbtree import rbtree_inorder_for_each_entry
Expand All @@ -18,6 +19,7 @@
"kernfs_name",
"kernfs_path",
"kernfs_walk",
"kernfs_children",
)


Expand Down Expand Up @@ -61,28 +63,44 @@ def kernfs_path(kn: Object) -> bytes:
return b"/".join(names)


def kernfs_walk(parent: Object, path: Path) -> Object:
def kernfs_walk(parent: Object, path: Path, follow_symlinks: bool = False) -> Object:
"""
Find the kernfs node with the given path from the given parent kernfs node.

:param parent: ``struct kernfs_node *``
:param path: Path name.
:param follow_symlinks: If follow_symlinks is ``False``, and the
last component of a path is a symlink, the function will
return ``struct kernfs_node *`` of the symbolic link.
Comment on lines +72 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only the last component?

I guess I don't know well enough whether there are cases where symlinks would be in the middle of a real kernfs path. So maybe it won't make a difference. It just seems reasonable to support it within the path too.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That probably came from my description in #443. My intention was that internal symlinks are always followed, and the last one is controlled by the parameter, just like O_NOFOLLOW and the os module. I haven't read the code to see if there was a misunderstanding.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, back from vacation and read the code now. Yes, this needs to be changed to always follow symlinks that are not the last component.

:return: ``struct kernfs_node *`` (``NULL`` if not found)
"""
kernfs_nodep_type = parent.type_
kernfs_node_type = kernfs_nodep_type.type
link_flag = parent.prog_.constant("KERNFS_LINK")
for name in os.fsencode(path).split(b"/"):
if not name:
continue

for parent in rbtree_inorder_for_each_entry(
kernfs_node_type, parent.dir.children.address_of_(), "rb"
):
for parent in kernfs_children(parent):
if (
parent.name.string_() == name
and not parent.ns # For now, we don't bother with namespaced kernfs nodes.
):
break
else:
return NULL(parent.prog_, kernfs_nodep_type)
if parent.flags & link_flag and follow_symlinks:
parent = parent.symlink.target_kn
return parent


def kernfs_children(kn: Object) -> Iterator[Object]:
"""
Iterate over the children of a directory in kernfs.

:param parent: ``struct kernfs_node *``
:return: Iterator of ``struct kernfs_node *``.
"""
for child in rbtree_inorder_for_each_entry(
"struct kernfs_node", kn.dir.children.address_of_(), "rb"
):
yield child
Comment on lines +103 to +106
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor simplification:

Suggested change
for child in rbtree_inorder_for_each_entry(
"struct kernfs_node", kn.dir.children.address_of_(), "rb"
):
yield child
return rbtree_inorder_for_each_entry(
"struct kernfs_node", kn.dir.children.address_of_(), "rb"
)