55from __future__ import annotations
66
77import stat
8- from datetime import datetime
98from functools import cached_property , lru_cache
10- from typing import BinaryIO , Iterator , Optional
9+ from typing import TYPE_CHECKING , BinaryIO
1110
1211from dissect .util import ts
1312from dissect .util .stream import RunlistStream
2019 NotASymlinkError ,
2120)
2221
22+ if TYPE_CHECKING :
23+ from collections .abc import Iterator
24+ from datetime import datetime
25+
2326
2427class QNX4 :
2528 """QNX4 filesystem implementation.
@@ -29,20 +32,19 @@ class QNX4:
2932 """
3033
3134 def __init__ (self , fh : BinaryIO ):
32- self .fh = fh
33-
34- fh .seek (c_qnx4 .QNX4_BLOCK_SIZE )
35- if fh .read (16 ) != b"/" + b"\x00 " * 15 :
35+ if not _is_qnx4 (fh ):
3636 raise ValueError ("Invalid QNX4 filesystem" )
3737
38+ self .fh = fh
39+ self .block_size = c_qnx4 .QNX4_BLOCK_SIZE
3840 self .inode = lru_cache (1024 )(self .inode )
3941
4042 self .root = INode (self , c_qnx4 .QNX4_ROOT_INO * c_qnx4 .QNX4_INODES_PER_BLOCK )
4143
4244 def inode (self , inum : int ) -> INode :
4345 return INode (self , inum )
4446
45- def get (self , path : str | int , node : Optional [ INode ] = None ) -> INode :
47+ def get (self , path : str | int , node : INode | None = None ) -> INode :
4648 if isinstance (path , int ):
4749 return self .inode (path )
4850
@@ -79,7 +81,7 @@ def __repr__(self) -> str:
7981
8082 def _read_inode (self ) -> c_qnx4 .qnx4_inode_entry :
8183 block , index = divmod (self .inum , c_qnx4 .QNX4_INODES_PER_BLOCK )
82- self .fs .fh .seek ((block * c_qnx4 . QNX4_BLOCK_SIZE ) + (index * c_qnx4 .QNX4_DIR_ENTRY_SIZE ))
84+ self .fs .fh .seek ((block * self . fs . block_size ) + (index * c_qnx4 .QNX4_DIR_ENTRY_SIZE ))
8385 return c_qnx4 .qnx4_inode_entry (self .fs .fh )
8486
8587 @cached_property
@@ -123,8 +125,8 @@ def atime(self) -> datetime:
123125 return ts .from_unix (self .inode .di_atime )
124126
125127 @cached_property
126- def ctime (self ) -> int :
127- """Return the datetime creation time."""
128+ def ctime (self ) -> datetime :
129+ """Return the file change time."""
128130 return ts .from_unix (self .inode .di_ctime )
129131
130132 @cached_property
@@ -193,7 +195,7 @@ def is_ipc(self) -> bool:
193195
194196 def listdir (self ) -> dict [str , INode ]:
195197 """Return a directory listing."""
196- return { name : inode for name , inode in self .iterdir ()}
198+ return dict ( self .iterdir ())
197199
198200 def iterdir (self ) -> Iterator [tuple [str , INode ]]:
199201 """Iterate directory contents."""
@@ -203,7 +205,7 @@ def iterdir(self) -> Iterator[tuple[str, INode]]:
203205 fh = self .fs .fh
204206 for block , size in self ._iter_chain ():
205207 for i in range (c_qnx4 .QNX4_INODES_PER_BLOCK * size ):
206- fh .seek ((block * c_qnx4 . QNX4_BLOCK_SIZE ) + (i * c_qnx4 .QNX4_DIR_ENTRY_SIZE ))
208+ fh .seek ((block * self . fs . block_size ) + (i * c_qnx4 .QNX4_DIR_ENTRY_SIZE ))
207209
208210 buf = fh .read (c_qnx4 .QNX4_DIR_ENTRY_SIZE )
209211 if len (buf ) != c_qnx4 .QNX4_DIR_ENTRY_SIZE :
@@ -222,7 +224,7 @@ def iterdir(self) -> Iterator[tuple[str, INode]]:
222224 inum = ((link_info .dl_inode_blk - 1 ) * c_qnx4 .QNX4_INODES_PER_BLOCK ) + link_info .dl_inode_ndx
223225
224226 if link_info .dl_lfn_blk :
225- fh .seek ((link_info .dl_lfn_blk - 1 ) * c_qnx4 . QNX4_BLOCK_SIZE )
227+ fh .seek ((link_info .dl_lfn_blk - 1 ) * self . fs . block_size )
226228 lfn_entry = c_qnx4 .qnx4_longfilename_entry (fh )
227229 name = lfn_entry .lfn_name
228230 else :
@@ -245,7 +247,7 @@ def _iter_chain(self) -> Iterator[tuple[int, int]]:
245247
246248 xblk_num = self .inode .di_xblk
247249 while num_extents :
248- self .fs .fh .seek ((xblk_num - 1 ) * c_qnx4 . QNX4_BLOCK_SIZE )
250+ self .fs .fh .seek ((xblk_num - 1 ) * self . fs . block_size )
249251 xblk = c_qnx4 .qnx4_xblk (self .fs .fh )
250252 if xblk .signature != b"IamXblk" :
251253 raise Error ("Invalid QNX4 xblk signature" )
@@ -258,8 +260,13 @@ def _iter_chain(self) -> Iterator[tuple[int, int]]:
258260
259261 def dataruns (self ) -> list [tuple [int , int ]]:
260262 """Return the data runlist."""
261- return [( block , size ) for block , size in self ._iter_chain ()]
263+ return list ( self ._iter_chain ())
262264
263265 def open (self ) -> BinaryIO :
264266 """Return a file-like object for reading the file."""
265- return RunlistStream (self .fs .fh , self .dataruns (), self .size , c_qnx4 .QNX4_BLOCK_SIZE )
267+ return RunlistStream (self .fs .fh , self .dataruns (), self .size , self .fs .block_size )
268+
269+
270+ def _is_qnx4 (fh : BinaryIO ) -> bool :
271+ fh .seek (c_qnx4 .QNX4_BLOCK_SIZE )
272+ return fh .read (16 ) == b"/" + b"\x00 " * 15
0 commit comments