Source code for crash.subsystem.storage

# -*- coding: utf-8 -*-
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

from typing import Iterable

from crash.util import container_of
from crash.util.symbols import Types, Symvals, SymbolCallbacks, TypeCallbacks
from crash.types.classdev import for_each_class_device
from crash.exceptions import DelayedAttributeError, InvalidArgumentError

import gdb
from gdb.types import get_basic_type

types = Types(['struct gendisk', 'struct hd_struct', 'struct device',
               'struct device_type', 'struct bdev_inode'])
symvals = Symvals(['block_class', 'blockdev_superblock', 'disk_type',
                   'part_type'])

[docs]def dev_to_gendisk(dev: gdb.Value) -> gdb.Value: """ Converts a ``struct device`` that is embedded in a ``struct gendisk`` back to the ``struct gendisk``. Args: dev: A ``struct device`` contained within a ``struct gendisk``. The value must be of type ``struct device``. Returns: :obj:`gdb.Value`: The converted gendisk. The value is of type ``struct gendisk``. """ return container_of(dev, types.gendisk_type, 'part0.__dev')
[docs]def dev_to_part(dev: gdb.Value) -> gdb.Value: """ Converts a ``struct device`` that is embedded in a ``struct hd_struct`` back to the ``struct hd_struct``. Args: dev: A ``struct device`` embedded within a ``struct hd_struct``. The value must be of type ``struct device``. Returns: :obj:`gdb.Value`: The converted ``struct hd_struct``. The value is of type ``struct hd_struct``. """ return container_of(dev, types.hd_struct_type, '__dev')
[docs]def gendisk_to_dev(gendisk: gdb.Value) -> gdb.Value: """ Converts a ``struct gendisk`` that embeds a ``struct device`` to the ``struct device``. Args: dev: A ``struct gendisk`` that embeds a ``struct device``. The value must be of type ``struct device``. Returns: :obj:`gdb.Value`: The converted ``struct device``. The value is of type ``struct device``. """ return gendisk['part0']['__dev']
[docs]def part_to_dev(part: gdb.Value) -> gdb.Value: """ Converts a ``struct hd_struct`` that embeds a ``struct device`` to the ``struct device``. Args: dev: A ``struct hd_struct`` that embeds a ``struct device``. The value must be of type ``struct device``. Returns: :obj:`gdb.Value`: The converted ``struct device``. The value is of type ``struct device``. """ return part['__dev']
[docs]def for_each_block_device(subtype: gdb.Value = None) -> Iterable[gdb.Value]: """ Iterates over each block device registered with the block class. This method iterates over the block_class klist and yields every member found. The members are either struct gendisk or struct hd_struct, depending on whether it describes an entire disk or a partition, respectively. The members can be filtered by providing a subtype, which corresponds to a the the type field of the struct device. Args: subtype (optional): The ``struct device_type`` that will be used to match and filter. Typically the values associated with the ``disk_type`` or ``part_type`` :obj:`gdb.Symbol`. Yields: :obj:`gdb.Value`: The next block device that matches the subtype. The value is of type ``struct gendisk`` or ``struct hd_struct``. Raises: :obj:`RuntimeError`: An unknown device type was encountered during iteration. :obj:`TypeError`: The provided subtype was not of ``struct device_type`` or ``struct device type *`` """ if subtype: if get_basic_type(subtype.type) == types.device_type_type: subtype = subtype.address elif get_basic_type(subtype.type) != types.device_type_type.pointer(): raise InvalidArgumentError("subtype must be {} not {}" .format(types.device_type_type.pointer(), subtype.type.unqualified())) for dev in for_each_class_device(symvals.block_class, subtype): if dev['type'] == symvals.disk_type.address: yield dev_to_gendisk(dev) elif dev['type'] == symvals.part_type.address: yield dev_to_part(dev) else: raise RuntimeError("Encountered unexpected device type {}" .format(dev['type']))
[docs]def for_each_disk() -> Iterable[gdb.Value]: """ Iterates over each block device registered with the block class that corresponds to an entire disk. This is an alias for for_each_block_device(``disk_type``) """ return for_each_block_device(symvals.disk_type)
[docs]def gendisk_name(gendisk: gdb.Value) -> str: """ Returns the name of the provided block device. This method evaluates the block device and returns the name, including partition number, if applicable. Args: gendisk: A ``struct gendisk`` or ``struct hd_struct`` for which to return the name. The value must be of type ``struct gendisk`` or ``struct hd_struct``. Returns: :obj:`str`: The name of the block device Raises: :obj:`.InvalidArgumentError`: gendisk does not describe a ``struct gendisk`` or ``struct hd_struct`` """ if gendisk.type.code == gdb.TYPE_CODE_PTR: gendisk = gendisk.dereference() if get_basic_type(gendisk.type) == types.gendisk_type: return gendisk['disk_name'].string() if get_basic_type(gendisk.type) == types.hd_struct_type: parent = dev_to_gendisk(part_to_dev(gendisk)['parent']) return "{}{:d}".format(gendisk_name(parent), int(gendisk['partno'])) raise InvalidArgumentError("expected {} or {}, not {}" .format(types.gendisk_type, types.hd_struct_type, gendisk.type.unqualified()))
[docs]def block_device_name(bdev: gdb.Value) -> str: """ Returns the name of the provided block device. This method evaluates the block device and returns the name, including partition number, if applicable. Args: bdev: A ``struct block_device`` for which to return the name. The value must be of type ``struct block_device``. Returns: :obj:`str`: The name of the block device """ return gendisk_name(bdev['bd_disk'])
[docs]def is_bdev_inode(inode: gdb.Value) -> bool: """ Tests whether the provided ``struct inode`` describes a block device This method evaluates the inode and returns :obj:`True` or :obj:`False`, depending on whether the inode describes a block device. Args: bdev: The ``struct inode`` to test whether it describes a block device. The value must be of type ``struct inode``. Returns: :obj:`bool`: :obj:`True` if the inode describes a block device, :obj:`False` otherwise. """ return inode['i_sb'] == symvals.blockdev_superblock
[docs]def inode_to_block_device(inode: gdb.Value) -> gdb.Value: """ Returns the block device associated with this inode. If the inode describes a block device, return that block device. Otherwise, raise InvalidArgumentError. Args: inode: The ``struct inode`` for which to return the associated block device. The value must be of type ``struct inode``. Returns: :obj:`gdb.Value`: The ``struct block_device`` associated with the provided ``struct inode``. The value is of type ``struct block_device``. Raises: :obj:`.InvalidArgumentError`: inode does not describe a block device """ if inode['i_sb'] != symvals.blockdev_superblock: raise InvalidArgumentError("inode does not correspond to block device") return container_of(inode, types.bdev_inode_type, 'vfs_inode')['bdev']
[docs]def inode_on_bdev(inode: gdb.Value) -> gdb.Value: """ Returns the block device associated with this inode. If the inode describes a block device, return that block device. Otherwise, return the block device, if any, associated with the inode's super block. Args: inode: The ``struct inode`` for which to return the associated block device. The value must be of type ``struct inode``. Returns: :obj:`gdb.Value`: The ``struct block_device`` associated with the provided ``struct inode``. The value is of type ``struct inode``. """ if is_bdev_inode(inode): return inode_to_block_device(inode) return inode['i_sb']['s_bdev'].dereference()
# pylint: disable=unused-argument def _check_types(result: gdb.Symbol) -> None: try: if symvals.part_type.type.unqualified() != types.device_type_type: raise TypeError("part_type expected to be {} not {}" .format(symvals.device_type_type, types.part_type.type)) if symvals.disk_type.type.unqualified() != types.device_type_type: raise TypeError("disk_type expected to be {} not {}" .format(symvals.device_type_type, types.disk_type.type)) except DelayedAttributeError: pass symbol_cbs = SymbolCallbacks([('disk_type', _check_types), ('part_type', _check_types)]) type_cbs = TypeCallbacks([('struct device_type', _check_types)])