# -*- coding: utf-8 -*-
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
from typing import Iterable, Union
from crash.util import container_of, get_typed_pointer, decode_flags
from crash.util.symbols import Types, Symvals
from crash.infra.lookup import DelayedSymval, DelayedType
from crash.types.list import list_for_each_entry
from crash.subsystem.storage import block_device_name
import gdb
types = Types('struct super_block')
symvals = Symvals('super_blocks')
AddressSpecifier = Union[int, str, gdb.Value]
MS_RDONLY = 1
MS_NOSUID = 2
MS_NODEV = 4
MS_NOEXEC = 8
MS_SYNCHRONOUS = 16
MS_REMOUNT = 32
MS_MANDLOCK = 64
MS_DIRSYNC = 128
MS_NOATIME = 1024
MS_NODIRATIME = 2048
MS_BIND = 4096
MS_MOVE = 8192
MS_REC = 16384
MS_VERBOSE = 32768
MS_SILENT = 32768
MS_POSIXACL = (1<<16)
MS_UNBINDABLE = (1<<17)
MS_PRIVATE = (1<<18)
MS_SLAVE = (1<<19)
MS_SHARED = (1<<20)
MS_RELATIME = (1<<21)
MS_KERNMOUNT = (1<<22)
MS_I_VERSION = (1<<23)
MS_STRICTATIME = (1<<24)
MS_LAZYTIME = (1<<25)
MS_NOSEC = (1<<28)
MS_BORN = (1<<29)
MS_ACTIVE = (1<<30)
MS_NOUSER = (1<<31)
SB_FLAGS = {
MS_RDONLY : "MS_RDONLY",
MS_NOSUID : "MS_NOSUID",
MS_NODEV : "MS_NODEV",
MS_NOEXEC : "MS_NOEXEC",
MS_SYNCHRONOUS : "MS_SYNCHRONOUS",
MS_REMOUNT : "MS_REMOUNT",
MS_MANDLOCK : "MS_MANDLOCK",
MS_DIRSYNC : "MS_DIRSYNC",
MS_NOATIME : "MS_NOATIME",
MS_NODIRATIME : "MS_NODIRATIME",
MS_BIND : "MS_BIND",
MS_MOVE : "MS_MOVE",
MS_REC : "MS_REC",
MS_SILENT : "MS_SILENT",
MS_POSIXACL : "MS_POSIXACL",
MS_UNBINDABLE : "MS_UNBINDABLE",
MS_PRIVATE : "MS_PRIVATE",
MS_SLAVE : "MS_SLAVE",
MS_SHARED : "MS_SHARED",
MS_RELATIME : "MS_RELATIME",
MS_KERNMOUNT : "MS_KERNMOUNT",
MS_I_VERSION : "MS_I_VERSION",
MS_STRICTATIME : "MS_STRICTATIME",
MS_LAZYTIME : "MS_LAZYTIME",
MS_NOSEC : "MS_NOSEC",
MS_BORN : "MS_BORN",
MS_ACTIVE : "MS_ACTIVE",
MS_NOUSER : "MS_NOUSER",
}
S_IFMT = 0o170000
S_IFSOCK = 0o140000
S_IFLNK = 0o120000
S_IFREG = 0o100000
S_IFBLK = 0o060000
S_IFDIR = 0o040000
S_IFCHR = 0o020000
S_IFIFO = 0o010000
S_ISUID = 0o0004000
S_ISGID = 0o0002000
S_ISVTX = 0o0001000
S_IRWXU = 0o00700
S_IRUSR = 0o00400
S_IWUSR = 0o00200
S_IXUSR = 0o00100
S_IRWXG = 0o00070
S_IRGRP = 0o00040
S_IWGRP = 0o00020
S_IXGRP = 0o00010
S_IRWXO = 0o00007
S_IROTH = 0o00004
S_IWOTH = 0o00002
S_IXOTH = 0o00001
INODE_MODE_BITS = {
S_IFSOCK : 'S_IFSOCK',
S_IFLNK : 'S_IFLNK',
S_IFREG : 'S_IFREG',
S_IFBLK : 'S_IFBLK',
S_IFDIR : 'S_IFDIR',
S_IFCHR : 'S_IFCHR',
S_IFIFO : 'S_IFIFO',
S_ISUID : 'S_ISUID',
S_ISGID : 'S_ISGID',
S_ISVTX : 'S_ISVTX',
S_IRWXU : 'S_IRWXU',
S_IRUSR : 'S_IRUSR',
S_IWUSR : 'S_IWUSR',
S_IXUSR : 'S_IXUSR',
S_IRWXG : 'S_IRWXG',
S_IRGRP : 'S_IRGRP',
S_IWGRP : 'S_IWGRP',
S_IXGRP : 'S_IXGRP',
S_IRWXO : 'S_IRWXO',
S_IROTH : 'S_IROTH',
S_IWOTH : 'S_IWOTH',
S_IXOTH : 'S_IXOTH',
}
_inode_fmt_bits = {
S_IFSOCK : 's',
S_IFLNK : 'l',
S_IFREG : '-',
S_IFBLK : 'b',
S_IFDIR : 'd',
S_IFCHR : 'c',
S_IFIFO : 'p',
}
_inode_rwx_bits = {
S_IRUSR : 'r',
S_IWUSR : 'w',
S_IXUSR : 'x',
S_IRGRP : 'r',
S_IWGRP : 'w',
S_IXGRP : 'x',
S_IROTH : 'r',
S_IWOTH : 'w',
S_IXOTH : 'x',
}
[docs]def ls_style_mode_perms(i_mode: gdb.Value) -> str:
mode = int(i_mode)
fmt = '?'
for bit in sorted(_inode_fmt_bits.keys()):
if (bit & i_mode) == bit:
fmt = _inode_fmt_bits[bit]
perms = [fmt]
for bit in sorted(_inode_rwx_bits.keys(), reverse=True):
if (bit & i_mode) == bit:
perms.append(_inode_rwx_bits[bit])
else:
perms.append('-')
if mode & S_ISUID:
if mode & S_IXUSR:
perms[3] = 's'
else:
perms[3] = 'S'
if mode & S_ISGID:
if mode & S_IXGRP:
perms[6] = 's'
else:
perms[6] = 'S'
if mode & S_ISVTX:
if mode & S_IXOTH:
perms[9] = 't'
else:
perms[9] = 'T'
return "".join(perms)
[docs]def ls_style_inode_perms(inode: gdb.Value) -> str:
return ls_style_mode_perms(inode['i_mode'])
def _S_ISMODE(i_mode: int, mode: int) -> bool:
return (i_mode & S_IFMT) == mode
[docs]def S_ISLNK(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFLNK)
[docs]def S_ISREG(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFREG)
[docs]def S_ISDIR(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFDIR)
[docs]def S_ISCHR(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFCHR)
[docs]def S_ISBLK(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFBLK)
[docs]def S_ISFIFO(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFIFO)
[docs]def S_ISSOCK(i_mode: int) -> bool:
return _S_ISMODE(i_mode, S_IFSOCK)
[docs]def super_fstype(sb: gdb.Value) -> str:
"""
Returns the file system type's name for a given superblock.
Args:
sb: The ``struct super_block`` for which to return the file system
type's name. The value must be of type ``struct super_block``.
Returns:
:obj:`str`:The file system type's name
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
return sb['s_type']['name'].string()
[docs]def super_flags(sb: gdb.Value) -> str:
"""
Returns the flags associated with the given superblock.
Args:
sb: The ``struct super_block`` for which to return the flags.
The value must be of type ``struct super_block``.
Returns:
:obj:`str`:The flags field in human-readable form.
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
return decode_flags(sb['s_flags'], SB_FLAGS)
[docs]def for_each_super_block() -> Iterable[gdb.Value]:
"""
Iterate over the list of super blocks and yield each one.
Yields:
:obj:`gdb.Value`: One value for each super block. Each value
will be of type ``struct super_block``.
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
for sb in list_for_each_entry(symvals.super_blocks,
types.super_block_type, 's_list'):
yield sb
[docs]def get_super_block(desc: AddressSpecifier, force: bool = False) -> gdb.Value:
"""
Given an address description return a gdb.Value that contains
a struct super_block at that address.
Args:
desc: The address for which to provide a casted pointer. The address
may be specified using an existing Value, an integer address,
or a hexadecimal address represented as a 0x-prefixed string.
force: Skip testing whether the value is available.
Returns:
:obj:`gdb.Value`: The super block at the requested location.
The value will be ``struct super_block``.
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
sb = get_typed_pointer(desc, types.super_block_type).dereference()
if not force:
try:
x = int(sb['s_dev']) # pylint: disable=unused-variable
except gdb.NotAvailableError:
raise gdb.NotAvailableError(f"no superblock available at `{desc}'")
return sb
[docs]def is_fstype_super(super_block: gdb.Value, name: str) -> bool:
"""
Tests whether the super_block belongs to a particular file system type.
This uses a naive string comparison so modules are not required.
Args:
super_block: The struct super_block to test. The value must be
of type ``struct super_block``.
name: The name of the file system type
Returns:
:obj:`bool`: whether the ``struct super_block`` belongs to the
specified file system
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
return super_fstype(super_block) == name
[docs]def is_fstype_inode(inode: gdb.Value, name: str) -> bool:
"""
Tests whether the inode belongs to a particular file system type.
Args:
inode: The struct inode to test. The value must be of
type ``struct inode``.
name: The name of the file system type
Returns:
:obj:`bool`: whether the inode belongs to the specified file system
Raises:
:obj:`gdb.NotAvailableError`: The target value was not available.
"""
return is_fstype_super(inode['i_sb'], name)