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.
- dissect/target/filesystem.py +44 -25
- dissect/target/filesystems/itunes.py +1 -1
- dissect/target/filesystems/tar.py +1 -1
- dissect/target/helpers/fsutil.py +30 -4
- dissect/target/plugins/filesystem/unix/capability.py +102 -87
- dissect/target/plugins/filesystem/walkfs.py +32 -21
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/METADATA +1 -1
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/RECORD +13 -13
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/WHEEL +1 -1
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/LICENSE +0 -0
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/top_level.txt +0 -0
dissect/target/filesystem.py
CHANGED
@@ -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
|
-
"""
|
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
|
-
"""
|
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
|
-
"""
|
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
|
-
"""
|
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(
|
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(
|
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(
|
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(
|
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."""
|
dissect/target/helpers/fsutil.py
CHANGED
@@ -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,
|
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(
|
436
|
-
path = normalize(
|
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(
|
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
|
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
|
-
("
|
12
|
+
("datetime", "ts_mtime"),
|
13
|
+
("path", "path"),
|
14
14
|
("string[]", "permitted"),
|
15
15
|
("string[]", "inheritable"),
|
16
16
|
("boolean", "effective"),
|
17
|
-
("uint32", "
|
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")
|
86
|
-
raise UnsupportedPluginError("
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
101
|
-
except
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
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
|
32
|
+
raise UnsupportedPluginError("No filesystems to walk")
|
34
33
|
|
35
34
|
@export(record=FilesystemRecord)
|
36
|
-
|
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
|
39
|
-
|
40
|
-
|
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=
|
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.
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
360
|
-
dissect.target-3.19.
|
361
|
-
dissect.target-3.19.
|
362
|
-
dissect.target-3.19.
|
363
|
-
dissect.target-3.19.
|
364
|
-
dissect.target-3.19.
|
365
|
-
dissect.target-3.19.
|
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,,
|
File without changes
|
File without changes
|
{dissect.target-3.19.dev46.dist-info → dissect.target-3.19.dev48.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|