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

[1.x] Fix for block blkdev_ro() miss reports disk's readonly status #144

Merged
merged 2 commits into from
Jan 8, 2025
Merged
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
64 changes: 55 additions & 9 deletions drgn_tools/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import drgn
from drgn import cast
from drgn import container_of
from drgn import Object
from drgn import TypeKind
from drgn.helpers.common.format import decode_enum_type_flags
Expand Down Expand Up @@ -51,18 +52,60 @@ def for_each_badblocks(bb: Object) -> Iterable[Object]:

def blkdev_ro(bdev: Object) -> int:
"""
Check whether ``struct block_device *`` is read only
Check whether ``struct block_device *`` is read only, which could be
user set it to readonly through ioctl, or its underlying disk is set
to readonly by disk driver.

:param bdev: ``struct block_device *``
:returns: 1 if readonly, 0 if readwrite, -1 if unknown
:returns: 1 if readonly, 0 if readwrite, -1 if unknown which is possible
for kernel before v5.11 if the block_deivce has not be opened yet.
"""
ro = -1
if has_member(bdev, "__bd_flags"):
# v6.10: 01e198f01d55 ("bdev: move ->bd_read_only to ->__bd_flags")
BD_READ_ONLY = 1 << 8
ro = int(bool(bdev.__bd_flags.counter & BD_READ_ONLY))
elif has_member(bdev, "bd_read_only"):
# v5.11: 83950d359010 ("block: move the policy field to struct
# block_device")
ro = int(bool(bdev.bd_read_only.value_()))

# For v5.11 and up
if ro != -1:
# check whether disk driver set disk to readonly.
# v5.12: 52f019d43c22 ("block: add a hard-readonly flag to struct gendisk")
# introduced this flag to mark disk driver setting disk to readonly.
GD_READ_ONLY = 1 << 1
# v5.10: 38430f0876fa ("block: move the NEED_PART_SCAN flag to struct gendisk")
# add memeber "state" for "struct gendisk", not necessary to check whether
# this member exist since this branch is for v5.11+
ro |= int(bool(bdev.bd_disk.state & GD_READ_ONLY))
return ro
else: # for v5.10 and below
# "bd_part" is NULL if bdev was not opened yet, this can only happen
# for the kernel before this commit:
# v5.11 0d02129e76ed ("block: merge struct block_device and struct hd_struct")
if bdev.bd_part:
return int(bdev.bd_part.policy != 0)
else:
return -1


def BD_INODE(bdev: Object) -> Object:
"""
Return the inode associated with a block device. Analogous to the kernel
function of the same name.

:param bdev: ``struct block_device *``
:returns: ``struct inode *``
"""
if has_member(bdev, "bd_read_only"):
return bool(bdev.bd_read_only.value_())
# "bd_part" is NULL if bdev was not opened yet.
if bdev.bd_part:
return bdev.bd_part.policy != 0
if has_member(bdev, "bd_inode"):
return bdev.bd_inode
else:
return -1
# v6.10: 203c1ce0bb06 ("RIP ->bd_inode")
return container_of(
bdev, "struct bdev_inode", "bdev"
).vfs_inode.address_of_()


def blkdev_size(bdev: Object) -> int:
Expand All @@ -72,7 +115,7 @@ def blkdev_size(bdev: Object) -> int:
:param bdev: ``struct block_device *``
:returns: device size
"""
return int(bdev.bd_inode.i_size)
return BD_INODE(bdev).i_size.value_()


def blkdev_name(bdev: Object) -> str:
Expand All @@ -84,6 +127,9 @@ def blkdev_name(bdev: Object) -> str:
"""
if has_member(bdev, "bd_partno"):
partno = int(bdev.bd_partno)
elif has_member(bdev, "__bd_flags"):
# v6.10: 1116b9fa15c0 ("bdev: infrastructure for flags")
partno = int(bdev.__bd_flags.counter & 0xFF)
else:
partno = int(bdev.bd_part.partno)

Expand Down
Loading