dissect.target 3.19.dev38__py3-none-any.whl → 3.19.dev40__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,16 +4,21 @@ import logging
4
4
  import stat
5
5
  import zipfile
6
6
  from datetime import datetime, timezone
7
- from typing import BinaryIO, Optional
7
+ from typing import BinaryIO, Iterator
8
8
 
9
9
  from dissect.util.stream import BufferedStream
10
10
 
11
- from dissect.target.exceptions import FileNotFoundError
11
+ from dissect.target.exceptions import (
12
+ FileNotFoundError,
13
+ FilesystemError,
14
+ IsADirectoryError,
15
+ NotADirectoryError,
16
+ NotASymlinkError,
17
+ )
12
18
  from dissect.target.filesystem import (
13
19
  Filesystem,
14
20
  FilesystemEntry,
15
21
  VirtualDirectory,
16
- VirtualFile,
17
22
  VirtualFilesystem,
18
23
  )
19
24
  from dissect.target.helpers import fsutil
@@ -33,7 +38,7 @@ class ZipFilesystem(Filesystem):
33
38
  def __init__(
34
39
  self,
35
40
  fh: BinaryIO,
36
- base: Optional[str] = None,
41
+ base: str | None = None,
37
42
  *args,
38
43
  **kwargs,
39
44
  ):
@@ -52,12 +57,7 @@ class ZipFilesystem(Filesystem):
52
57
  continue
53
58
 
54
59
  rel_name = fsutil.normpath(mname[len(self.base) :], alt_separator=self.alt_separator)
55
-
56
- # NOTE: Normally we would check here if the member is a symlink or not
57
-
58
- entry_cls = ZipFilesystemDirectoryEntry if member.is_dir() else ZipFilesystemEntry
59
- file_entry = entry_cls(self, rel_name, member)
60
- self._fs.map_file_entry(rel_name, file_entry)
60
+ self._fs.map_file_entry(rel_name, ZipFilesystemEntry(self, rel_name, member))
61
61
 
62
62
  @staticmethod
63
63
  def _detect(fh: BinaryIO) -> bool:
@@ -69,60 +69,95 @@ class ZipFilesystem(Filesystem):
69
69
  return self._fs.get(path, relentry=relentry)
70
70
 
71
71
 
72
- class ZipFilesystemEntry(VirtualFile):
72
+ # Note: We subclass from VirtualDirectory because VirtualFilesystem is currently only compatible with VirtualDirectory
73
+ # Subclass from VirtualDirectory so we get that compatibility for free, and override the rest to do our own thing
74
+ class ZipFilesystemEntry(VirtualDirectory):
75
+ fs: ZipFilesystem
76
+ entry: zipfile.ZipInfo
77
+
78
+ def __init__(self, fs: ZipFilesystem, path: str, entry: zipfile.ZipInfo):
79
+ super().__init__(fs, path)
80
+ self.entry = entry
81
+
73
82
  def open(self) -> BinaryIO:
74
- """Returns file handle (file-like object)."""
83
+ if self.is_dir():
84
+ raise IsADirectoryError(self.path)
85
+
86
+ if self.is_symlink():
87
+ return self._resolve().open()
88
+
75
89
  try:
76
90
  return BufferedStream(self.fs.zip.open(self.entry), size=self.entry.file_size)
77
91
  except Exception:
78
- raise FileNotFoundError()
92
+ raise FileNotFoundError(self.path)
79
93
 
80
- def readlink(self) -> str:
81
- """Read the link if this entry is a symlink. Returns a string."""
82
- raise NotImplementedError()
94
+ def iterdir(self) -> Iterator[str]:
95
+ if not self.is_dir():
96
+ raise NotADirectoryError(self.path)
83
97
 
84
- def readlink_ext(self) -> FilesystemEntry:
85
- """Read the link if this entry is a symlink. Returns a filesystem entry."""
86
- raise NotImplementedError()
98
+ entry = self._resolve()
99
+ if isinstance(entry, ZipFilesystemEntry):
100
+ yield from super(ZipFilesystemEntry, entry).iterdir()
101
+ else:
102
+ yield from entry.iterdir()
87
103
 
88
- def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
89
- """Return the stat information of this entry."""
90
- return self.lstat()
104
+ def scandir(self) -> Iterator[FilesystemEntry]:
105
+ if not self.is_dir():
106
+ raise NotADirectoryError(self.path)
91
107
 
92
- def lstat(self) -> fsutil.stat_result:
93
- """Return the stat information of the given path, without resolving links."""
94
- # ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
95
- return fsutil.stat_result(
96
- [
97
- stat.S_IFREG | 0o777,
98
- self.entry.header_offset,
99
- id(self.fs),
100
- 1,
101
- 0,
102
- 0,
103
- self.entry.file_size,
104
- 0,
105
- datetime(*self.entry.date_time, tzinfo=timezone.utc).timestamp(),
106
- 0,
107
- ]
108
- )
108
+ entry = self._resolve()
109
+ if isinstance(entry, ZipFilesystemEntry):
110
+ yield from super(ZipFilesystemEntry, entry).scandir()
111
+ else:
112
+ yield from entry.scandir()
109
113
 
114
+ def is_dir(self, follow_symlinks: bool = True) -> bool:
115
+ try:
116
+ entry = self._resolve(follow_symlinks=follow_symlinks)
117
+ except FilesystemError:
118
+ return False
110
119
 
111
- class ZipFilesystemDirectoryEntry(VirtualDirectory):
112
- def __init__(self, fs: ZipFilesystem, path: str, entry: zipfile.ZipInfo):
113
- super().__init__(fs, path)
114
- self.entry = entry
120
+ if isinstance(entry, ZipFilesystemEntry):
121
+ return entry.entry.is_dir()
122
+ return isinstance(entry, VirtualDirectory)
123
+
124
+ def is_file(self, follow_symlinks: bool = True) -> bool:
125
+ try:
126
+ entry = self._resolve(follow_symlinks=follow_symlinks)
127
+ except FilesystemError:
128
+ return False
129
+
130
+ if isinstance(entry, ZipFilesystemEntry):
131
+ return not entry.entry.is_dir()
132
+ return False
133
+
134
+ def is_symlink(self) -> bool:
135
+ return stat.S_ISLNK(self.entry.external_attr >> 16)
136
+
137
+ def readlink(self) -> str:
138
+ if not self.is_symlink():
139
+ raise NotASymlinkError()
140
+ return self.fs.zip.open(self.entry).read().decode()
141
+
142
+ def readlink_ext(self) -> FilesystemEntry:
143
+ return FilesystemEntry.readlink_ext(self)
115
144
 
116
145
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
117
- """Return the stat information of this entry."""
118
- return self.lstat()
146
+ return self._resolve(follow_symlinks=follow_symlinks).lstat()
119
147
 
120
148
  def lstat(self) -> fsutil.stat_result:
121
149
  """Return the stat information of the given path, without resolving links."""
122
150
  # ['mode', 'addr', 'dev', 'nlink', 'uid', 'gid', 'size', 'atime', 'mtime', 'ctime']
151
+ mode = self.entry.external_attr >> 16
152
+
153
+ if self.entry.is_dir() and not stat.S_ISDIR(mode):
154
+ mode = stat.S_IFDIR | mode
155
+ elif not self.entry.is_dir() and not stat.S_ISREG(mode):
156
+ mode = stat.S_IFREG | mode
157
+
123
158
  return fsutil.stat_result(
124
159
  [
125
- stat.S_IFDIR | 0o777,
160
+ mode,
126
161
  self.entry.header_offset,
127
162
  id(self.fs),
128
163
  1,
@@ -144,6 +144,7 @@ class stat_result: # noqa
144
144
  "st_file_attributes": "Windows file attribute bits",
145
145
  "st_fstype": "Type of filesystem",
146
146
  "st_reparse_tag": "Windows reparse tag",
147
+ "st_birthtime_ns": "time of creation in nanoseconds",
147
148
  # Internal fields
148
149
  "_s": "internal tuple",
149
150
  }
@@ -193,6 +194,7 @@ class stat_result: # noqa
193
194
  self.st_file_attributes = s[22]
194
195
  self.st_fstype = s[23]
195
196
  self.st_reparse_tag = s[24]
197
+ self.st_birthtime_ns = s[25]
196
198
 
197
199
  # stat_result behaves like a tuple, but only with the first 10 fields
198
200
  # Note that this means it specifically uses the integer variants of the timestamps
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev38
3
+ Version: 3.19.dev40
4
4
  Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -42,7 +42,7 @@ dissect/target/filesystems/tar.py,sha256=kQNhcEDPX005svse039OeR2AGSDigGuGz2AKoVr
42
42
  dissect/target/filesystems/vmfs.py,sha256=sRtYBUAKTKcHrjCXqpFJ8GIVU-ERjqxhB2zXBndtcXU,4955
43
43
  dissect/target/filesystems/vmtar.py,sha256=LlKWkTIuLemQmG9yGqL7980uC_AOL77_GWhbJc_grSk,804
44
44
  dissect/target/filesystems/xfs.py,sha256=kIyFGKYlyFYC7H3jaEv-lNKtBW4ZkD92H0WpfGcr1ww,4498
45
- dissect/target/filesystems/zip.py,sha256=WT1bQhzX_1MXXVZTKrJniae4xqRqMZ8FsfbvhgGQRTQ,4462
45
+ dissect/target/filesystems/zip.py,sha256=BeNj23DOYfWuTm5V1V419ViJiMfBrO1VA5gP6rljwXs,5467
46
46
  dissect/target/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dissect/target/helpers/cache.py,sha256=TXlJBdFRz6V9zKs903am4Yawr0maYw5kZY0RqklDQJM,8568
48
48
  dissect/target/helpers/config.py,sha256=RMHnIuKJHINHiLrvKN3EyA0jFA1o6-pbeaycG8Pgrp8,2596
@@ -50,7 +50,7 @@ dissect/target/helpers/configutil.py,sha256=AEnkMQ0e6PncvCqGa-ACzBQWQBhMGBCzO5qz
50
50
  dissect/target/helpers/cyber.py,sha256=WnJlk-HqAETmDAgLq92JPxyDLxvzSoFV_WrO-odVKBI,16805
51
51
  dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
52
52
  dissect/target/helpers/docs.py,sha256=J5U65Y3yOTqxDEZRCdrEmO63XQCeDzOJea1PwPM6Cyc,5146
53
- dissect/target/helpers/fsutil.py,sha256=tPyH4RBDqM9QXjamIQaDRLUy3b4dKmfrT6k3ZP01U6Y,19772
53
+ dissect/target/helpers/fsutil.py,sha256=EK1idVnD-3d2P5wTa6it-BQL4YCgbW2ZzNIM7yMiCpU,19871
54
54
  dissect/target/helpers/hashutil.py,sha256=bYAGEjyYyxuCTziO4kCx6srzY1Cm-PXmayRRcxt5ca4,1061
55
55
  dissect/target/helpers/keychain.py,sha256=wYH0sf7eaxP0bZTo80RF_BQMWulCWmIQ8Tzt9K5TSNQ,3611
56
56
  dissect/target/helpers/lazy.py,sha256=823VtmdWsbJyVZvNWopDhQdqq2i1xtj6b8IKfveboKw,1771
@@ -349,10 +349,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
349
349
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
350
350
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
351
351
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
352
- dissect.target-3.19.dev38.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
353
- dissect.target-3.19.dev38.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
354
- dissect.target-3.19.dev38.dist-info/METADATA,sha256=moRisLjdf7BN29AYj8cVY3tFh8VxpOVFuU-EfD8f8W0,12719
355
- dissect.target-3.19.dev38.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
356
- dissect.target-3.19.dev38.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
357
- dissect.target-3.19.dev38.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
358
- dissect.target-3.19.dev38.dist-info/RECORD,,
352
+ dissect.target-3.19.dev40.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
353
+ dissect.target-3.19.dev40.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
354
+ dissect.target-3.19.dev40.dist-info/METADATA,sha256=KfWwT1qzG3w3xrcYRJ7uxcMe46PmOltMBoWWE6opaQg,12719
355
+ dissect.target-3.19.dev40.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
356
+ dissect.target-3.19.dev40.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
357
+ dissect.target-3.19.dev40.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
358
+ dissect.target-3.19.dev40.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5