7
7
import stat
8
8
import struct
9
9
from bisect import bisect_right
10
- from datetime import datetime
11
10
from functools import cache , cached_property , lru_cache
12
- from typing import BinaryIO , Iterator , Optional , Union
11
+ from typing import TYPE_CHECKING , BinaryIO
13
12
14
13
from dissect .util import ts
15
14
from dissect .util .stream import RunlistStream
23
22
NotASymlinkError ,
24
23
)
25
24
25
+ if TYPE_CHECKING :
26
+ from collections .abc import Iterator
27
+ from datetime import datetime
28
+
26
29
27
30
class SquashFS :
28
31
def __init__ (self , fh : BinaryIO ):
@@ -68,15 +71,15 @@ def inode(
68
71
self ,
69
72
block : int ,
70
73
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 ,
75
78
) -> INode :
76
79
# squashfs inode numbers consist of a block number and offset in that block
77
80
return INode (self , block , offset , name , type , inode_number , parent )
78
81
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 :
80
83
if isinstance (path , int ):
81
84
return self .inode (path >> 16 , path & 0xFFFF )
82
85
@@ -135,7 +138,7 @@ def _read_metadata(self, block: int, offset: int, length: int) -> tuple[int, int
135
138
136
139
return block , offset , b"" .join (result )
137
140
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 ]:
139
142
if length is not None :
140
143
# Data block
141
144
compressed = length & c_squashfs .SQUASHFS_COMPRESSED_BIT_BLOCK == 0
@@ -177,13 +180,12 @@ def _lookup_inode(self, inode_number: int) -> INode:
177
180
_ , _ , data = self ._read_metadata (self .lookup_table [block ], offset , 8 )
178
181
return self .get (struct .unpack ("<Q" , data )[0 ])
179
182
180
- def _lookup_fragment (self , fragment : int ) -> bytes :
183
+ def _lookup_fragment (self , fragment : int ) -> c_squashfs . squashfs_fragment_entry :
181
184
fragment_offset = fragment * len (c_squashfs .squashfs_fragment_entry )
182
185
block , offset = divmod (fragment_offset , c_squashfs .SQUASHFS_METADATA_SIZE )
183
186
184
187
_ , _ , 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 )
187
189
188
190
def iter_inodes (self ) -> Iterator [INode ]:
189
191
for inum in range (1 , self .sb .inodes + 1 ):
@@ -196,10 +198,10 @@ def __init__(
196
198
fs : SquashFS ,
197
199
block : int ,
198
200
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 ,
203
205
):
204
206
self .fs = fs
205
207
self .block = block
@@ -209,6 +211,8 @@ def __init__(
209
211
self ._inode_number = inode_number
210
212
self .parent = parent
211
213
214
+ self .listdir = cache (self .listdir )
215
+
212
216
def __repr__ (self ) -> str :
213
217
return f"<inode { self .inode_number } ({ self .block } , { self .offset } )>"
214
218
@@ -220,15 +224,9 @@ def _metadata(
220
224
| c_squashfs .squashfs_reg_inode_header
221
225
| c_squashfs .squashfs_symlink_inode_header
222
226
| 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
226
227
| c_squashfs .squashfs_ldir_inode_header
227
228
| c_squashfs .squashfs_lreg_inode_header
228
- | c_squashfs .squashfs_symlink_inode_header
229
- | c_squashfs .squashfs_ldev_inode_header
230
229
| c_squashfs .squashfs_ldev_inode_header
231
- | c_squashfs .squashfs_lipc_inode_header
232
230
| c_squashfs .squashfs_lipc_inode_header ,
233
231
int ,
234
232
int ,
@@ -262,16 +260,10 @@ def header(
262
260
| c_squashfs .squashfs_reg_inode_header
263
261
| c_squashfs .squashfs_symlink_inode_header
264
262
| 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
268
263
| c_squashfs .squashfs_ldir_inode_header
269
264
| c_squashfs .squashfs_lreg_inode_header
270
- | c_squashfs .squashfs_symlink_inode_header
271
- | c_squashfs .squashfs_ldev_inode_header
272
265
| c_squashfs .squashfs_ldev_inode_header
273
266
| c_squashfs .squashfs_lipc_inode_header
274
- | c_squashfs .squashfs_lipc_inode_header
275
267
):
276
268
header , _ , _ = self ._metadata ()
277
269
return header
@@ -315,11 +307,12 @@ def mtime(self) -> datetime:
315
307
return ts .from_unix (self .header .mtime )
316
308
317
309
@property
318
- def size (self ) -> Optional [ int ] :
310
+ def size (self ) -> int | None :
319
311
if self .is_dir () or self .is_file ():
320
312
return self .header .file_size
321
- elif self .is_symlink ():
313
+ if self .is_symlink ():
322
314
return self .header .symlink_size
315
+ return None
323
316
324
317
def is_dir (self ) -> bool :
325
318
return self .type == stat .S_IFDIR
@@ -363,13 +356,9 @@ def link(self) -> str:
363
356
@cached_property
364
357
def link_inode (self ) -> INode :
365
358
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
370
360
return self .fs .get (self .link , relnode )
371
361
372
- @cache
373
362
def listdir (self ) -> dict [str , INode ]:
374
363
return {inode .name : inode for inode in self .iterdir ()}
375
364
@@ -407,7 +396,7 @@ def iterdir(self) -> Iterator[INode]:
407
396
)
408
397
409
398
@cached_property
410
- def block_list (self ) -> list [tuple [Optional [ int ] , int ]]:
399
+ def block_list (self ) -> list [tuple [int | None , int ]]:
411
400
fragment = self .header .fragment
412
401
file_size = self .header .file_size
413
402
if fragment == c_squashfs .SQUASHFS_INVALID_FRAG :
0 commit comments