77import stat
88import struct
99from bisect import bisect_right
10- from datetime import datetime
1110from functools import cache , cached_property , lru_cache
12- from typing import BinaryIO , Iterator , Optional , Union
11+ from typing import TYPE_CHECKING , BinaryIO
1312
1413from dissect .util import ts
1514from dissect .util .stream import RunlistStream
2322 NotASymlinkError ,
2423)
2524
25+ if TYPE_CHECKING :
26+ from collections .abc import Iterator
27+ from datetime import datetime
28+
2629
2730class SquashFS :
2831 def __init__ (self , fh : BinaryIO ):
@@ -68,15 +71,15 @@ def inode(
6871 self ,
6972 block : int ,
7073 offset : int ,
71- name : Optional [ str ] = None ,
72- type : Optional [ int ] = None ,
73- inode_number : Optional [ int ] = None ,
74- parent : Optional [ INode ] = None ,
74+ name : str | None = None ,
75+ type : int | None = None ,
76+ inode_number : int | None = None ,
77+ parent : INode | None = None ,
7578 ) -> INode :
7679 # squashfs inode numbers consist of a block number and offset in that block
7780 return INode (self , block , offset , name , type , inode_number , parent )
7881
79- def get (self , path : Union [ str , int ] , node : Optional [ INode ] = None ) -> INode :
82+ def get (self , path : str | int , node : INode | None = None ) -> INode :
8083 if isinstance (path , int ):
8184 return self .inode (path >> 16 , path & 0xFFFF )
8285
@@ -135,7 +138,7 @@ def _read_metadata(self, block: int, offset: int, length: int) -> tuple[int, int
135138
136139 return block , offset , b"" .join (result )
137140
138- def _read_block (self , block : int , length : Optional [ int ] = None ) -> tuple [int , bytes ]:
141+ def _read_block (self , block : int , length : int | None = None ) -> tuple [int , bytes ]:
139142 if length is not None :
140143 # Data block
141144 compressed = length & c_squashfs .SQUASHFS_COMPRESSED_BIT_BLOCK == 0
@@ -177,13 +180,12 @@ def _lookup_inode(self, inode_number: int) -> INode:
177180 _ , _ , data = self ._read_metadata (self .lookup_table [block ], offset , 8 )
178181 return self .get (struct .unpack ("<Q" , data )[0 ])
179182
180- def _lookup_fragment (self , fragment : int ) -> bytes :
183+ def _lookup_fragment (self , fragment : int ) -> c_squashfs . squashfs_fragment_entry :
181184 fragment_offset = fragment * len (c_squashfs .squashfs_fragment_entry )
182185 block , offset = divmod (fragment_offset , c_squashfs .SQUASHFS_METADATA_SIZE )
183186
184187 _ , _ , data = self ._read_metadata (self .fragment_table [block ], offset , len (c_squashfs .squashfs_fragment_entry ))
185- entry = c_squashfs .squashfs_fragment_entry (data )
186- return entry
188+ return c_squashfs .squashfs_fragment_entry (data )
187189
188190 def iter_inodes (self ) -> Iterator [INode ]:
189191 for inum in range (1 , self .sb .inodes + 1 ):
@@ -196,10 +198,10 @@ def __init__(
196198 fs : SquashFS ,
197199 block : int ,
198200 offset : int ,
199- name : Optional [ str ] = None ,
200- type : Optional [ int ] = None ,
201- inode_number : Optional [ int ] = None ,
202- parent : Optional [ INode ] = None ,
201+ name : str | None = None ,
202+ type : int | None = None ,
203+ inode_number : int | None = None ,
204+ parent : INode | None = None ,
203205 ):
204206 self .fs = fs
205207 self .block = block
@@ -209,6 +211,8 @@ def __init__(
209211 self ._inode_number = inode_number
210212 self .parent = parent
211213
214+ self .listdir = cache (self .listdir )
215+
212216 def __repr__ (self ) -> str :
213217 return f"<inode { self .inode_number } ({ self .block } , { self .offset } )>"
214218
@@ -220,15 +224,9 @@ def _metadata(
220224 | c_squashfs .squashfs_reg_inode_header
221225 | c_squashfs .squashfs_symlink_inode_header
222226 | c_squashfs .squashfs_dev_inode_header
223- | c_squashfs .squashfs_dev_inode_header
224- | c_squashfs .squashfs_base_inode_header
225- | c_squashfs .squashfs_base_inode_header
226227 | c_squashfs .squashfs_ldir_inode_header
227228 | c_squashfs .squashfs_lreg_inode_header
228- | c_squashfs .squashfs_symlink_inode_header
229- | c_squashfs .squashfs_ldev_inode_header
230229 | c_squashfs .squashfs_ldev_inode_header
231- | c_squashfs .squashfs_lipc_inode_header
232230 | c_squashfs .squashfs_lipc_inode_header ,
233231 int ,
234232 int ,
@@ -262,16 +260,10 @@ def header(
262260 | c_squashfs .squashfs_reg_inode_header
263261 | c_squashfs .squashfs_symlink_inode_header
264262 | c_squashfs .squashfs_dev_inode_header
265- | c_squashfs .squashfs_dev_inode_header
266- | c_squashfs .squashfs_base_inode_header
267- | c_squashfs .squashfs_base_inode_header
268263 | c_squashfs .squashfs_ldir_inode_header
269264 | c_squashfs .squashfs_lreg_inode_header
270- | c_squashfs .squashfs_symlink_inode_header
271- | c_squashfs .squashfs_ldev_inode_header
272265 | c_squashfs .squashfs_ldev_inode_header
273266 | c_squashfs .squashfs_lipc_inode_header
274- | c_squashfs .squashfs_lipc_inode_header
275267 ):
276268 header , _ , _ = self ._metadata ()
277269 return header
@@ -315,11 +307,12 @@ def mtime(self) -> datetime:
315307 return ts .from_unix (self .header .mtime )
316308
317309 @property
318- def size (self ) -> Optional [ int ] :
310+ def size (self ) -> int | None :
319311 if self .is_dir () or self .is_file ():
320312 return self .header .file_size
321- elif self .is_symlink ():
313+ if self .is_symlink ():
322314 return self .header .symlink_size
315+ return None
323316
324317 def is_dir (self ) -> bool :
325318 return self .type == stat .S_IFDIR
@@ -363,13 +356,9 @@ def link(self) -> str:
363356 @cached_property
364357 def link_inode (self ) -> INode :
365358 link = self .link
366- if link .startswith ("/" ):
367- relnode = None
368- else :
369- relnode = self .parent
359+ relnode = None if link .startswith ("/" ) else self .parent
370360 return self .fs .get (self .link , relnode )
371361
372- @cache
373362 def listdir (self ) -> dict [str , INode ]:
374363 return {inode .name : inode for inode in self .iterdir ()}
375364
@@ -407,7 +396,7 @@ def iterdir(self) -> Iterator[INode]:
407396 )
408397
409398 @cached_property
410- def block_list (self ) -> list [tuple [Optional [ int ] , int ]]:
399+ def block_list (self ) -> list [tuple [int | None , int ]]:
411400 fragment = self .header .fragment
412401 file_size = self .header .file_size
413402 if fragment == c_squashfs .SQUASHFS_INVALID_FRAG :
0 commit comments