def test_hlist_for_each_entry(self): self.assertEqual( list( hlist_for_each_entry("struct drgn_test_hlist_entry", self.empty, "node")), [], ) self.assertEqual( list( hlist_for_each_entry("struct drgn_test_hlist_entry", self.full, "node")), [self.entry(i) for i in range(self.num_entries)], )
def find_user(prog: Program, uid: Union[Object, IntegerLike]) -> Object: """ Return the user structure with the given UID. :param uid: ``kuid_t`` object or integer. :return: ``struct user_state *`` """ try: uidhashentry = prog.cache["uidhashentry"] except KeyError: uidhash_table = prog["uidhash_table"] uidhash_sz = len(uidhash_table) uidhash_bits = uidhash_sz.bit_length() - 1 uidhash_mask = uidhash_sz - 1 def uidhashentry(uid: int) -> Object: hash = ((uid >> uidhash_bits) + uid) & uidhash_mask return uidhash_table + hash prog.cache["uidhashentry"] = uidhashentry uid = _kuid_val(uid) for user in hlist_for_each_entry("struct user_struct", uidhashentry(uid), "uidhash_node"): if user.uid.val == uid: return user return NULL(prog, "struct user_struct *")
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 _call(self, objs: Iterable[drgn.Object]) -> Iterable[drgn.Object]: sname = get_valid_struct_name(self, self.args.struct_name) for obj in objs: try: yield from hlist_for_each_entry(sname, obj, self.args.member) except LookupError as err: raise sdb.CommandError(self.name, str(err))
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 find_user(prog, uid): """ .. c:function:: struct user_struct *find_user(kuid_t uid) Return the user structure with the given UID, which may be a ``kuid_t`` or an integer. """ try: uidhashentry = prog.cache["uidhashentry"] except KeyError: uidhash_table = prog["uidhash_table"] uidhash_sz = len(uidhash_table) uidhash_bits = uidhash_sz.bit_length() - 1 uidhash_mask = uidhash_sz - 1 def uidhashentry(uid): hash = ((uid >> uidhash_bits) + uid) & uidhash_mask return uidhash_table + hash prog.cache["uidhashentry"] = uidhashentry uid = _kuid_val(uid) for user in hlist_for_each_entry( "struct user_struct", uidhashentry(uid), "uidhash_node" ): if user.uid.val == uid: return user return NULL(prog, "struct user_struct *")
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 inode_paths(inode: Object) -> Iterator[bytes]: """ Return an iterator over all of the paths of an inode from the root of its filesystem. :param inode: ``struct inode *`` """ return (dentry_path(dentry) for dentry in hlist_for_each_entry( "struct dentry", inode.i_dentry.address_of_(), "d_u.d_alias"))
def for_each_user(prog: Program) -> Iterator[Object]: """ Iterate over all users in the system. :return: Iterator of ``struct user_struct *`` objects. """ for hash_entry in prog["uidhash_table"]: for user in hlist_for_each_entry("struct user_struct", hash_entry, "uidhash_node"): yield user
def inode_paths(inode): """ .. c:function:: inode_paths(struct inode *inode) Return an iterator over all of the paths of an inode from the root of its filesystem. :rtype: Iterator[bytes] """ return (dentry_path(dentry) for dentry in hlist_for_each_entry( 'struct dentry', inode.i_dentry.address_of_(), 'd_u.d_alias'))
def qdisc_lookup(dev: Object, major: IntegerLike) -> Object: """ Get a Qdisc from a device and a major handle number. It is worth noting that conventionally handles are hexadecimal, e.g. ``10:`` in a ``tc`` command means major handle 0x10. :param dev: ``struct net_device *`` :param major: Qdisc major handle number. :return: ``struct Qdisc *`` (``NULL`` if not found) """ major = operator.index(major) << 16 roots = [dev.qdisc] if dev.ingress_queue: roots.append(dev.ingress_queue.qdisc_sleeping) # Since Linux kernel commit 59cc1f61f09c ("net: sched: convert qdisc linked # list to hashtable") (in v4.7), a device's child Qdiscs are maintained in # a hashtable in its struct net_device. Before that, they are maintained in # a linked list in their root Qdisc. use_hashtable = dev.prog_.type("struct net_device").has_member( "qdisc_hash") for root in roots: if root.handle == major: return root if use_hashtable: for head in root.dev_queue.dev.qdisc_hash: for qdisc in hlist_for_each_entry("struct Qdisc", head.address_of_(), "hash"): if qdisc.handle == major: return qdisc else: for qdisc in list_for_each_entry("struct Qdisc", root.list.address_of_(), "list"): if qdisc.handle == major: return qdisc return NULL(dev.prog_, "struct Qdisc *")
def netdev_get_by_name(prog_or_net: Union[Program, Object], name: Union[str, bytes]) -> Object: """ Get the network device with the given interface name. :param prog_or_net: ``struct net *`` containing the device, or :class:`Program` to use the initial network namespace. :param name: Network interface name. :return: ``struct net_device *`` (``NULL`` if not found) """ if isinstance(prog_or_net, Program): prog_or_net = prog_or_net["init_net"] if isinstance(name, str): name = name.encode() # Since Linux kernel commit ff92741270bf ("net: introduce name_node struct # to be used in hashlist") (in v5.5), the device name hash table contains # struct netdev_name_node entries. Before that, it contained the struct # net_device directly. try: entry_type = prog_or_net.prog_.type("struct netdev_name_node") member = "hlist" entry_is_name_node = True except LookupError: entry_type = prog_or_net.prog_.type("struct net_device") member = "name_hlist" entry_is_name_node = False for i in range(_NETDEV_HASHENTRIES): head = prog_or_net.dev_name_head[i] for entry in hlist_for_each_entry(entry_type, head, member): if entry.name.string_() == name: if entry_is_name_node: return entry.dev else: return entry return NULL(prog_or_net.prog_, "struct net_device *")
def netdev_get_by_index(prog_or_net: Union[Program, Object], ifindex: IntegerLike) -> Object: """ Get the network device with the given interface index number. :param prog_or_net: ``struct net *`` containing the device, or :class:`Program` to use the initial network namespace. :param ifindex: Network interface index number. :return: ``struct net_device *`` (``NULL`` if not found) """ if isinstance(prog_or_net, Program): prog_or_net = prog_or_net["init_net"] if isinstance(ifindex, Object): ifindex = ifindex.read_() head = prog_or_net.dev_index_head[operator.index(ifindex) & (_NETDEV_HASHENTRIES - 1)] for netdev in hlist_for_each_entry("struct net_device", head, "index_hlist"): if netdev.ifindex == ifindex: return netdev return NULL(prog_or_net.prog_, "struct net_device *")
def walk(self, obj: drgn.Object) -> Iterable[drgn.Object]: yield from hlist_for_each_entry(" ".join(self.args.type), obj, self.args.member)