dissect.target 3.16.dev23__py3-none-any.whl → 3.16.dev25__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.
@@ -1588,5 +1588,7 @@ register("btrfs", "BtrfsFilesystem")
1588
1588
  register("exfat", "ExfatFilesystem")
1589
1589
  register("squashfs", "SquashFSFilesystem")
1590
1590
  register("zip", "ZipFilesystem")
1591
+ register("tar", "TarFilesystem")
1592
+ register("cpio", "CpioFilesystem")
1591
1593
  register("ad1", "AD1Filesystem")
1592
1594
  register("jffs", "JFFSFilesystem")
@@ -0,0 +1,18 @@
1
+ from typing import BinaryIO, Optional
2
+
3
+ from dissect.util import cpio
4
+
5
+ from dissect.target.filesystems.tar import TarFilesystem
6
+ from dissect.target.helpers.fsutil import open_decompress
7
+
8
+
9
+ class CpioFilesystem(TarFilesystem):
10
+ __type__ = "cpio"
11
+
12
+ def __init__(self, fh: BinaryIO, base: Optional[str] = None, *args, **kwargs):
13
+ super().__init__(open_decompress(fileobj=fh), base, tarinfo=cpio.CpioInfo, *args, **kwargs)
14
+
15
+ @staticmethod
16
+ def _detect(fh: BinaryIO) -> bool:
17
+ """Detect a cpio file on a given file-like object."""
18
+ return cpio.detect_header(open_decompress(fileobj=fh)) != cpio.FORMAT_CPIO_UNKNOWN
@@ -20,6 +20,13 @@ try:
20
20
  except ImportError:
21
21
  HAVE_BZ2 = False
22
22
 
23
+ try:
24
+ import zstandard
25
+
26
+ HAVE_ZSTD = True
27
+ except ImportError:
28
+ HAVE_ZSTD = False
29
+
23
30
  import dissect.target.filesystem as filesystem
24
31
  from dissect.target.exceptions import FileNotFoundError, SymlinkRecursionError
25
32
  from dissect.target.helpers.polypath import (
@@ -445,17 +452,22 @@ def resolve_link(
445
452
 
446
453
 
447
454
  def open_decompress(
448
- path: TargetPath,
455
+ path: Optional[TargetPath] = None,
449
456
  mode: str = "rb",
457
+ *,
458
+ fileobj: Optional[BinaryIO] = None,
450
459
  encoding: Optional[str] = "UTF-8",
451
460
  errors: Optional[str] = "backslashreplace",
452
461
  newline: Optional[str] = None,
453
462
  ) -> Union[BinaryIO, TextIO]:
454
- """Open and decompress a file. Handles gz and bz2 files. Uncompressed files are opened as-is.
463
+ """Open and decompress a file. Handles gz, bz2 and zstd files. Uncompressed files are opened as-is.
464
+
465
+ When passing in an already opened ``fileobj``, the mode, encoding, errors and newline arguments are ignored.
455
466
 
456
467
  Args:
457
468
  path: The path to the file to open and decompress. It is assumed this path exists.
458
469
  mode: The mode in which to open the file.
470
+ fileobj: The file-like object to open and decompress. This is mutually exclusive with path.
459
471
  encoding: The decoding for text streams. By default UTF-8 encoding is used.
460
472
  errors: The error handling for text streams. By default we're more lenient and use ``backslashreplace``.
461
473
  newline: How newlines are handled for text streams.
@@ -469,7 +481,17 @@ def open_decompress(
469
481
  for line in open_decompress(Path("/dir/file.gz"), "rt"):
470
482
  print(line)
471
483
  """
472
- file = path.open()
484
+ if path and fileobj:
485
+ raise ValueError("path and fileobj are mutually exclusive")
486
+
487
+ if not path and not fileobj:
488
+ raise ValueError("path or fileobj is required")
489
+
490
+ if path:
491
+ file = path.open("rb")
492
+ else:
493
+ file = fileobj
494
+
473
495
  magic = file.read(4)
474
496
  file.seek(0)
475
497
 
@@ -480,13 +502,22 @@ def open_decompress(
480
502
 
481
503
  if magic[:2] == b"\x1f\x8b":
482
504
  return gzip.open(file, mode, encoding=encoding, errors=errors, newline=newline)
483
- # In a valid bz2 header the 4th byte is in the range b'1' ... b'9'.
484
- elif HAVE_BZ2 and magic[:3] == b"BZh" and 0x31 <= magic[3] <= 0x39:
505
+
506
+ if HAVE_BZ2 and magic[:3] == b"BZh" and 0x31 <= magic[3] <= 0x39:
507
+ # In a valid bz2 header the 4th byte is in the range b'1' ... b'9'.
485
508
  return bz2.open(file, mode, encoding=encoding, errors=errors, newline=newline)
486
- else:
509
+
510
+ if HAVE_ZSTD and magic[:4] in [b"\xfd\x2f\xb5\x28", b"\x28\xb5\x2f\xfd"]:
511
+ # stream_reader is not seekable, so we have to resort to the less
512
+ # efficient decompressor which returns bytes.
513
+ return io.BytesIO(zstandard.decompress(file.read()))
514
+
515
+ if path:
487
516
  file.close()
488
517
  return path.open(mode, encoding=encoding, errors=errors, newline=newline)
489
518
 
519
+ return file
520
+
490
521
 
491
522
  def reverse_readlines(fh: TextIO, chunk_size: int = 1024 * 1024 * 8) -> Iterator[str]:
492
523
  """Like iterating over a ``TextIO`` file-like object, but starting from the end of the file.
dissect/target/plugin.py CHANGED
@@ -932,6 +932,8 @@ class NamespacePlugin(Plugin):
932
932
  try:
933
933
  subplugin = getattr(self.target, entry)
934
934
  self._subplugins.append(subplugin)
935
+ except UnsupportedPluginError:
936
+ target.log.warning("Subplugin %s is not compatible with target.", entry)
935
937
  except Exception:
936
938
  target.log.exception("Failed to load subplugin: %s", entry)
937
939
 
File without changes
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import BinaryIO, Iterable
4
+
5
+ from dissect.cstruct import cstruct
6
+
7
+ from dissect.target.exceptions import UnsupportedPluginError
8
+ from dissect.target.helpers.record import TargetRecordDescriptor
9
+ from dissect.target.plugin import export
10
+ from dissect.target.plugins.os.unix.locate.locate import BaseLocatePlugin
11
+
12
+ gnulocate_def = """
13
+ #define MAGIC 0x004c4f43415445303200 /* b'/x00LOCATE02/x00' */
14
+
15
+ struct entry {
16
+ int8 offset;
17
+ char path[];
18
+ }
19
+ """
20
+
21
+ GNULocateRecord = TargetRecordDescriptor(
22
+ "linux/locate/gnulocate",
23
+ [
24
+ ("path", "path"),
25
+ ("string", "source"),
26
+ ],
27
+ )
28
+
29
+ c_gnulocate = cstruct()
30
+ c_gnulocate.load(gnulocate_def)
31
+
32
+
33
+ class GNULocateFile:
34
+ """locate file parser
35
+
36
+ Multiple formats exist for the locatedb file. This class only supports the most recent version ``LOCATE02``.
37
+
38
+ The file is encoded with front compression (incremental encoding). This is a form of compression
39
+ which takes a number of characters of the previous encoded entries. Entries are separated with a null byte.
40
+
41
+ Resources:
42
+ - https://manpages.ubuntu.com/manpages/trusty/en/man5/locatedb.5.html
43
+ """
44
+
45
+ def __init__(self, fh: BinaryIO):
46
+ self.fh = fh
47
+ self.count = 0
48
+ self.previous_path = ""
49
+
50
+ magic = int.from_bytes(self.fh.read(10), byteorder="big")
51
+ if magic != c_gnulocate.MAGIC:
52
+ raise ValueError(f"Invalid Locate file magic. Expected /x00LOCATE02/x00, got {magic}")
53
+
54
+ def __iter__(self) -> Iterable[GNULocateFile]:
55
+ try:
56
+ while True:
57
+ # NOTE: The offset could be negative, which indicates
58
+ # that we decrease the number of characters of the previous path.
59
+ entry = c_gnulocate.entry(self.fh)
60
+ current_filepath_end = entry.path.decode(errors="backslashreplace")
61
+ offset = entry.offset
62
+
63
+ self.count += offset
64
+
65
+ path = self.previous_path[0 : self.count] + current_filepath_end
66
+ self.previous_path = path
67
+ yield path
68
+ except EOFError:
69
+ return
70
+
71
+
72
+ class GNULocatePlugin(BaseLocatePlugin):
73
+ __namespace__ = "gnulocate"
74
+
75
+ path = "/var/cache/locate/locatedb"
76
+
77
+ def check_compatible(self) -> None:
78
+ if not self.target.fs.path(self.path).exists():
79
+ raise UnsupportedPluginError(f"No locatedb file found at {self.path}")
80
+
81
+ @export(record=GNULocateRecord)
82
+ def locate(self) -> GNULocateRecord:
83
+ """Yield file and directory names from GNU findutils' locatedb file.
84
+
85
+ Resources:
86
+ - https://manpages.debian.org/testing/locate/locatedb.5.en.html
87
+ """
88
+ locate_fh = self.target.fs.path(self.path).open()
89
+ locate_file = GNULocateFile(locate_fh)
90
+
91
+ for path in locate_file:
92
+ yield GNULocateRecord(path=self.target.fs.path(path), source=self.path)
@@ -0,0 +1,5 @@
1
+ from dissect.target.plugin import NamespacePlugin
2
+
3
+
4
+ class BaseLocatePlugin(NamespacePlugin):
5
+ __namespace__ = "locate"
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from typing import BinaryIO, Iterable, Iterator
6
+
7
+ from dissect.cstruct import cstruct
8
+ from dissect.util.ts import from_unix
9
+
10
+ from dissect.target.exceptions import UnsupportedPluginError
11
+ from dissect.target.helpers.record import TargetRecordDescriptor
12
+ from dissect.target.plugin import export
13
+ from dissect.target.plugins.os.unix.locate.locate import BaseLocatePlugin
14
+
15
+ # Resources: https://linux.die.net/man/5/locate.db
16
+ mlocate_def = """
17
+ #define MAGIC 0x006d6c6f63617465 /* b'/x00mlocate' */
18
+
19
+ struct header_config {
20
+ int32 conf_size;
21
+ int8 version; /* file format version */
22
+ int8 require_visibility;
23
+ int8 pad[2]; /* 32-bit total alignment */
24
+ char root_database;
25
+ char config_block[conf_size];
26
+ int8 pad;
27
+ };
28
+
29
+ enum DBE_TYPE: uint8 { /* database entry type */
30
+ FILE = 0x0, /* file */
31
+ DIRECTORY = 0x1, /* directory */
32
+ END = 0x2 /* end of directory */
33
+ };
34
+
35
+ struct directory_entry {
36
+ /* time is the 'maximum of st_ctime and
37
+ st_mtime of the directory' according to docs */
38
+ int64 time_seconds;
39
+ int32 time_nanoseconds;
40
+ int32 padding;
41
+ char path[];
42
+ };
43
+
44
+ struct entry {
45
+ char path[];
46
+ };
47
+ """
48
+
49
+
50
+ @dataclass
51
+ class MLocate:
52
+ ts: datetime
53
+ ts_ns: int
54
+ parent: str
55
+ path: str
56
+ dbe_type: str
57
+
58
+
59
+ MLocateRecord = TargetRecordDescriptor(
60
+ "linux/locate/mlocate",
61
+ [
62
+ ("datetime", "ts"),
63
+ ("varint", "ts_ns"),
64
+ ("path", "parent"),
65
+ ("path", "path"),
66
+ ("string", "type"),
67
+ ("string", "source"),
68
+ ],
69
+ )
70
+
71
+ c_mlocate = cstruct(endian=">")
72
+ c_mlocate.load(mlocate_def)
73
+
74
+
75
+ class MLocateFile:
76
+ """mlocate file parser
77
+
78
+ Resources:
79
+ - https://manpages.debian.org/testing/mlocate/mlocate.db.5.en.html
80
+ """
81
+
82
+ def __init__(self, fh: BinaryIO):
83
+ self.fh = fh
84
+
85
+ magic = int.from_bytes(self.fh.read(8), byteorder="big")
86
+ if magic != c_mlocate.MAGIC:
87
+ raise ValueError(f"Invalid mlocate file magic. Expected b'x00mlocate', got {magic}")
88
+
89
+ self.header = c_mlocate.header_config(self.fh)
90
+
91
+ def _parse_directory_entries(self) -> Iterator[str, c_mlocate.entry]:
92
+ while (dbe_type := c_mlocate.DBE_TYPE(self.fh)) != c_mlocate.DBE_TYPE.END:
93
+ entry = c_mlocate.entry(self.fh)
94
+ dbe_type = "file" if dbe_type == c_mlocate.DBE_TYPE.FILE else "directory"
95
+
96
+ yield dbe_type, entry
97
+
98
+ def __iter__(self) -> Iterable[MLocateFile]:
99
+ while True:
100
+ try:
101
+ directory_entry = c_mlocate.directory_entry(self.fh)
102
+ parent = directory_entry.path.decode()
103
+
104
+ for dbe_type, file_entry in self._parse_directory_entries():
105
+ file_path = file_entry.path.decode()
106
+
107
+ yield MLocate(
108
+ ts=from_unix(directory_entry.time_seconds),
109
+ ts_ns=directory_entry.time_nanoseconds,
110
+ parent=parent,
111
+ path=file_path,
112
+ dbe_type=dbe_type,
113
+ )
114
+ except EOFError:
115
+ return
116
+
117
+
118
+ class MLocatePlugin(BaseLocatePlugin):
119
+ __namespace__ = "mlocate"
120
+
121
+ path = "/var/lib/mlocate/mlocate.db"
122
+
123
+ def check_compatible(self) -> None:
124
+ if not self.target.fs.path(self.path).exists():
125
+ raise UnsupportedPluginError(f"No mlocate.db file found at {self.path}")
126
+
127
+ @export(record=MLocateRecord)
128
+ def locate(self) -> Iterator[MLocateRecord]:
129
+ """Yield file and directory names from mlocate.db file.
130
+
131
+ ``mlocate`` is a new implementation of GNU locate,
132
+ but has been deprecated since Ubuntu 22.
133
+
134
+ Resources:
135
+ - https://manpages.debian.org/testing/mlocate/mlocate.db.5.en.html
136
+ """
137
+ mlocate_fh = self.target.fs.path(self.path).open()
138
+ mlocate_file = MLocateFile(mlocate_fh)
139
+
140
+ for item in mlocate_file:
141
+ parent = self.target.fs.path(item.parent)
142
+ yield MLocateRecord(
143
+ ts=item.ts,
144
+ ts_ns=item.ts_ns,
145
+ parent=parent,
146
+ path=parent.joinpath(item.path),
147
+ type=item.dbe_type,
148
+ source=self.path,
149
+ _target=self.target,
150
+ )
@@ -0,0 +1,189 @@
1
+ from __future__ import annotations
2
+
3
+ import platform
4
+ import sys
5
+ from io import BytesIO
6
+ from typing import BinaryIO, Iterable
7
+
8
+ from dissect.cstruct import cstruct
9
+ from dissect.util.stream import RangeStream
10
+
11
+ from dissect.target.exceptions import UnsupportedPluginError
12
+ from dissect.target.helpers.record import TargetRecordDescriptor
13
+ from dissect.target.plugin import export
14
+ from dissect.target.plugins.os.unix.locate.locate import BaseLocatePlugin
15
+
16
+ try:
17
+ import zstandard # noqa
18
+
19
+ HAS_ZSTD = True
20
+ except ImportError:
21
+ HAS_ZSTD = False
22
+
23
+ # Resource: https://git.sesse.net/?p=plocate @ db.h
24
+ plocate_def = """
25
+ #define MAGIC 0x00706c6f63617465 /* b'/x00plocate' */
26
+
27
+ struct header {
28
+ uint32_t version;
29
+ uint32_t hashtable_size;
30
+ uint32_t extra_ht_slots;
31
+ uint32_t num_docids;
32
+ uint64_t hash_table_offset_bytes;
33
+ uint64_t filename_index_offset_bytes;
34
+
35
+ /* Version 1 and up only. */
36
+ uint32_t max_version;
37
+ uint32_t zstd_dictionary_length_bytes;
38
+ uint64_t zstd_dictionary_offset_bytes;
39
+
40
+ /* Only if max_version >= 2, and only relevant for updatedb. */
41
+ uint64_t directory_data_length_bytes;
42
+ uint64_t directory_data_offset_bytes;
43
+ uint64_t next_zstd_dictionary_length_bytes;
44
+ uint64_t next_zstd_dictionary_offset_bytes;
45
+ uint64_t conf_block_length_bytes;
46
+ uint64_t conf_block_offset_bytes;
47
+
48
+ uint8_t check_visibility;
49
+ char padding[7]; /* padding for alignment */
50
+ };
51
+
52
+ struct file {
53
+ char path[];
54
+ };
55
+ """
56
+
57
+ PLocateRecord = TargetRecordDescriptor(
58
+ "linux/locate/plocate",
59
+ [
60
+ ("path", "path"),
61
+ ("path", "source"),
62
+ ],
63
+ )
64
+
65
+ c_plocate = cstruct()
66
+ c_plocate.load(plocate_def)
67
+
68
+
69
+ class PLocateFile:
70
+ """plocate file parser
71
+
72
+ The ``plocate.db`` file contains a hashtable and trigrams to enable quick lookups of filenames.
73
+
74
+ We've implemented a few methods to gather those for possible future use, but for the PLocatePlugin
75
+ we're only interested in the filepaths stored in the database. Hence we don't use these methods.
76
+
77
+ Roughly speaking, the plocate.db file has the following structure:
78
+ - ``header`` (0x70 bytes)
79
+ - zstd compressed ``filename``s (until start of ``filename_index_offset_bytes``),
80
+ possibly including a dictionary
81
+ - hashtables (offset and length in ``header``)
82
+ - directory data (offset and length in ``header``)
83
+ - possible zstd dictionary (offset and length in ``header``)
84
+ - configuration block (offset and length in ``header``)
85
+
86
+ No documentation other than the source code is available on the format of this file.
87
+
88
+ Resources:
89
+ - https://git.sesse.net/?p=plocate
90
+ """
91
+
92
+ HEADER_SIZE = 0x70 # 0x8 bytes magic + 0x68 bytes header
93
+ NUM_OVERFLOW_SLOTS = 16
94
+ TRIGRAM_SIZE_BYTES = 16
95
+ DOCID_SIZE_BYTES = 8
96
+
97
+ def __init__(self, fh: BinaryIO):
98
+ self.fh = fh
99
+
100
+ magic = int.from_bytes(self.fh.read(8), byteorder="big")
101
+ if magic != c_plocate.MAGIC:
102
+ raise ValueError(f"Invalid plocate file magic. Expected b'/x00plocate', got {magic}")
103
+
104
+ self.header = c_plocate.header(self.fh)
105
+ self.dict_data = None
106
+
107
+ if self.header.zstd_dictionary_offset_bytes:
108
+ self.dict_data = zstandard.ZstdCompressionDict(self.fh.read(self.header.zstd_dictionary_length_bytes))
109
+
110
+ self.compressed_length_bytes = (
111
+ self.header.filename_index_offset_bytes - self.HEADER_SIZE - self.header.zstd_dictionary_length_bytes
112
+ )
113
+ self.ctx = zstandard.ZstdDecompressor(dict_data=self.dict_data)
114
+ self.buf = RangeStream(self.fh, self.fh.tell(), self.compressed_length_bytes)
115
+
116
+ def __iter__(self) -> Iterable[PLocateFile]:
117
+ # NOTE: This is a workaround for a PyPy 3.9 bug
118
+ # We don't know what breaks, but PyPy + zstandard = unhappy times
119
+ # You just get random garbage data back instead of the decompressed data
120
+ # This weird dance of using a decompressobj and unused data is the only way that seems to work
121
+ # It's more expensive on memory, but at least it doesn't break
122
+ if platform.python_implementation() == "PyPy" and sys.version_info < (3, 10):
123
+ obj = self.ctx.decompressobj()
124
+ buf = self.buf.read()
125
+
126
+ tmp = obj.decompress(buf)
127
+ while unused_data := obj.unused_data:
128
+ obj = self.ctx.decompressobj()
129
+ tmp += obj.decompress(unused_data)
130
+
131
+ reader = BytesIO(tmp)
132
+ else:
133
+ reader = self.ctx.stream_reader(self.buf)
134
+
135
+ with reader:
136
+ try:
137
+ while True:
138
+ file = c_plocate.file(reader)
139
+ yield file.path.decode(errors="surrogateescape")
140
+ except EOFError:
141
+ return
142
+
143
+ def filename_index(self) -> bytes:
144
+ """Return the filename index of the plocate.db file."""
145
+ self.fh.seek(self.header.filename_index_offset_bytes)
146
+ num_docids = self.header.num_docids
147
+ filename_index_size = num_docids * self.DOCID_SIZE_BYTES
148
+ return self.fh.read(filename_index_size)
149
+
150
+ def hashtable(self) -> bytes:
151
+ """Return the hashtable of the plocate.db file."""
152
+ self.fh.seek(self.header.hash_table_offset_bytes)
153
+ hashtable_size = (self.header.hashtable_size + self.NUM_OVERFLOW_SLOTS + 1) * self.TRIGRAM_SIZE_BYTES
154
+ return self.fh.read(hashtable_size)
155
+
156
+
157
+ class PLocatePlugin(BaseLocatePlugin):
158
+ __namespace__ = "plocate"
159
+
160
+ path = "/var/lib/plocate/plocate.db"
161
+
162
+ def check_compatible(self) -> None:
163
+ if not self.target.fs.path(self.path).exists():
164
+ raise UnsupportedPluginError(f"No plocate.db file found at {self.path}")
165
+
166
+ if not HAS_ZSTD:
167
+ raise UnsupportedPluginError(
168
+ "Please install `python-zstandard` or `pip install zstandard` to use the PLocatePlugin"
169
+ )
170
+
171
+ @export(record=PLocateRecord)
172
+ def locate(self) -> PLocateRecord:
173
+ """Yield file and directory names from the plocate.db.
174
+
175
+ ``plocate`` is the default package on Ubuntu 22 and newer to locate files.
176
+ It replaces ``mlocate`` and GNU ``locate``.
177
+
178
+ Resources:
179
+ - https://manpages.debian.org/testing/plocate/plocate.1.en.html
180
+ - https://git.sesse.net/?p=plocate
181
+ """
182
+ plocate = self.target.fs.path(self.path)
183
+ plocate_file = PLocateFile(plocate.open())
184
+
185
+ for path in plocate_file:
186
+ yield PLocateRecord(
187
+ path=self.target.fs.path(path),
188
+ source=self.path,
189
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.16.dev23
3
+ Version: 3.16.dev25
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,9 +1,9 @@
1
1
  dissect/target/__init__.py,sha256=Oc7ounTgq2hE4nR6YcNabetc7SQA40ldSa35VEdZcQU,63
2
2
  dissect/target/container.py,sha256=9ixufT1_0WhraqttBWwQjG80caToJqvCX8VjFk8d5F0,9307
3
3
  dissect/target/exceptions.py,sha256=VVW_Rq_vQinapz-2mbJ3UkxBEZpb2pE_7JlhMukdtrY,2877
4
- dissect/target/filesystem.py,sha256=aLkvZMgeah39Nhlscawh77cm2mzFYI9J5h3uT3Rigtc,53876
4
+ dissect/target/filesystem.py,sha256=VuWp_nxaG5WI9xvhEjl0M6X37_sVdclzgt3pZvtZ3hE,53944
5
5
  dissect/target/loader.py,sha256=0-LcZNi7S0qsXR7XGtrzxpuCh9BsLcqNR1T15O7SnBM,7257
6
- dissect/target/plugin.py,sha256=ndqz4RpbBCN6wagCBvfHzHkL0l0-gnbHjc7c8Blite4,48473
6
+ dissect/target/plugin.py,sha256=bwtTPASgJBUHBZ7Nr8eb5eXDOHMWfumduWj7loB0FP0,48605
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
8
8
  dissect/target/target.py,sha256=xNJdecZSt2oHcZwf775kOSTFRA-c_hKoScXaDuK-8FI,32155
9
9
  dissect/target/volume.py,sha256=aQZAJiny8jjwkc9UtwIRwy7nINXjCxwpO-_UDfh6-BA,15801
@@ -25,6 +25,7 @@ dissect/target/filesystems/ad1.py,sha256=nEPzaaRsb6bL4ItFo0uLdmdLvrmK9BjqHeD3FOp
25
25
  dissect/target/filesystems/btrfs.py,sha256=5MBi193ZvclkEQcxDr_sDHfj_FYU_hyYNRL4YqpDu4M,6243
26
26
  dissect/target/filesystems/cb.py,sha256=6LcoJiwsYu1Han31IUzVpZVDTifhTLTx_gLfNpB_p6k,5329
27
27
  dissect/target/filesystems/config.py,sha256=C2JnzBzMqbAjchGFDwURItCeUY7uxkhw1Gen-6cGkAc,11432
28
+ dissect/target/filesystems/cpio.py,sha256=ssVCjkAtLn2FqmNxeo6U5boyUdSYFxLWfXpytHYGPqs,641
28
29
  dissect/target/filesystems/dir.py,sha256=7GRvojL151_Vk9e3vqgZbWE3I8IL9bU6LUKc_xjk6D4,4050
29
30
  dissect/target/filesystems/exfat.py,sha256=PRkZPUVN5NlgB1VetFtywdNgF6Yj5OBtF5a25t-fFvw,5917
30
31
  dissect/target/filesystems/extfs.py,sha256=9Cke-H0CL-SPd3-xvdAgfc3YA5hYso0sq6hm0C9vGII,4640
@@ -46,7 +47,7 @@ dissect/target/helpers/configutil.py,sha256=t_UNvcWuMMT5C1tut_PgTwCnVUodf6RjhfXP
46
47
  dissect/target/helpers/cyber.py,sha256=Ki5oSU0GgQxjgC_yWoeieGP7GOY5blQCzNX7vy7Pgas,16782
47
48
  dissect/target/helpers/descriptor_extensions.py,sha256=uT8GwznfDAiIgMM7JKKOY0PXKMv2c0GCqJTCkWFgops,2605
48
49
  dissect/target/helpers/docs.py,sha256=J5U65Y3yOTqxDEZRCdrEmO63XQCeDzOJea1PwPM6Cyc,5146
49
- dissect/target/helpers/fsutil.py,sha256=jGinb11-w6TvbzH7z-9F6J09X5CY3_yxBoNsKxsFAXE,18637
50
+ dissect/target/helpers/fsutil.py,sha256=NKwpAq_NlbFDvGGV9ahC5flPda_5jUBDqtF-Ch5ASDs,19543
50
51
  dissect/target/helpers/hashutil.py,sha256=SD24rcV_y0sBEl7M9T-isjm-VzJvCiTN2BoWMqAOAVI,2160
51
52
  dissect/target/helpers/keychain.py,sha256=wYH0sf7eaxP0bZTo80RF_BQMWulCWmIQ8Tzt9K5TSNQ,3611
52
53
  dissect/target/helpers/lazy.py,sha256=823VtmdWsbJyVZvNWopDhQdqq2i1xtj6b8IKfveboKw,1771
@@ -230,6 +231,11 @@ dissect/target/plugins/os/unix/linux/redhat/yum.py,sha256=kEvB-C2CNoqxSbgGRZiuo6
230
231
  dissect/target/plugins/os/unix/linux/suse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
231
232
  dissect/target/plugins/os/unix/linux/suse/_os.py,sha256=eaqgnkbunBJ2Hf_GE96THjfT3ybVIZvtWId-dx3JMV4,575
232
233
  dissect/target/plugins/os/unix/linux/suse/zypper.py,sha256=amepAWivvbHFt2AoJUHC8lIeuD5Iy8MFXTWKqTYAEqE,4142
234
+ dissect/target/plugins/os/unix/locate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
235
+ dissect/target/plugins/os/unix/locate/gnulocate.py,sha256=P-YbMFw901p2EBgTaZH6axShfIRRDrCx3APBy6Ii3lE,2934
236
+ dissect/target/plugins/os/unix/locate/locate.py,sha256=uXFcWAqoz_3eNWHhsGoEtkkhmT5J3F1GYvr4uQxi308,122
237
+ dissect/target/plugins/os/unix/locate/mlocate.py,sha256=DhrFgxDQF-fMZaA0WK8Z-5o9i9iDsuTHW7MHJtWwz6o,4485
238
+ dissect/target/plugins/os/unix/locate/plocate.py,sha256=jUIqG-vD66hFn14SkQpJ2rfJnbtwOBMkJcaduWLtnlw,6591
233
239
  dissect/target/plugins/os/unix/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
234
240
  dissect/target/plugins/os/unix/log/atop.py,sha256=UmaqdnSmE8AO8bEj4drGSc1HH2n4Pdlxpwfa7RgraIY,16314
235
241
  dissect/target/plugins/os/unix/log/audit.py,sha256=OjorWTmCFvCI5RJq6m6WNW0Lhb-poB2VAggKOGZUHK4,3722
@@ -325,10 +331,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
325
331
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
326
332
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
327
333
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
328
- dissect.target-3.16.dev23.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
329
- dissect.target-3.16.dev23.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
330
- dissect.target-3.16.dev23.dist-info/METADATA,sha256=0sRgs6_clcf3PUsMUj5HEJkVmN398nl1HOMUtvwTe48,11113
331
- dissect.target-3.16.dev23.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
332
- dissect.target-3.16.dev23.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
333
- dissect.target-3.16.dev23.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
334
- dissect.target-3.16.dev23.dist-info/RECORD,,
334
+ dissect.target-3.16.dev25.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
335
+ dissect.target-3.16.dev25.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
336
+ dissect.target-3.16.dev25.dist-info/METADATA,sha256=dlfz79OtrmH132ugeqPIQsRDdu-FgcSqIbt0i1AdSgo,11113
337
+ dissect.target-3.16.dev25.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
338
+ dissect.target-3.16.dev25.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
339
+ dissect.target-3.16.dev25.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
340
+ dissect.target-3.16.dev25.dist-info/RECORD,,