dissect.target 3.19.dev46__py3-none-any.whl → 3.19.dev48__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
 
@@ -739,7 +753,7 @@ class FilesystemEntry:
739
753
  """
740
754
  log.debug("%r::readlink_ext()", self)
741
755
  # Default behavior, resolve link own filesystem.
742
- return fsutil.resolve_link(fs=self.fs, entry=self)
756
+ return fsutil.resolve_link(self.fs, self.readlink(), self.path, alt_separator=self.fs.alt_separator)
743
757
 
744
758
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
745
759
  """Determine the stat information of this entry.
@@ -1453,10 +1467,15 @@ class LayerFilesystem(Filesystem):
1453
1467
  """Get a :class:`FilesystemEntry` relative to a specific entry."""
1454
1468
  parts = path.split("/")
1455
1469
 
1456
- for part in parts:
1470
+ for i, part in enumerate(parts):
1457
1471
  if entry.is_symlink():
1458
1472
  # Resolve using the RootFilesystem instead of the entry's Filesystem
1459
- entry = fsutil.resolve_link(fs=self, entry=entry)
1473
+ entry = fsutil.resolve_link(
1474
+ self,
1475
+ entry.readlink(),
1476
+ "/".join(parts[:i]),
1477
+ alt_separator=entry.fs.alt_separator,
1478
+ )
1460
1479
  entry = entry.get(part)
1461
1480
 
1462
1481
  return entry
@@ -94,7 +94,7 @@ class ITunesFilesystemEntry(VirtualFile):
94
94
  def readlink_ext(self) -> FilesystemEntry:
95
95
  """Read the link if this entry is a symlink. Returns a filesystem entry."""
96
96
  # Can't use the one in VirtualFile as it overrides the FilesystemEntry
97
- return fsutil.resolve_link(fs=self.fs, entry=self)
97
+ return fsutil.resolve_link(self.fs, self.readlink(), self.path, alt_separator=self.fs.alt_separator)
98
98
 
99
99
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
100
100
  """Return the stat information of this entry."""
@@ -121,7 +121,7 @@ class TarFilesystemEntry(VirtualFile):
121
121
  def readlink_ext(self) -> FilesystemEntry:
122
122
  """Read the link if this entry is a symlink. Returns a filesystem entry."""
123
123
  # Can't use the one in VirtualFile as it overrides the FilesystemEntry
124
- return fsutil.resolve_link(fs=self.fs, entry=self)
124
+ return fsutil.resolve_link(self.fs, self.readlink(), self.path, alt_separator=self.fs.alt_separator)
125
125
 
126
126
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
127
127
  """Return the stat information of this entry."""
@@ -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
 
@@ -425,15 +440,20 @@ def has_glob_magic(s) -> bool:
425
440
 
426
441
 
427
442
  def resolve_link(
428
- fs: filesystem.Filesystem, entry: filesystem.FilesystemEntry, previous_links: set[str] = None
443
+ fs: filesystem.Filesystem,
444
+ link: str,
445
+ path: str,
446
+ *,
447
+ alt_separator: str = "",
448
+ previous_links: set[str] | None = None,
429
449
  ) -> filesystem.FilesystemEntry:
430
450
  """Resolves a symlink to its actual path.
431
451
 
432
452
  It stops resolving once it detects an infinite recursion loop.
433
453
  """
434
454
 
435
- link = normalize(entry.readlink(), alt_separator=entry.fs.alt_separator)
436
- path = normalize(entry.path, alt_separator=entry.fs.alt_separator)
455
+ link = normalize(link, alt_separator=alt_separator)
456
+ path = normalize(path, alt_separator=alt_separator)
437
457
 
438
458
  # Create hash for entry based on path and link
439
459
  link_id = f"{path}{link}"
@@ -456,7 +476,13 @@ def resolve_link(
456
476
  entry = fs.get(link)
457
477
 
458
478
  if entry.is_symlink():
459
- entry = resolve_link(fs, entry, previous_links)
479
+ entry = resolve_link(
480
+ fs,
481
+ entry.readlink(),
482
+ link,
483
+ alt_separator=entry.fs.alt_separator,
484
+ previous_links=previous_links,
485
+ )
460
486
 
461
487
  return entry
462
488
 
@@ -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.dev48
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=__p2p72B6mKgIiAOj85EC3ESdZ8gjgkm2pt1KKym3h0,60743
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
@@ -32,13 +32,13 @@ dissect/target/filesystems/exfat.py,sha256=PRkZPUVN5NlgB1VetFtywdNgF6Yj5OBtF5a25
32
32
  dissect/target/filesystems/extfs.py,sha256=pFv1dyqqTnImpMuy-slAqnnLVfteV9tS03AsG-svN9E,4776
33
33
  dissect/target/filesystems/fat.py,sha256=ZSw-wS57vo5eIXJndfI1rZkGu_qh-vyioMzCZFZ_UTE,4611
34
34
  dissect/target/filesystems/ffs.py,sha256=Wu8sS1jjmD0QXXcAaD2h_zzfvinjco8qvj0hErufZ-4,4555
35
- dissect/target/filesystems/itunes.py,sha256=6LPUHSf2qpHacMgA4bdlEKUIV_BaLxmIxyLESXqNexI,6345
35
+ dissect/target/filesystems/itunes.py,sha256=w2lcWv6jlBPm84tsGZehxKBMXXyuW3KlmwVTF4ssQec,6395
36
36
  dissect/target/filesystems/jffs.py,sha256=Ceqa5Em2pepnXMH_XZFmSNjQyWPo1uWTthBFSMWfKRo,3926
37
37
  dissect/target/filesystems/ntfs.py,sha256=fGgCKjdO5GrPC21DDr0SwIxmwR7KruNIqGUzysboirA,7068
38
38
  dissect/target/filesystems/overlay.py,sha256=-dqWuMWLcq3usKbJYh0MW-qyp4dfLlOAh2z6FjNPu9I,4314
39
39
  dissect/target/filesystems/smb.py,sha256=uxfcOWwEoDCw8Qpsa94T5Pn-SKd4WXs4OOrzVVI55d8,6406
40
40
  dissect/target/filesystems/squashfs.py,sha256=ehzlThXB7n96XUvQnsK5tWLsA9HIxYN-Zxl7aO9D7ts,3921
41
- dissect/target/filesystems/tar.py,sha256=kQNhcEDPX005svse039OeR2AGSDigGuGz2AKoVrgg84,5692
41
+ dissect/target/filesystems/tar.py,sha256=EJyvRCU6H7eu0exC0tQggyAZKZ3JFFaihYyx9SIQNqk,5742
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
@@ -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=y-k8ni04pHMODkgN7clTqdx-3tnFbUr0ubjJEaWOOqY,20504
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.dev48.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
360
+ dissect.target-3.19.dev48.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
361
+ dissect.target-3.19.dev48.dist-info/METADATA,sha256=rm0EHsiugMzObZ-OiOxBChll3DHKnBCw6oJm3dU2e4s,12897
362
+ dissect.target-3.19.dev48.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
363
+ dissect.target-3.19.dev48.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
364
+ dissect.target-3.19.dev48.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
365
+ dissect.target-3.19.dev48.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