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

dm: add dmsetup table support for linear target #56

Merged
merged 1 commit into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
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
20 changes: 20 additions & 0 deletions drgn_tools/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ def blkdev_ro(bdev: Object) -> bool:
return bdev.bd_part.policy != 0


def blkdev_name(bdev: Object) -> str:
"""
Return block device name

:param bdev: ``struct block_device *``
:returns: device name
"""
if has_member(bdev, "bd_partno"):
partno = int(bdev.bd_partno)
else:
partno = int(bdev.bd_part.partno)

disk_name = bdev.bd_disk.disk_name.string_().decode()
if partno == 0:
return disk_name
if disk_name[len(disk_name) - 1].isdigit():
Copy link
Member

Choose a reason for hiding this comment

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

Python has a neat feature where you can index with a negative integer, and it will instead index backwards from the end of whatever you're indexing. This means you can omit len(disk_name) here, it's quite nice:

Suggested change
if disk_name[len(disk_name) - 1].isdigit():
if disk_name[-1].isdigit():

Anyway, this is just a note for the future :) Not something to block the PR on!

return disk_name + "p" + str(partno)
return disk_name + str(partno)


def for_each_request_queue(prog: drgn.Program) -> Iterable[Object]:
"""
List all request_queue in the system.
Expand Down
67 changes: 67 additions & 0 deletions drgn_tools/dm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from typing import Iterable
from typing import Tuple

from drgn import cast
from drgn import Object
from drgn import Program
from drgn.helpers.linux.list import list_for_each_entry
from drgn.helpers.linux.rbtree import rbtree_inorder_for_each_entry

from drgn_tools.block import blkdev_name
from drgn_tools.corelens import CorelensModule
from drgn_tools.module import ensure_debuginfo
from drgn_tools.table import print_table
Expand Down Expand Up @@ -104,6 +106,70 @@ def show_dm(prog: Program) -> None:
print_table(output)


def dm_table(dm: Object) -> Object:
"""
return the ``struct dm_table *``

There were two definitions of ``struct dm_table`` before commit
1d3aa6f683b1("dm: remove dummy definition of 'struct dm_table'")
which was included in v4.10, specify file for the correct symbol.
"""
if kernel_version(dm.prog_) < (4, 10, 0):
table_type = dm.prog_.type(
"struct dm_table *", "drivers/md/dm-table.c"
)
Comment on lines +118 to +120
Copy link
Member

Choose a reason for hiding this comment

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

Oof. This is quite frustrating, especially because CTF has no way to represent the filename where a type is declared. It resolves name collisions in its own way, which has to do with kernel module names, but it's not perfect either.

Unfortunately there's not much to do about this in the context of this PR. It's merely another concern for me to keep in mind for CTF.

Copy link
Member

Choose a reason for hiding this comment

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

Interestingly enough, I took a look at the UEK4 CTF archives, and found that the "undefined" variant of dm_table is not even included in the CTF archive, since it never gets used by anything. I guess CTF is "smart enough" to handle this situation.

Also, UEK4 is not currently planned to be supported for CTF, so that's not much of an issue anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

CTF is awesome!

return cast(table_type, dm.map)
else:
return cast("struct dm_table *", dm.map)


def dm_target_name(dm: Object) -> str:
table = dm_table(dm)
if table.value_() == 0x0:
Copy link
Member

Choose a reason for hiding this comment

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

Again, another note that for NULL testing, you can simply do if not table. It's not a big deal though, I wouldn't block the PR over it.

return "None"
return table.targets.type.name.string_().decode()


def show_table_linear(dm: Object, name: str) -> None:
table = dm_table(dm)
for tid in range(table.num_targets):
target = table.targets[tid]
dev = cast("struct linear_c *", target.private)
print(
"%s: %d %d linear %d:%d [%s] %d"
% (
name,
int(target.begin),
int(target.len),
dev.dev.bdev.bd_dev >> 20,
dev.dev.bdev.bd_dev & 0xFFFFF,
blkdev_name(dev.dev.bdev),
dev.start,
)
)


dmtable_handler = {
"linear": show_table_linear,
}


def show_dm_table(prog: Program) -> None:
for dm, name in for_each_dm(prog):
target_name = dm_target_name(dm)
if target_name == "None":
print("dm %s doesn't have a target" % hex(dm.value_()))
continue
elif target_name not in dmtable_handler.keys():
print(
"dm %s used non-support target %s"
% (hex(dm.value_()), target_name)
)
continue
else:
dmtable_handler[target_name](dm, name)


class Dm(CorelensModule):
"""Display info about device mapper devices"""

Expand All @@ -112,3 +178,4 @@ class Dm(CorelensModule):

def run(self, prog: Program, args: argparse.Namespace) -> None:
show_dm(prog)
show_dm_table(prog)