dissect.target 3.19.dev46__py3-none-any.whl → 3.19.dev48__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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