2525 from collections .abc import Iterator
2626 from datetime import datetime
2727
28+ from dissect .apfs .objects .btree_node import BTreeNode
2829 from dissect .apfs .objects .keybag import VolumeKeybag
2930
3031
@@ -151,7 +152,7 @@ def is_onekey(self) -> bool:
151152 def formatted_by (self ) -> tuple [str , datetime , int ]:
152153 """Information about the tool that formatted the filesystem."""
153154 return (
154- self .object .apfs_formatted_by .id .decode (). split ("\x00 " , 1 )[0 ],
155+ self .object .apfs_formatted_by .id .split (b "\x00 " , 1 )[0 ]. decode () ,
155156 from_unix (self .object .apfs_formatted_by .timestamp ),
156157 self .object .apfs_formatted_by .last_xid ,
157158 )
@@ -166,7 +167,7 @@ def modified_by(self) -> list[tuple[str, datetime, int]]:
166167
167168 result .append (
168169 (
169- entry .id .decode (). split ("\x00 " , 1 )[0 ],
170+ entry .id .split (b "\x00 " , 1 )[0 ]. decode () ,
170171 from_unix (entry .timestamp ),
171172 entry .last_xid ,
172173 )
@@ -224,7 +225,7 @@ def unlock(self, password: str, uuid: UUID | str | None = None) -> None:
224225 raise Error ("No VEK found for this volume" )
225226
226227 for kek in self .keybag .keks ():
227- if uuid is not None and str (kek .uuid ) != uuid :
228+ if uuid is not None and str (kek .uuid ) != str ( uuid ) :
228229 continue
229230
230231 if not kek .verify ():
@@ -245,7 +246,7 @@ def cursor(self) -> Cursor:
245246 """Create a new cursor for the volume's root B-tree."""
246247 return Cursor (self .root_tree , self .omap , self .object .apfs_root_tree_oid if self .is_sealed else 0 , self .xid )
247248
248- def _cursor_state (self , oid : int ) -> Any :
249+ def _cursor_state (self , oid : int ) -> tuple [ BTreeNode , int , list [ tuple [ BTreeNode , int ]]] :
249250 """Precompute the cursor state for a given object ID.
250251
251252 Args:
@@ -308,6 +309,7 @@ def inode(self, oid: int | str, sibling_id: int | None = None) -> INode:
308309
309310 Args:
310311 oid: The object ID of the inode to retrieve.
312+ sibling_id: The sibling ID of the inode to retrieve, if applicable.
311313 """
312314 if isinstance (oid , str ):
313315 if ":" not in oid :
@@ -321,12 +323,10 @@ def inodes(self) -> Iterator[INode]:
321323
322324 for key , value in cursor .walk ():
323325 oid , type = parse_fs_object_key (key )
324- if type != c_apfs .APFS_TYPE .INODE :
325- continue
326-
327- inode = self .inode (oid )
328- inode ._inode_raw = value
329- yield inode
326+ if type == c_apfs .APFS_TYPE .INODE :
327+ inode = self .inode (oid )
328+ inode ._inode_raw = value
329+ yield inode
330330
331331 def get (self , path : str | int | DirectoryEntry , node : INode | None = None ) -> INode :
332332 """Get an inode by its path, object ID, or directory entry.
@@ -406,6 +406,7 @@ class INode:
406406 Args:
407407 volume: Parent APFS volume.
408408 oid: The object ID of the inode.
409+ sibling_id: The sibling ID of the inode, if applicable.
409410 """
410411
411412 def __init__ (self , volume : FS , oid : int , sibling_id : int | None = None ):
@@ -476,10 +477,7 @@ def parent(self) -> INode:
476477 def parents (self ) -> Iterator [INode ]:
477478 """Iterate over the parent inodes of this inode, up to the root."""
478479 obj = self
479- while True :
480- if obj .parent is obj :
481- break
482-
480+ while obj .parent is not obj :
483481 obj = obj .parent
484482 yield obj
485483
@@ -607,7 +605,7 @@ def siblings(self) -> list[INode]:
607605
608606 @cached_property
609607 def sibling_link (self ) -> tuple [int , str ] | None :
610- """The sibling link (``parent_id``, ``name`` tuple) of this inode, if available."""
608+ """The sibling link (``parent_id``, ``name``) tuple of this inode, if available."""
611609 if self .sibling_id is not None :
612610 for _ , key , value in self .volume ._records (self .oid , c_apfs .APFS_TYPE .SIBLING_LINK ):
613611 if c_apfs .j_sibling_key (key ).sibling_id == self .sibling_id :
@@ -637,7 +635,7 @@ def names(self) -> list[str]:
637635 def path (self ) -> str :
638636 """The full path of this inode, if available."""
639637 parts = [self .name or f"<unlinked:{ self .oid } >" ]
640- parts .extend (parent .name or f"<unlinked:{ self .oid } >" for parent in self .parents )
638+ parts .extend (parent .name or f"<unlinked:{ parent .oid } >" for parent in self .parents )
641639
642640 return "/" + "/" .join (parts [::- 1 ])
643641
@@ -689,7 +687,7 @@ def readlink(self) -> str:
689687
690688 return self .xattr [c_apfs .SYMLINK_EA_NAME ].open ().read ().decode ().rstrip ("\x00 " )
691689
692- def open (self ) -> FileStream :
690+ def open (self ) -> BufferedStream | DecmpfsStream | FileStream :
693691 """Open a stream for reading the inode data."""
694692 if self .is_compressed ():
695693 return DecmpfsStream (self )
0 commit comments