def for_each_disk(prog: Program) -> Iterator[Object]: """ Iterate over all disks in the system. :return: Iterator of ``struct gendisk *`` objects. """ # Before Linux kernel commit 0d02129e76ed ("block: merge struct # block_device and struct hd_struct") (in v5.11), partition devices are in # struct hd_struct::__dev. After that commit, they are in struct # block_device::bd_device. We start by assuming that the kernel has this # commit and fall back to the old path if that fails. have_bd_device = True for device in _for_each_block_device(prog): if have_bd_device: try: bdev = container_of(device, "struct block_device", "bd_device") except LookupError: have_bd_device = False else: if bdev.bd_partno == 0: yield bdev.bd_disk continue part = container_of(device, "struct hd_struct", "__dev") if part.partno == 0: yield container_of(part, "struct gendisk", "part0")
def css_next_child(pos: Object, parent: Object) -> Object: """ Get the next child (or ``NULL`` if there is none) of the given parent starting from the given position (``NULL`` to initiate traversal). :param pos: ``struct cgroup_subsys_state *`` :param parent: ``struct cgroup_subsys_state *`` :return: ``struct cgroup_subsys_state *`` """ if not pos: next_ = container_of( parent.children.next, "struct cgroup_subsys_state", "sibling" ) elif not (pos.flags & pos.prog_["CSS_RELEASED"]): next_ = container_of(pos.sibling.next, "struct cgroup_subsys_state", "sibling") else: serial_nr = pos.serial_nr.value_() # Read once and cache. for next_ in list_for_each_entry( "struct cgroup_subsys_state", parent.children.address_of_(), "sibling" ): if next_.serial_nr > serial_nr: break if next_.sibling.address_of_() != parent.children.address_of_(): return next_ return NULL(next_.prog_, "struct cgroup_subsys_state *")
def print_auxiliary_device(device): auxiliary_device = container_of(device, "struct auxiliary_device", "dev") # print(auxiliary_device.id) driver_data = auxiliary_device.dev.driver_data print("driver_data %x" % driver_data) mlx5_adev = container_of(auxiliary_device, "struct mlx5_adev", "adev") # print(mlx5_adev) # print("%x" % mlx5_adev.idx) # print("%x" % mlx5_adev.mdev) print("mlx5_core_dev device name: %-20s" % mlx5_adev.mdev.device.kobj.name.string_().decode()) print('')
def list_prev_entry(pos, member): """ .. c:function:: type *list_prev_entry(type *pos, member) Return the previous entry in a list. """ return container_of(getattr(pos, member).prev, pos.type_.type, member)
def list_next_entry(pos, member): """ .. c:function:: type *list_next_entry(type *pos, member) Return the next entry in a list. """ return container_of(getattr(pos, member).next, pos.type_.type, member)
def for_each_pid(prog_or_ns: Union[Program, Object]) -> Iterator[Object]: """ Iterate over all PIDs in a namespace. :param prog_or_ns: ``struct pid_namespace *`` to iterate over, or :class:`Program` to iterate over initial PID namespace. :return: Iterator of ``struct pid *`` objects. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns["init_pid_ns"].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, "idr"): for nr, entry in idr_for_each(ns.idr): yield cast("struct pid *", entry) else: pid_hash = prog["pid_hash"] for i in range(1 << prog["pidhash_shift"].value_()): for upid in hlist_for_each_entry("struct upid", pid_hash[i].address_of_(), "pid_chain"): if upid.ns == ns: yield container_of(upid, "struct pid", f"numbers[{int(ns.level)}]")
def for_each_pid(prog_or_ns): """ .. c:function:: for_each_pid(struct pid_namespace *ns) Iterate over all of the PIDs in the given namespace. If given a :class:`Program` instead, the initial PID namespace is used. :return: Iterator of ``struct pid *`` objects. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns['init_pid_ns'].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, 'idr'): for nr, entry in idr_for_each(ns.idr): yield cast('struct pid *', entry) else: pid_hash = prog['pid_hash'] for i in range(1 << prog['pidhash_shift'].value_()): for upid in hlist_for_each_entry('struct upid', pid_hash[i].address_of_(), 'pid_chain'): if upid.ns == ns: yield container_of(upid, 'struct pid', f'numbers[{int(ns.level)}]')
def hash(rhashtable, type, member): nodes = [] tbl = rhashtable.tbl # print('') # print("rhashtable %lx" % rhashtable.address_of_()) # print("bucket_table %lx" % tbl) # buckets = tbl.buckets # print("buckets %lx" % buckets.address_of_()) buckets = tbl.buckets size = tbl.size.value_() print("") for i in range(size): rhash_head = buckets[i] if type_exist("struct rhash_lock_head"): rhash_head = cast("struct rhash_head *", rhash_head) if rhash_head.value_() == 0: continue while True: if rhash_head.value_() & 1: break obj = container_of(rhash_head, type, member) nodes.append(obj) rhash_head = rhash_head.next return nodes
def find_pid(prog_or_ns, nr): """ .. c:function:: struct pid *find_pid(struct pid_namespace *ns, int nr) Return the ``struct pid *`` for the given PID number in the given namespace. If given a :class:`Program` instead, the initial PID namespace is used. """ if isinstance(prog_or_ns, Program): prog = prog_or_ns ns = prog_or_ns['init_pid_ns'].address_of_() else: prog = prog_or_ns.prog_ ns = prog_or_ns if hasattr(ns, 'idr'): return cast('struct pid *', idr_find(ns.idr, nr)) else: # We could implement pid_hashfn() and only search that bucket, but it's # different for 32-bit and 64-bit systems, and it has changed at least # once, in v4.7. Searching the whole hash table is slower but # foolproof. pid_hash = prog['pid_hash'] for i in range(1 << prog['pidhash_shift'].value_()): for upid in hlist_for_each_entry('struct upid', pid_hash[i].address_of_(), 'pid_chain'): if upid.nr == nr and upid.ns == ns: return container_of(upid, 'struct pid', f'numbers[{ns.level.value_()}]') return NULL(prog, 'struct pid *')
def get_mlx5_core_devs(): devs = {} bus_type = prog["pci_bus_type"] subsys_private = bus_type.p k_list = subsys_private.klist_devices.k_list for dev in list_for_each_entry('struct device_private', k_list.address_of_(), 'knode_bus.n_node'): addr = dev.value_() device_private = Object(prog, 'struct device_private', address=addr) device = device_private.device # struct pci_dev { # struct device dev; # } pci_dev = container_of(device, "struct pci_dev", "dev") driver_data = device.driver_data mlx5_core = Object(prog, 'struct mlx5_core_dev', address=driver_data) driver = device.driver if driver_data.value_(): name = driver.name.string_().decode() if name == "mlx5_core": pci_name = device.kobj.name.string_().decode() index = pci_name.split('.')[1] devs[int(index)] = mlx5_core return devs
def rb_find( type: Union[str, Type], root: Object, member: str, key: KeyType, cmp: Callable[[KeyType, Object], int], ) -> Object: """ Find an entry in a red-black tree given a key and a comparator function. Note that this function does not have an analogue in the Linux kernel source code, as tree searches are all open-coded. :param type: Entry type. :param root: ``struct rb_root *`` :param member: Name of ``struct rb_node`` member in entry type. :param key: Key to find. :param cmp: Callback taking key and entry that returns < 0 if the key is less than the entry, > 0 if the key is greater than the entry, and 0 if the key matches the entry. :return: ``type *`` found entry, or ``NULL`` if not found. """ prog = root.prog_ type = prog.type(type) node = root.rb_node.read_() while node: entry = container_of(node, type, member) ret = cmp(key, entry) if ret < 0: node = node.rb_left.read_() elif ret > 0: node = node.rb_right.read_() else: return entry return NULL(prog, prog.pointer_type(type))
def print_sock(nsock): print("\tportid : %x" % nsock.portid) # print("\tdst_portid: %x" % nsock.dst_portid) # all 0 sock = nsock.sk print("\tsock %lx" % sock.address_of_().value_()) head = sock.sk_wq.wait.head print("\tsk_data_ready: %s" % lib.address_to_name(hex(sock.sk_data_ready))) print("") # print(head) if 1: return for entry in list_for_each_entry('struct wait_queue_entry', head.address_of_(), 'entry'): print("\twait_queue_entry %lx" % entry.value_()) print("\twait_queue_entry.flags %d" % entry.flags.value_()) eppoll_entry = container_of(entry, "struct eppoll_entry", 'wait') print("\teppoll_entry %lx" % eppoll_entry) epitem = eppoll_entry.base print("\tepitem %lx" % epitem) event = epitem.event ffd = epitem.ffd print("\tepoll_filefd %lx" % ffd.address_of_().value_()) if entry.flags.value_(): print("\tfd: %d" % ffd.fd.value_()) print("\tfile: %lx" % ffd.file) # 10000019 # EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLWAKEUP print("\tevents: %x" % event.events) print("\tdata: %x" % event.data) func = entry.func print("\t%s" % lib.address_to_name(hex(func))) print("")
def d_path( # type: ignore # Need positional-only arguments. path_or_vfsmnt: Object, dentry: Optional[Object] = None) -> bytes: if dentry is None: vfsmnt = path_or_vfsmnt.mnt dentry = path_or_vfsmnt.dentry.read_() else: vfsmnt = path_or_vfsmnt dentry = dentry.read_() mnt = container_of(vfsmnt, "struct mount", "mnt") d_op = dentry.d_op.read_() if d_op and d_op.d_dname: return b"[" + dentry.d_inode.i_sb.s_type.name.string_() + b"]" components = [] while True: if dentry == mnt.mnt.mnt_root: mnt_parent = mnt.mnt_parent.read_() if mnt == mnt_parent: break dentry = mnt.mnt_mountpoint.read_() mnt = mnt_parent continue d_parent = dentry.d_parent.read_() if dentry == d_parent: break components.append(dentry.d_name.name.string_()) components.append(b"/") dentry = d_parent if components: return b"".join(reversed(components)) else: return b"/"
def path_lookup(prog_or_root, path, allow_negative=False): """ .. c:function:: struct path path_lookup(struct path *root, const char *path, bool allow_negative) Look up the given path name relative to the given root directory. If given a :class:`Program` instead of a ``struct path``, the initial root filesystem is used. :param bool allow_negative: Whether to allow returning a negative dentry (i.e., a dentry for a non-existent path). :raises Exception: if the dentry is negative and ``allow_negative`` is ``False``, or if the path is not present in the dcache. The latter does not necessarily mean that the path does not exist; it may be uncached. On a live system, you can make the kernel cache the path by accessing it (e.g., with :func:`open()` or :func:`os.stat()`): >>> path_lookup(prog, '/usr/include/stdlib.h') ... Exception: could not find '/usr/include/stdlib.h' in dcache >>> open('/usr/include/stdlib.h').close() >>> path_lookup(prog, '/usr/include/stdlib.h') (struct path){ .mnt = (struct vfsmount *)0xffff8b70413cdca0, .dentry = (struct dentry *)0xffff8b702ac2c480, } """ if isinstance(prog_or_root, Program): prog_or_root = prog_or_root["init_task"].fs.root mnt = root_mnt = container_of(prog_or_root.mnt.read_(), "struct mount", "mnt") dentry = root_dentry = prog_or_root.dentry.read_() components = os.fsencode(path).split(b"/") for i, component in enumerate(components): if component == b"" or component == b".": continue elif component == b"..": mnt, dentry = _follow_dotdot(mnt, dentry, root_mnt, root_dentry) else: for child in list_for_each_entry("struct dentry", dentry.d_subdirs.address_of_(), "d_child"): if child.d_name.name.string_() == component: dentry = child break else: failed_path = os.fsdecode(b"/".join(components[:i + 1])) raise Exception(f"could not find {failed_path!r} in dcache") mnt, dentry = _follow_mount(mnt, dentry) if not allow_negative and not dentry.d_inode: failed_path = os.fsdecode(b"/".join(components)) raise Exception(f"{failed_path!r} dentry is negative") return Object( mnt.prog_, "struct path", value={ "mnt": mnt.mnt.address_of_(), "dentry": dentry, }, )
def for_each_partition(prog: Program) -> Iterator[Object]: """ Iterate over all partitions in the system. :return: Iterator of ``struct hd_struct *`` objects. """ for device in _for_each_block_device(prog): yield container_of(device, "struct hd_struct", "__dev")
def for_each_partition(prog): """ Iterate over all partitions in the system. :return: Iterator of ``struct hd_struct *`` objects. """ for device in _for_each_block_device(prog): yield container_of(device, 'struct hd_struct', '__dev')
def for_each_partition(prog: Program) -> Iterator[Object]: """ Iterate over all partitions in the system. :return: Iterator of ``struct block_device *`` or ``struct hd_struct *`` objects depending on the kernel version. """ # See the comment in for_each_disk(). have_bd_device = True for device in _for_each_block_device(prog): if have_bd_device: try: yield container_of(device, "struct block_device", "bd_device") continue except LookupError: have_bd_device = False yield container_of(device, "struct hd_struct", "__dev")
def _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: sname = get_valid_struct_name(self, self.args.struct_name) for obj in objs: try: container_obj = drgn.container_of(obj, sname, self.args.member) except (TypeError, LookupError) as err: raise sdb.CommandError(self.name, str(err)) yield container_obj
def find_memcg_ids(css=prog['root_mem_cgroup'].css, prefix=''): if not list_empty(css.children.address_of_()): for css in list_for_each_entry('struct cgroup_subsys_state', css.children.address_of_(), 'sibling'): name = prefix + '/' + css.cgroup.kn.name.string_().decode('utf-8') memcg = container_of(css, 'struct mem_cgroup', 'css') MEMCGS[css.cgroup.kn.id.value_()] = memcg find_memcg_ids(css, name)
def pid_task(pid, pid_type): """ .. c:function:: struct task_struct *pid_task(struct pid *pid, enum pid_type pid_type) Return the ``struct task_struct *`` containing the given ``struct pid *`` of the given type. """ if not pid: return NULL(pid.prog_, 'struct task_struct *') first = pid.tasks[0].first if not first: return NULL(pid.prog_, 'struct task_struct *') try: return container_of(first, 'struct task_struct', f'pid_links[{int(pid_type)}]') except LookupError: return container_of(first, 'struct task_struct', f'pids[{int(pid_type)}].node')
def list_prev_entry(pos: Object, member: str) -> Object: """ Return the previous entry in a list. :param pos: ``type*`` :param member: Name of list node member in entry type. :return: ``type *`` """ return container_of(getattr(pos, member).prev, pos.type_.type, member)
def SOCK_INODE(sock: Object) -> Object: """ Get the inode of a socket. :param sock: ``struct socket *`` :return: ``struct inode *`` """ return container_of(sock, "struct socket_alloc", "socket").vfs_inode.address_of_()
def hlist_nulls_entry(pos, type, member): """ .. c:function:: type *hlist_nulls_entry(struct hlist_nulls_node *pos, type, member) Return an entry in a nulls hash list. The nulls hash list is assumed to be non-empty. """ return container_of(pos, type, member)
def list_last_entry(head, type, member): """ .. c:function:: type *list_last_entry(struct list_head *head, type, member) Return the last entry in a list. The list is assumed to be non-empty. """ return container_of(head.prev, type, member)
def for_each_disk(prog): """ Iterate over all disks in the system. :return: Iterator of ``struct gendisk *`` objects. """ disk_type = prog['disk_type'].address_of_() for device in _for_each_block_device(prog): if device.type == disk_type: yield container_of(device, 'struct gendisk', 'part0.__dev')
def inode_path(inode): """ .. c:function:: char *inode_path(struct inode *inode) Return any path of an inode from the root of its filesystem. """ if hlist_empty(inode.i_dentry): return None return dentry_path( container_of(inode.i_dentry.first, 'struct dentry', 'd_u.d_alias'))
def for_each_partition(prog): """ Iterate over all partitions in the system. :return: Iterator of ``struct hd_struct *`` objects. """ devices = prog['block_class'].p.klist_devices.k_list.address_of_() for device in list_for_each_entry('struct device', devices, 'knode_class.n_node'): yield container_of(device, 'struct hd_struct', '__dev')
def for_each_disk(prog: Program) -> Iterator[Object]: """ Iterate over all disks in the system. :return: Iterator of ``struct gendisk *`` objects. """ disk_type = prog["disk_type"].address_of_() for device in _for_each_block_device(prog): if device.type == disk_type: yield container_of(device, "struct gendisk", "part0.__dev")
def print_hmap(hmap_addr, struct_name, member): objs = [] buckets = hmap_addr.buckets.value_() n = hmap_addr.n.value_() if n == 0: return objs # print("\n=== %s: buckets: %x, n: %d ===" % (struct_name, buckets, n)) i = 0 while 1: p = Object(prog, 'void *', address=buckets) if p.value_() == 0: buckets = buckets + 8 continue data = container_of(p, "struct " + struct_name, member) objs.append(data) i += 1 if i == n: return objs next = data.member_(member).next while next.value_() != 0: data = container_of(next, "struct " + struct_name, member) objs.append(data) i += 1 if i == n: return objs next = data.member_(member).next buckets = buckets + 8 return objs
def list_first_entry(head, type, member): """ .. c:function:: type *list_first_entry(struct list_head *head, type, member) Return the first entry in a list. The list is assumed to be non-empty. See also :func:`list_first_entry_or_null()`. """ return container_of(head.next, type, member)