dissect.target 3.19.dev46__py3-none-any.whl → 3.19.dev47__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.
@@ -228,10 +228,9 @@ class Filesystem:
228
228
  topdown: bool = True,
229
229
  onerror: Optional[Callable] = None,
230
230
  followlinks: bool = False,
231
- ) -> Iterator[str]:
232
- """Walk a directory pointed to by ``path``, returning the string representation of both files and directories.
233
-
234
- It walks across all the files inside ``path`` recursively.
231
+ ) -> Iterator[tuple[str, list[str], list[str]]]:
232
+ """Recursively walk a directory pointed to by ``path``, returning the string representation of both files
233
+ and directories.
235
234
 
236
235
  Args:
237
236
  path: The path to walk on the filesystem.
@@ -250,10 +249,9 @@ class Filesystem:
250
249
  topdown: bool = True,
251
250
  onerror: Optional[Callable] = None,
252
251
  followlinks: bool = False,
253
- ) -> Iterator[FilesystemEntry]:
254
- """Walk a directory pointed to by ``path``, returning FilesystemEntry's of both files and directories.
255
-
256
- It walks across all the files inside ``path`` recursively.
252
+ ) -> Iterator[tuple[list[FilesystemEntry], list[FilesystemEntry], list[FilesystemEntry]]]:
253
+ """Recursively walk a directory pointed to by ``path``, returning :class:`FilesystemEntry` of files
254
+ and directories.
257
255
 
258
256
  Args:
259
257
  path: The path to walk on the filesystem.
@@ -266,6 +264,19 @@ class Filesystem:
266
264
  """
267
265
  return self.get(path).walk_ext(topdown, onerror, followlinks)
268
266
 
267
+ def recurse(self, path: str) -> Iterator[FilesystemEntry]:
268
+ """Recursively walk a directory and yield contents as :class:`FilesystemEntry`.
269
+
270
+ Does not follow symbolic links.
271
+
272
+ Args:
273
+ path: The path to recursively walk on the target filesystem.
274
+
275
+ Returns:
276
+ An iterator of :class:`FilesystemEntry`.
277
+ """
278
+ return self.get(path).recurse()
279
+
269
280
  def glob(self, pattern: str) -> Iterator[str]:
270
281
  """Iterate over the directory part of ``pattern``, returning entries matching ``pattern`` as strings.
271
282
 
@@ -578,10 +589,9 @@ class FilesystemEntry:
578
589
  topdown: bool = True,
579
590
  onerror: Optional[Callable] = None,
580
591
  followlinks: bool = False,
581
- ) -> Iterator[str]:
582
- """Walk a directory and list its contents as strings.
583
-
584
- It walks across all the files inside the entry recursively.
592
+ ) -> Iterator[tuple[str, list[str], list[str]]]:
593
+ """Recursively walk a directory and yield its contents as strings split in a tuple
594
+ of lists of files, directories and symlinks.
585
595
 
586
596
  These contents include::
587
597
  - files
@@ -603,15 +613,9 @@ class FilesystemEntry:
603
613
  topdown: bool = True,
604
614
  onerror: Optional[Callable] = None,
605
615
  followlinks: bool = False,
606
- ) -> Iterator[FilesystemEntry]:
607
- """Walk a directory and show its contents as :class:`FilesystemEntry`.
608
-
609
- It walks across all the files inside the entry recursively.
610
-
611
- These contents include::
612
- - files
613
- - directories
614
- - symboliclinks
616
+ ) -> Iterator[tuple[list[FilesystemEntry], list[FilesystemEntry], list[FilesystemEntry]]]:
617
+ """Recursively walk a directory and yield its contents as :class:`FilesystemEntry` split in a tuple of
618
+ lists of files, directories and symlinks.
615
619
 
616
620
  Args:
617
621
  topdown: ``True`` puts this entry at the top of the list, ``False`` puts this entry at the bottom.
@@ -619,10 +623,20 @@ class FilesystemEntry:
619
623
  followlinks: ``True`` if we want to follow any symbolic link
620
624
 
621
625
  Returns:
622
- An iterator of :class:`FilesystemEntry`.
626
+ An iterator of tuples :class:`FilesystemEntry`.
623
627
  """
624
628
  yield from fsutil.walk_ext(self, topdown, onerror, followlinks)
625
629
 
630
+ def recurse(self) -> Iterator[FilesystemEntry]:
631
+ """Recursively walk a directory and yield its contents as :class:`FilesystemEntry`.
632
+
633
+ Does not follow symbolic links.
634
+
635
+ Returns:
636
+ An iterator of :class:`FilesystemEntry`.
637
+ """
638
+ yield from fsutil.recurse(self)
639
+
626
640
  def glob(self, pattern: str) -> Iterator[str]:
627
641
  """Iterate over this directory part of ``patern``, returning entries matching ``pattern`` as strings.
628
642
 
@@ -96,6 +96,7 @@ __all__ = [
96
96
  "TargetPath",
97
97
  "walk_ext",
98
98
  "walk",
99
+ "recurse",
99
100
  ]
100
101
 
101
102
 
@@ -291,6 +292,20 @@ def walk_ext(path_entry, topdown=True, onerror=None, followlinks=False):
291
292
  yield [path_entry], dirs, files
292
293
 
293
294
 
295
+ def recurse(path_entry: filesystem.FilesystemEntry) -> Iterator[filesystem.FilesystemEntry]:
296
+ """Recursively walk the given :class:`FilesystemEntry`, yields :class:`FilesystemEntry` instances."""
297
+ yield path_entry
298
+
299
+ if not path_entry.is_dir():
300
+ return
301
+
302
+ for child_entry in path_entry.scandir():
303
+ if child_entry.is_dir() and not child_entry.is_symlink():
304
+ yield from recurse(child_entry)
305
+ else:
306
+ yield child_entry
307
+
308
+
294
309
  def glob_split(pattern: str, alt_separator: str = "") -> tuple[str, str]:
295
310
  """Split a pattern on path part boundaries on the first path part with a glob pattern.
296
311
 
@@ -1,20 +1,20 @@
1
- import struct
2
1
  from enum import IntEnum
3
2
  from io import BytesIO
3
+ from typing import Iterator
4
4
 
5
- from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
5
+ from dissect.target.exceptions import UnsupportedPluginError
6
6
  from dissect.target.helpers.record import TargetRecordDescriptor
7
7
  from dissect.target.plugin import Plugin, export
8
- from dissect.target.plugins.filesystem.walkfs import generate_record
9
8
 
10
9
  CapabilityRecord = TargetRecordDescriptor(
11
10
  "filesystem/unix/capability",
12
11
  [
13
- ("record", "record"),
12
+ ("datetime", "ts_mtime"),
13
+ ("path", "path"),
14
14
  ("string[]", "permitted"),
15
15
  ("string[]", "inheritable"),
16
16
  ("boolean", "effective"),
17
- ("uint32", "rootid"),
17
+ ("uint32", "root_id"),
18
18
  ],
19
19
  )
20
20
 
@@ -82,88 +82,103 @@ class CapabilityPlugin(Plugin):
82
82
  """Plugin to yield files with capabilites set."""
83
83
 
84
84
  def check_compatible(self) -> None:
85
- if not self.target.has_function("walkfs") or self.target.os == "windows":
86
- raise UnsupportedPluginError("Unsupported plugin")
85
+ if not self.target.has_function("walkfs"):
86
+ raise UnsupportedPluginError("Need walkfs plugin")
87
+
88
+ if not any(fs.__type__ in ("extfs", "xfs") for fs in self.target.filesystems):
89
+ raise UnsupportedPluginError("Capability plugin only works on EXT and XFS filesystems")
87
90
 
88
91
  @export(record=CapabilityRecord)
89
- def capability_binaries(self):
90
- """Find all files that have capabilities set."""
91
- for path_entries, _, files in self.target.fs.walk_ext("/"):
92
- entries = [path_entries[-1]] + files
93
- for entry in entries:
94
- path = self.target.fs.path(entry.path)
95
- try:
96
- record = generate_record(self.target, path)
97
- except FileNotFoundError:
98
- continue
92
+ def capability_binaries(self) -> Iterator[CapabilityRecord]:
93
+ """Find all files that have capabilities set on files.
94
+
95
+ Resources:
96
+ - https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
97
+ """
98
+
99
+ for entry in self.target.fs.recurse("/"):
100
+ if not entry.is_file() or entry.is_symlink():
101
+ continue
102
+
103
+ try:
104
+ attrs = [attr for attr in entry.lattr() if attr.name == "security.capability"]
105
+ except Exception as e:
106
+ self.target.log.warning("Failed to get attrs for entry %s", entry)
107
+ self.target.log.debug("", exc_info=e)
108
+ continue
109
+
110
+ for attr in attrs:
99
111
  try:
100
- attrs = path.get().lattr()
101
- except TypeError:
102
- # Virtual(File|Directory|Symlink) instances don't have a functional lattr()
103
- continue
104
- except Exception:
105
- self.target.log.exception("Failed to get attrs for entry %s", entry)
106
- continue
107
-
108
- for attr in attrs:
109
- if attr.name != "security.capability":
110
- continue
111
-
112
- buf = BytesIO(attr.value)
113
-
114
- # Reference: https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
115
- # The struct is small enough we can just use struct
116
- magic_etc = struct.unpack("<I", buf.read(4))[0]
117
- cap_revision = magic_etc & VFS_CAP_REVISION_MASK
118
-
119
- permitted_caps = []
120
- inheritable_caps = []
121
- rootid = None
122
-
123
- if cap_revision == VFS_CAP_REVISION_1:
124
- num_caps = VFS_CAP_U32_1
125
- data_len = (1 + 2 * VFS_CAP_U32_1) * 4
126
- elif cap_revision == VFS_CAP_REVISION_2:
127
- num_caps = VFS_CAP_U32_2
128
- data_len = (1 + 2 * VFS_CAP_U32_2) * 4
129
- elif cap_revision == VFS_CAP_REVISION_3:
130
- num_caps = VFS_CAP_U32_3
131
- data_len = (2 + 2 * VFS_CAP_U32_2) * 4
132
- else:
133
- self.target.log.error("Unexpected capability revision: %s", entry)
134
- continue
135
-
136
- if data_len != len(attr.value):
137
- self.target.log.error("Unexpected capability length: %s", entry)
138
- continue
139
-
140
- for _ in range(num_caps):
141
- permitted_val, inheritable_val = struct.unpack("<2I", buf.read(8))
142
- permitted_caps.append(permitted_val)
143
- inheritable_caps.append(inheritable_val)
144
-
145
- if cap_revision == VFS_CAP_REVISION_3:
146
- rootid = struct.unpack("<I", buf.read(4))[0]
147
-
148
- permitted = []
149
- inheritable = []
150
-
151
- for capability in Capabilities:
152
- for caps, results in [(permitted_caps, permitted), (inheritable_caps, inheritable)]:
153
- # CAP_TO_INDEX
154
- cap_index = capability.value >> 5
155
- if cap_index >= len(caps):
156
- # We loop over all capabilities, but might only have a version 1 caps list
157
- continue
158
-
159
- if caps[cap_index] & (1 << (capability.value & 31)) != 0:
160
- results.append(capability.name)
161
-
162
- yield CapabilityRecord(
163
- record=record,
164
- permitted=permitted,
165
- inheritable=inheritable,
166
- effective=magic_etc & VFS_CAP_FLAGS_EFFECTIVE != 0,
167
- rootid=rootid,
168
- _target=self.target,
169
- )
112
+ permitted, inheritable, effective, root_id = parse_attr(attr.value)
113
+ except ValueError as e:
114
+ self.target.log.warning("Could not parse attributes for entry %s: %s", entry, str(e.value))
115
+ self.target.log.debug("", exc_info=e)
116
+
117
+ yield CapabilityRecord(
118
+ ts_mtime=entry.lstat().st_mtime,
119
+ path=self.target.fs.path(entry.path),
120
+ permitted=permitted,
121
+ inheritable=inheritable,
122
+ effective=effective,
123
+ root_id=root_id,
124
+ _target=self.target,
125
+ )
126
+
127
+
128
+ def parse_attr(attr: bytes) -> tuple[list[str], list[str], bool, int]:
129
+ """Efficiently parse a Linux xattr capability struct.
130
+
131
+ Returns:
132
+ A tuple of permitted capability names, inheritable capability names, effective flag and ``root_id``.
133
+ """
134
+ buf = BytesIO(attr)
135
+
136
+ # The struct is small enough we can just use int.from_bytes
137
+ magic_etc = int.from_bytes(buf.read(4), "little")
138
+ effective = magic_etc & VFS_CAP_FLAGS_EFFECTIVE != 0
139
+ cap_revision = magic_etc & VFS_CAP_REVISION_MASK
140
+
141
+ permitted_caps = []
142
+ inheritable_caps = []
143
+ root_id = None
144
+
145
+ if cap_revision == VFS_CAP_REVISION_1:
146
+ num_caps = VFS_CAP_U32_1
147
+ data_len = (1 + 2 * VFS_CAP_U32_1) * 4
148
+
149
+ elif cap_revision == VFS_CAP_REVISION_2:
150
+ num_caps = VFS_CAP_U32_2
151
+ data_len = (1 + 2 * VFS_CAP_U32_2) * 4
152
+
153
+ elif cap_revision == VFS_CAP_REVISION_3:
154
+ num_caps = VFS_CAP_U32_3
155
+ data_len = (2 + 2 * VFS_CAP_U32_2) * 4
156
+
157
+ else:
158
+ raise ValueError("Unexpected capability revision '%s'" % cap_revision)
159
+
160
+ if data_len != (actual_len := len(attr)):
161
+ raise ValueError("Unexpected capability length (%s vs %s)", data_len, actual_len)
162
+
163
+ for _ in range(num_caps):
164
+ permitted_caps.append(int.from_bytes(buf.read(4), "little"))
165
+ inheritable_caps.append(int.from_bytes(buf.read(4), "little"))
166
+
167
+ if cap_revision == VFS_CAP_REVISION_3:
168
+ root_id = int.from_bytes(buf.read(4), "little")
169
+
170
+ permitted = []
171
+ inheritable = []
172
+
173
+ for capability in Capabilities:
174
+ for caps, results in [(permitted_caps, permitted), (inheritable_caps, inheritable)]:
175
+ # CAP_TO_INDEX
176
+ cap_index = capability.value >> 5
177
+ if cap_index >= len(caps):
178
+ # We loop over all capabilities, but might only have a version 1 caps list
179
+ continue
180
+
181
+ if caps[cap_index] & (1 << (capability.value & 31)) != 0:
182
+ results.append(capability.name)
183
+
184
+ return permitted, inheritable, effective, root_id
@@ -1,12 +1,11 @@
1
- from typing import Iterable
1
+ from typing import Iterator
2
2
 
3
3
  from dissect.util.ts import from_unix
4
4
 
5
5
  from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
6
- from dissect.target.filesystem import LayerFilesystemEntry
7
- from dissect.target.helpers.fsutil import TargetPath
6
+ from dissect.target.filesystem import FilesystemEntry, LayerFilesystemEntry
8
7
  from dissect.target.helpers.record import TargetRecordDescriptor
9
- from dissect.target.plugin import Plugin, export
8
+ from dissect.target.plugin import Plugin, arg, export
10
9
  from dissect.target.target import Target
11
10
 
12
11
  FilesystemRecord = TargetRecordDescriptor(
@@ -30,37 +29,49 @@ FilesystemRecord = TargetRecordDescriptor(
30
29
  class WalkFSPlugin(Plugin):
31
30
  def check_compatible(self) -> None:
32
31
  if not len(self.target.filesystems):
33
- raise UnsupportedPluginError("No filesystems found")
32
+ raise UnsupportedPluginError("No filesystems to walk")
34
33
 
35
34
  @export(record=FilesystemRecord)
36
- def walkfs(self) -> Iterable[FilesystemRecord]:
35
+ @arg("--walkfs-path", default="/", help="path to recursively walk")
36
+ def walkfs(self, walkfs_path: str = "/") -> Iterator[FilesystemRecord]:
37
37
  """Walk a target's filesystem and return all filesystem entries."""
38
- for path_entries, _, files in self.target.fs.walk_ext("/"):
39
- entries = [path_entries[-1]] + files
40
- for entry in entries:
41
- path = self.target.fs.path(entry.path)
42
- try:
43
- record = generate_record(self.target, path)
44
- except FileNotFoundError:
45
- continue
46
- yield record
38
+ for entry in self.target.fs.recurse(walkfs_path):
39
+ try:
40
+ yield generate_record(self.target, entry)
47
41
 
42
+ except FileNotFoundError as e:
43
+ self.target.log.warning("File not found: %s", entry)
44
+ self.target.log.debug("", exc_info=e)
45
+ except Exception as e:
46
+ self.target.log.warning("Exception generating record for: %s", entry)
47
+ self.target.log.debug("", exc_info=e)
48
+ continue
49
+
50
+
51
+ def generate_record(target: Target, entry: FilesystemEntry) -> FilesystemRecord:
52
+ """Generate a :class:`FilesystemRecord` from the given :class:`FilesystemEntry`.
53
+
54
+ Args:
55
+ target: :class:`Target` instance
56
+ entry: :class:`FilesystemEntry` instance
57
+
58
+ Returns:
59
+ Generated :class:`FilesystemRecord` for the given :class:`FilesystemEntry`.
60
+ """
61
+ stat = entry.lstat()
48
62
 
49
- def generate_record(target: Target, path: TargetPath) -> FilesystemRecord:
50
- stat = path.lstat()
51
- btime = from_unix(stat.st_birthtime) if stat.st_birthtime else None
52
- entry = path.get()
53
63
  if isinstance(entry, LayerFilesystemEntry):
54
64
  fs_types = [sub_entry.fs.__type__ for sub_entry in entry.entries]
55
65
  else:
56
66
  fs_types = [entry.fs.__type__]
67
+
57
68
  return FilesystemRecord(
58
69
  atime=from_unix(stat.st_atime),
59
70
  mtime=from_unix(stat.st_mtime),
60
71
  ctime=from_unix(stat.st_ctime),
61
- btime=btime,
72
+ btime=from_unix(stat.st_birthtime) if stat.st_birthtime else None,
62
73
  ino=stat.st_ino,
63
- path=path,
74
+ path=entry.path,
64
75
  size=stat.st_size,
65
76
  mode=stat.st_mode,
66
77
  uid=stat.st_uid,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.19.dev46
3
+ Version: 3.19.dev47
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
@@ -1,7 +1,7 @@
1
1
  dissect/target/__init__.py,sha256=Oc7ounTgq2hE4nR6YcNabetc7SQA40ldSa35VEdZcQU,63
2
2
  dissect/target/container.py,sha256=0YcwcGmfJjhPXUB6DEcjWEoSuAtTDxMDpoTviMrLsxM,9353
3
3
  dissect/target/exceptions.py,sha256=ULi7NXlqju_d8KENEL3aimmfKTFfbNssfeWhAnOB654,2972
4
- dissect/target/filesystem.py,sha256=G1gbOUpnQZyovubYGEUKgaDV0eHH5vE83-0gTc5PZAM,59793
4
+ dissect/target/filesystem.py,sha256=WpcT7UKzJxr85-Y4x8QeQci4UX0z39VlraRcnmkDFkg,60519
5
5
  dissect/target/loader.py,sha256=I8WNzDA0SMy42F7zfyBcSKj_VKNv64213WUvtGZ77qE,7374
6
6
  dissect/target/plugin.py,sha256=k9xWNnIGQG0DQsq6DKYJ6_DAX1aIA0SjzniWmOwX8O4,50317
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
@@ -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=EK1idVnD-3d2P5wTa6it-BQL4YCgbW2ZzNIM7yMiCpU,19871
53
+ dissect/target/helpers/fsutil.py,sha256=X1JrgVd9VZh-ttynar3uW9Y9vdqvB5uMvq-4DKFgm9k,20356
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
@@ -169,7 +169,7 @@ dissect/target/plugins/filesystem/acquire_handles.py,sha256=-pX_akH5GrYe0HofXOa2
169
169
  dissect/target/plugins/filesystem/acquire_hash.py,sha256=OVxI19-Bl1tdqCiFMscFMLmyoiBOsuAjL-Q8aQpEwl0,1441
170
170
  dissect/target/plugins/filesystem/icat.py,sha256=bOMi04IlljnKwxTWTZJKtK7RxKnabFu3WcXyUwzkE-4,4090
171
171
  dissect/target/plugins/filesystem/resolver.py,sha256=HfyASUFV4F9uD-yFXilFpPTORAsRDvdmTvuYHgOaOWg,4776
172
- dissect/target/plugins/filesystem/walkfs.py,sha256=e8HEZcV5Wiua26FGWL3xgiQ_PIhcNvGI5KCdsAx2Nmo,2298
172
+ dissect/target/plugins/filesystem/walkfs.py,sha256=rklbN805roy2fKAQe5L1JhTvI0qNgGS70ZNGFwevLB0,2740
173
173
  dissect/target/plugins/filesystem/yara.py,sha256=zh4hU3L_egddLqDeaHDVuCWYhTlNzPYPVak36Q6IMxI,6621
174
174
  dissect/target/plugins/filesystem/ntfs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  dissect/target/plugins/filesystem/ntfs/mft.py,sha256=WzvKSlH4egvDsuonRQ3AjYS59t9jjFX_-GOsAPLUeSk,12418
@@ -177,7 +177,7 @@ dissect/target/plugins/filesystem/ntfs/mft_timeline.py,sha256=vvNFAZbr7s3X2OTYf4
177
177
  dissect/target/plugins/filesystem/ntfs/usnjrnl.py,sha256=uiT1ipmcAo__6VIUi8R_vvIu22vdnjMACKwLSAbzYjs,3704
178
178
  dissect/target/plugins/filesystem/ntfs/utils.py,sha256=xG7Lgw9NX4tDDrZVRm0vycFVJTOM7j-HrjqzDh0f4uA,3136
179
179
  dissect/target/plugins/filesystem/unix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
- dissect/target/plugins/filesystem/unix/capability.py,sha256=oTJVEr8Yszejd-FxU0D8J49ATxNrJOcUnBFIc96k8kg,5920
180
+ dissect/target/plugins/filesystem/unix/capability.py,sha256=WScVTW9bZBsH36ZRsAUTZzl9yXG4rglv_MaieWD5H-w,5752
181
181
  dissect/target/plugins/filesystem/unix/suid.py,sha256=Q0Y5CyPm34REruyZYP5siFAka4i7QEOOxZ9K2L-SxPY,1290
182
182
  dissect/target/plugins/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
183
183
  dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHnpSnKhC1mjZE,3432
@@ -356,10 +356,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
356
356
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
357
357
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
358
358
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
359
- dissect.target-3.19.dev46.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
360
- dissect.target-3.19.dev46.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
361
- dissect.target-3.19.dev46.dist-info/METADATA,sha256=KqAZhon-mJ3zlkhXprWA1AQGJ5atgKACGFMvgTKsayo,12897
362
- dissect.target-3.19.dev46.dist-info/WHEEL,sha256=nCVcAvsfA9TDtwGwhYaRrlPhTLV9m-Ga6mdyDtuwK18,91
363
- dissect.target-3.19.dev46.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
364
- dissect.target-3.19.dev46.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
365
- dissect.target-3.19.dev46.dist-info/RECORD,,
359
+ dissect.target-3.19.dev47.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
360
+ dissect.target-3.19.dev47.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
361
+ dissect.target-3.19.dev47.dist-info/METADATA,sha256=NCGFEq1LFs3I0EP59BzAba793TZy6rhKYUi4bT9rBxs,12897
362
+ dissect.target-3.19.dev47.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
363
+ dissect.target-3.19.dev47.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
364
+ dissect.target-3.19.dev47.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
365
+ dissect.target-3.19.dev47.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.0)
2
+ Generator: setuptools (73.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5