Skip to content

Commit b805239

Browse files
authored
Add target-inspect (#1374)
1 parent b06a57c commit b805239

File tree

5 files changed

+288
-31
lines changed

5 files changed

+288
-31
lines changed

dissect/target/target.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def name(self) -> str:
269269
return target_name
270270

271271
@classmethod
272-
def open(cls, path: str | Path) -> Self:
272+
def open(cls, path: str | Path, *, apply: bool = True) -> Self:
273273
"""Try to find a suitable loader for the given path and load a ``Target`` from it.
274274
275275
Args:
@@ -307,20 +307,22 @@ def open(cls, path: str | Path) -> Self:
307307
except Exception as e:
308308
raise TargetError(f"Failed to initiate {loader_cls.__name__} for target {spec}: {e}") from e
309309

310-
return cls._load(spec, loader_instance)
310+
return cls._load(spec, loader_instance, apply=apply)
311311

312312
@classmethod
313-
def open_raw(cls, path: str | Path) -> Self:
313+
def open_raw(cls, path: str | Path, *, apply: bool = True) -> Self:
314314
"""Open a Target with the given path using the :class:`~dissect.target.loaders.raw.RawLoader`.
315315
316316
Args:
317317
path: Path to load the ``Target`` from.
318318
"""
319319
adjusted_path = Path(path) if not isinstance(path, os.PathLike) else path
320-
return cls._load(path, loader.RawLoader(adjusted_path))
320+
return cls._load(path, loader.RawLoader(adjusted_path), apply=apply)
321321

322322
@classmethod
323-
def open_all(cls, paths: str | Path | list[str | Path], include_children: bool = False) -> Iterator[Self]:
323+
def open_all(
324+
cls, paths: str | Path | list[str | Path], include_children: bool = False, *, apply: bool = True
325+
) -> Iterator[Self]:
324326
"""Yield all targets from one or more paths or directories.
325327
326328
If the path is a directory, iterate files one directory deep.
@@ -336,7 +338,7 @@ def open_all(cls, paths: str | Path | list[str | Path], include_children: bool =
336338
TargetError: Raised when not a single ``Target`` can be loaded.
337339
"""
338340

339-
def _open_all(spec: str | Path, include_children: bool = False) -> Iterator[Target]:
341+
def _open_all(spec: str | Path, include_children: bool = False, *, apply: bool = True) -> Iterator[Target]:
340342
# If the path is a URI-like string, separate the path component
341343
adjusted_path, parsed_path = parse_path_uri(spec)
342344
# We always need a path to work with, so convert the spec into one if it's not one already
@@ -390,7 +392,9 @@ def _open_all(spec: str | Path, include_children: bool = False) -> Iterator[Targ
390392
# load_spec is the original "spec"
391393
# For URI-like paths it's the full original URI
392394
# For file/dir-like paths it's a Path object
393-
target = cls._load(load_spec, ldr)
395+
# If include_children is True, we override the apply parameter so we can find child targets
396+
# The apply parameter will then be used on the children
397+
target = cls._load(load_spec, ldr, apply=include_children or apply)
394398
except Exception as e:
395399
get_target_logger(load_spec).error("Failed to load target with loader %s", ldr)
396400
get_target_logger(load_spec).debug("", exc_info=e)
@@ -400,7 +404,7 @@ def _open_all(spec: str | Path, include_children: bool = False) -> Iterator[Targ
400404

401405
if include_children:
402406
try:
403-
yield from target.open_children()
407+
yield from target.open_children(apply=apply)
404408
except Exception as e:
405409
get_target_logger(load_spec).error("Failed to load child target from %s", target, exc_info=e)
406410

@@ -413,7 +417,7 @@ def _open_all(spec: str | Path, include_children: bool = False) -> Iterator[Targ
413417
for spec in paths:
414418
loaded = False
415419

416-
for target in _open_all(spec, include_children=include_children):
420+
for target in _open_all(spec, include_children=include_children, apply=apply):
417421
loaded = True
418422
at_least_one_loaded = True
419423
yield target
@@ -434,7 +438,7 @@ def _open_all(spec: str | Path, include_children: bool = False) -> Iterator[Targ
434438

435439
if path.is_dir():
436440
for entry in path.iterdir():
437-
for target in _open_all(entry, include_children=include_children):
441+
for target in _open_all(entry, include_children=include_children, apply=apply):
438442
at_least_one_loaded = True
439443
yield target
440444

@@ -492,7 +496,7 @@ def _load_child_plugins(self) -> None:
492496
)
493497
self.log.debug("", exc_info=e)
494498

495-
def open_child(self, child: int | str | Path) -> Target:
499+
def open_child(self, child: int | str | Path, *, apply: bool = True) -> Target:
496500
"""Open a child target.
497501
498502
Allows opening a nested child target by path, index or child pattern.
@@ -516,7 +520,7 @@ def open_child(self, child: int | str | Path) -> Target:
516520
for child_idx in map(int, str(child).split(".")):
517521
for _, child in current_target.list_children():
518522
if child_idx == 0:
519-
current_target = Target.open(current_target.fs.path(child.path))
523+
current_target = Target.open(current_target.fs.path(child.path), apply=apply)
520524
break
521525
child_idx -= 1
522526
else:
@@ -525,9 +529,9 @@ def open_child(self, child: int | str | Path) -> Target:
525529
return current_target
526530

527531
# Open child by path
528-
return Target.open(self.fs.path(child))
532+
return Target.open(self.fs.path(child), apply=apply)
529533

530-
def open_children(self, recursive: bool = False) -> Iterator[Target]:
534+
def open_children(self, recursive: bool = False, *, apply: bool = True) -> Iterator[Target]:
531535
"""Open all the child targets on a ``Target``.
532536
533537
Will open all discovered child targets if the current ``Target`` has them, such as VMs on a hypervisor.
@@ -540,15 +544,15 @@ def open_children(self, recursive: bool = False) -> Iterator[Target]:
540544
"""
541545
for _, child in self.list_children():
542546
try:
543-
target = self.open_child(child.path)
547+
target = self.open_child(child.path, apply=apply)
544548
except TargetError:
545549
self.log.exception("Failed to open child target %s", child)
546550
continue
547551

548552
yield target
549553

550554
if recursive:
551-
yield from target.open_children(recursive=recursive)
555+
yield from target.open_children(recursive=recursive, apply=apply)
552556

553557
def list_children(self, recursive: bool = False) -> Iterator[tuple[str, ChildTargetRecord]]:
554558
"""Lists all discovered child targets."""
@@ -569,7 +573,7 @@ def list_children(self, recursive: bool = False) -> Iterator[tuple[str, ChildTar
569573

570574
idx += 1
571575

572-
def reload(self) -> Target:
576+
def reload(self, *, apply: bool = True) -> Target:
573577
"""Reload the current target.
574578
575579
Using the loader with which the target was originally loaded,
@@ -584,12 +588,12 @@ def reload(self) -> Target:
584588
"""
585589

586590
if self._loader and self.path:
587-
return self._load(self.path, self._loader)
591+
return self._load(self.path, self._loader, apply=apply)
588592

589593
raise TargetError("Target has no path and/or loader")
590594

591595
@classmethod
592-
def _load(cls, path: str | Path | None, ldr: loader.Loader) -> Self:
596+
def _load(cls, path: str | Path | None, ldr: loader.Loader, *, apply: bool = True) -> Self:
593597
"""Internal function that attemps to load a path using a given loader.
594598
595599
Args:
@@ -607,7 +611,9 @@ def _load(cls, path: str | Path | None, ldr: loader.Loader) -> Self:
607611
try:
608612
ldr.map(target)
609613
target._loader = ldr
610-
target.apply()
614+
615+
if apply:
616+
target.apply()
611617
except Exception as e:
612618
raise TargetError(f"Failed to load target: {path}") from e
613619
else:
@@ -893,6 +899,7 @@ def __repr__(self) -> str:
893899

894900
class DiskCollection(Collection[container.Container]):
895901
def apply(self) -> None:
902+
"""Identify (basic) volume systems on all disks and add their volumes to the volume collection."""
896903
for disk in self.entries:
897904
# Some LVM configurations (i.e. RAID with the metadata at the end of the disk)
898905
# may be misidentified as having a valid MBR/GPT on some of the disks
@@ -921,7 +928,13 @@ def apply(self) -> None:
921928

922929

923930
class VolumeCollection(Collection[volume.Volume]):
924-
def apply(self) -> None:
931+
def apply(self, *, filesystems: bool = True) -> None:
932+
"""Identify logical and encrypted volumes on all volumes and add their volumes to the volume collection.
933+
Additionally, identify filesystems on all volumes and add them to the filesystem collection.
934+
935+
Args:
936+
filesystems: Whether to identify filesystems on the volumes.
937+
"""
925938
# We don't want later additions to modify the todo, so make a copy
926939
todo = self.entries[:]
927940
fs_volumes = []
@@ -988,6 +1001,10 @@ def apply(self) -> None:
9881001

9891002
todo = new_volumes
9901003

1004+
if not filesystems:
1005+
# Skip filesystem identification
1006+
return
1007+
9911008
mv_fs_volumes = []
9921009
for vol in fs_volumes:
9931010
try:

0 commit comments

Comments
 (0)