dissect.target 3.17.dev19__py3-none-any.whl → 3.17.dev20__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.
@@ -525,21 +525,21 @@ class FilesystemEntry:
525
525
  follow_symlinks: Whether to resolve the entry if it is a symbolic link.
526
526
 
527
527
  Returns:
528
- The resolved symbolic link if ``follow_symlinks`` is ``True`` and the ``FilesystemEntry`` is a
529
- symbolic link or else the ``FilesystemEntry`` itself.
528
+ The resolved symbolic link if ``follow_symlinks`` is ``True`` and the :class:`FilesystemEntry` is a
529
+ symbolic link or else the :class:`FilesystemEntry` itself.
530
530
  """
531
531
  if follow_symlinks and self.is_symlink():
532
532
  return self.readlink_ext()
533
533
  return self
534
534
 
535
535
  def get(self, path: str) -> FilesystemEntry:
536
- """Retrieve a FilesystemEntry relative to this entry.
536
+ """Retrieve a :class:`FilesystemEntry` relative to this entry.
537
537
 
538
538
  Args:
539
539
  path: The path relative to this filesystem entry.
540
540
 
541
541
  Returns:
542
- A relative FilesystemEntry.
542
+ A relative :class:`FilesystemEntry`.
543
543
  """
544
544
  raise NotImplementedError()
545
545
 
@@ -560,10 +560,10 @@ class FilesystemEntry:
560
560
  raise NotImplementedError()
561
561
 
562
562
  def scandir(self) -> Iterator[FilesystemEntry]:
563
- """Iterate over the contents of a directory, return them as FilesystemEntry's.
563
+ """Iterate over the contents of a directory, yields :class:`FilesystemEntry`.
564
564
 
565
565
  Returns:
566
- An iterator of directory entries as FilesystemEntry's.
566
+ An iterator of :class:`FilesystemEntry`.
567
567
  """
568
568
  raise NotImplementedError()
569
569
 
@@ -576,10 +576,10 @@ class FilesystemEntry:
576
576
  return list(self.iterdir())
577
577
 
578
578
  def listdir_ext(self) -> list[FilesystemEntry]:
579
- """List the contents of a directory as FilesystemEntry's.
579
+ """List the contents of a directory as a list of :class:`FilesystemEntry`.
580
580
 
581
581
  Returns:
582
- A list of FilesystemEntry's.
582
+ A list of :class:`FilesystemEntry`.
583
583
  """
584
584
  return list(self.scandir())
585
585
 
@@ -614,7 +614,7 @@ class FilesystemEntry:
614
614
  onerror: Optional[Callable] = None,
615
615
  followlinks: bool = False,
616
616
  ) -> Iterator[FilesystemEntry]:
617
- """Walk a directory and show its contents as FilesystemEntry's.
617
+ """Walk a directory and show its contents as :class:`FilesystemEntry`.
618
618
 
619
619
  It walks across all the files inside the entry recursively.
620
620
 
@@ -629,11 +629,11 @@ class FilesystemEntry:
629
629
  followlinks: ``True`` if we want to follow any symbolic link
630
630
 
631
631
  Returns:
632
- An iterator of directory entries as FilesystemEntry's.
632
+ An iterator of :class:`FilesystemEntry`.
633
633
  """
634
634
  yield from fsutil.walk_ext(self, topdown, onerror, followlinks)
635
635
 
636
- def glob(self, pattern) -> Iterator[str]:
636
+ def glob(self, pattern: str) -> Iterator[str]:
637
637
  """Iterate over this directory part of ``patern``, returning entries matching ``pattern`` as strings.
638
638
 
639
639
  Args:
@@ -645,21 +645,23 @@ class FilesystemEntry:
645
645
  for entry in self.glob_ext(pattern):
646
646
  yield entry.path
647
647
 
648
- def glob_ext(self, pattern) -> Iterator[FilesystemEntry]:
649
- """Iterate over the directory part of ``pattern``, returning entries matching ``pattern`` as FilesysmteEntry's.
648
+ def glob_ext(self, pattern: str) -> Iterator[FilesystemEntry]:
649
+ """Iterate over the directory part of ``pattern``, returning entries matching
650
+ ``pattern`` as :class:`FilesysmteEntry`.
650
651
 
651
652
  Args:
652
653
  pattern: The pattern to glob for.
653
654
 
654
655
  Returns:
655
- An iterator of FilesystemEntry's that match the pattern.
656
+ An iterator of :class:`FilesystemEntry` that match the pattern.
656
657
  """
657
658
  yield from fsutil.glob_ext(self, pattern)
658
659
 
659
660
  def exists(self, path: str) -> bool:
660
661
  """Determines whether a ``path``, relative to this entry, exists.
661
662
 
662
- If the `path` is a symbolic link, it will attempt to resolve it to find the FilesystemEntry it points to.
663
+ If the `path` is a symbolic link, it will attempt to resolve it to find
664
+ the :class:`FilesystemEntry` it points to.
663
665
 
664
666
  Args:
665
667
  path: The path relative to this entry.
@@ -737,7 +739,7 @@ class FilesystemEntry:
737
739
  raise NotImplementedError()
738
740
 
739
741
  def readlink_ext(self) -> FilesystemEntry:
740
- """Read the link where this entry points to, return the resulting path as FilesystemEntry.
742
+ """Read the link where this entry points to, return the resulting path as :class:`FilesystemEntry`.
741
743
 
742
744
  If it is a symlink and returns the string that corresponds to that path.
743
745
  This means it follows the path a link points to, it tries to do it recursively.
@@ -860,7 +862,7 @@ class VirtualDirectory(FilesystemEntry):
860
862
  raise TypeError(f"lattr is not allowed on VirtualDirectory: {self.path}")
861
863
 
862
864
  def add(self, name: str, entry: FilesystemEntry) -> None:
863
- """Add an entry to this VirtualDirectory."""
865
+ """Add an entry to this :class:`VirtualDirectory`."""
864
866
  if not self.fs.case_sensitive:
865
867
  name = name.lower()
866
868
 
@@ -1214,7 +1216,7 @@ class VirtualFilesystem(Filesystem):
1214
1216
  self.map_file_entry(vfspath, VirtualFile(self, file_path, fh))
1215
1217
 
1216
1218
  def map_file_entry(self, vfspath: str, entry: FilesystemEntry) -> None:
1217
- """Map a FilesystemEntry into the VFS.
1219
+ """Map a :class:`FilesystemEntry` into the VFS.
1218
1220
 
1219
1221
  Any missing subdirectories up to, but not including, the last part of
1220
1222
  ``vfspath`` will be created.
@@ -1271,7 +1273,7 @@ class VirtualFilesystem(Filesystem):
1271
1273
  return self.map_dir_from_tar(vfspath.lstrip("/"), tar_file, map_single_file=True)
1272
1274
 
1273
1275
  def link(self, src: str, dst: str) -> None:
1274
- """Hard link a FilesystemEntry to another location.
1276
+ """Hard link a :class:`FilesystemEntry` to another location.
1275
1277
 
1276
1278
  Args:
1277
1279
  src: The path to the target of the link.
@@ -1291,65 +1293,132 @@ class VirtualFilesystem(Filesystem):
1291
1293
  self.map_file_entry(dst, VirtualSymlink(self, dst, src))
1292
1294
 
1293
1295
 
1294
- class RootFilesystem(Filesystem):
1295
- __type__ = "root"
1296
+ class LayerFilesystem(Filesystem):
1297
+ __type__ = "layer"
1296
1298
 
1297
- def __init__(self, target: Target):
1298
- self.target = target
1299
- self.layers = []
1299
+ def __init__(self, **kwargs):
1300
+ self.layers: list[Filesystem] = []
1300
1301
  self.mounts = {}
1301
1302
  self._alt_separator = "/"
1302
1303
  self._case_sensitive = True
1303
- self._root_entry = RootFilesystemEntry(self, "/", [])
1304
- self.root = self.add_layer()
1305
- super().__init__(None)
1304
+ self._root_entry = LayerFilesystemEntry(self, "/", [])
1305
+ self.root = self.append_layer()
1306
+ super().__init__(None, **kwargs)
1307
+
1308
+ def __getattr__(self, attr: str) -> Any:
1309
+ """Provide "magic" access to filesystem specific attributes from any of the layers.
1310
+
1311
+ For example, on a :class:`LayerFilesystem` ``fs``, you can do ``fs.ntfs`` to access the
1312
+ internal NTFS object if it has an NTFS layer.
1313
+ """
1314
+ for fs in self.layers:
1315
+ try:
1316
+ return getattr(fs, attr)
1317
+ except AttributeError:
1318
+ continue
1319
+ else:
1320
+ return object.__getattribute__(self, attr)
1306
1321
 
1307
1322
  @staticmethod
1308
1323
  def detect(fh: BinaryIO) -> bool:
1309
- raise TypeError("Detect is not allowed on RootFilesystem class")
1324
+ raise TypeError("Detect is not allowed on LayerFilesystem class")
1310
1325
 
1311
- def mount(self, path: str, fs: Filesystem) -> None:
1326
+ def mount(self, path: str, fs: Filesystem, ignore_existing: bool = True) -> None:
1312
1327
  """Mount a filesystem at a given path.
1313
1328
 
1314
1329
  If there's an overlap with an existing mount, creates a new layer.
1330
+
1331
+ Args:
1332
+ path: The path to mount the filesystem at.
1333
+ fs: The filesystem to mount.
1334
+ ignore_existing: Whether to ignore existing mounts and create a new layer. Defaults to ``True``.
1315
1335
  """
1316
1336
  root = self.root
1317
1337
  for mount in self.mounts.keys():
1318
- if path == mount:
1338
+ if ignore_existing and path == mount:
1319
1339
  continue
1320
1340
 
1321
1341
  if path.startswith(mount):
1322
- root = self.add_layer()
1342
+ root = self.append_layer()
1323
1343
  break
1324
1344
 
1325
1345
  root.map_fs(path, fs)
1326
1346
  self.mounts[path] = fs
1327
1347
 
1328
1348
  def link(self, dst: str, src: str) -> None:
1329
- """Hard link a RootFilesystemEntry to another location."""
1330
- dst = fsutil.normalize(dst, alt_separator=self.alt_separator)
1349
+ """Hard link a :class:`FilesystemEntry` to another location."""
1331
1350
  self.root.map_file_entry(dst, self.get(src))
1332
1351
 
1333
1352
  def symlink(self, dst: str, src: str) -> None:
1334
1353
  """Create a symlink to another location."""
1335
1354
  self.root.symlink(dst, src)
1336
1355
 
1337
- def add_layer(self, **kwargs) -> VirtualFilesystem:
1356
+ def append_layer(self, **kwargs) -> VirtualFilesystem:
1357
+ """Append a new layer."""
1358
+ layer = VirtualFilesystem(case_sensitive=self.case_sensitive, alt_separator=self.alt_separator, **kwargs)
1359
+ self.append_fs_layer(layer)
1360
+ return layer
1361
+
1362
+ add_layer = append_layer
1363
+
1364
+ def prepend_layer(self, **kwargs) -> VirtualFilesystem:
1365
+ """Prepend a new layer."""
1338
1366
  layer = VirtualFilesystem(case_sensitive=self.case_sensitive, alt_separator=self.alt_separator, **kwargs)
1339
- self.layers.append(layer)
1340
- self._root_entry.entries.append(layer.root)
1367
+ self.prepend_fs_layer(layer)
1341
1368
  return layer
1342
1369
 
1370
+ def append_fs_layer(self, fs: Filesystem) -> None:
1371
+ """Append a filesystem as a layer.
1372
+
1373
+ Args:
1374
+ fs: The filesystem to append.
1375
+ """
1376
+ # Counterintuitively, we prepend the filesystem to the list of layers
1377
+ # We could reverse the list of layers upon iteration, but that is a hot path
1378
+ self.layers.insert(0, fs)
1379
+ self._root_entry.entries.insert(0, fs.get("/"))
1380
+
1381
+ def prepend_fs_layer(self, fs: Filesystem) -> None:
1382
+ """Prepend a filesystem as a layer.
1383
+
1384
+ Args:
1385
+ fs: The filesystem to prepend.
1386
+ """
1387
+ # Counterintuitively, we append the filesystem to the list of layers
1388
+ # We could reverse the list of layers upon iteration, but that is a hot path
1389
+ self.layers.append(fs)
1390
+ self._root_entry.entries.append(fs.get("/"))
1391
+
1392
+ def remove_fs_layer(self, fs: Filesystem) -> None:
1393
+ """Remove a filesystem layer.
1394
+
1395
+ Args:
1396
+ fs: The filesystem to remove.
1397
+ """
1398
+ self.remove_layer(self.layers.index(fs))
1399
+
1400
+ def remove_layer(self, idx: int) -> None:
1401
+ """Remove a layer by index.
1402
+
1403
+ Args:
1404
+ idx: The index of the layer to remove.
1405
+ """
1406
+ del self.layers[idx]
1407
+ del self._root_entry.entries[idx]
1408
+
1343
1409
  @property
1344
1410
  def case_sensitive(self) -> bool:
1411
+ """Whether the filesystem is case sensitive."""
1345
1412
  return self._case_sensitive
1346
1413
 
1347
1414
  @property
1348
1415
  def alt_separator(self) -> str:
1416
+ """The alternative separator of the filesystem."""
1349
1417
  return self._alt_separator
1350
1418
 
1351
1419
  @case_sensitive.setter
1352
1420
  def case_sensitive(self, value: bool) -> None:
1421
+ """Set the case sensitivity of the filesystem (and all layers)."""
1353
1422
  self._case_sensitive = value
1354
1423
  self.root.case_sensitive = value
1355
1424
  for layer in self.layers:
@@ -1357,14 +1426,14 @@ class RootFilesystem(Filesystem):
1357
1426
 
1358
1427
  @alt_separator.setter
1359
1428
  def alt_separator(self, value: str) -> None:
1429
+ """Set the alternative separator of the filesystem (and all layers)."""
1360
1430
  self._alt_separator = value
1361
1431
  self.root.alt_separator = value
1362
1432
  for layer in self.layers:
1363
1433
  layer.alt_separator = value
1364
1434
 
1365
- def get(self, path: str, relentry: FilesystemEntry = None) -> FilesystemEntry:
1366
- self.target.log.debug("%r::get(%r)", self, path)
1367
-
1435
+ def get(self, path: str, relentry: Optional[LayerFilesystemEntry] = None) -> LayerFilesystemEntry:
1436
+ """Get a :class:`FilesystemEntry` from the filesystem."""
1368
1437
  entry = relentry or self._root_entry
1369
1438
  path = fsutil.normalize(path, alt_separator=self.alt_separator).strip("/")
1370
1439
  full_path = fsutil.join(entry.path, path, alt_separator=self.alt_separator)
@@ -1388,9 +1457,10 @@ class RootFilesystem(Filesystem):
1388
1457
  raise NotASymlinkError(full_path)
1389
1458
  raise FileNotFoundError(full_path)
1390
1459
 
1391
- return RootFilesystemEntry(self, full_path, entries)
1460
+ return LayerFilesystemEntry(self, full_path, entries)
1392
1461
 
1393
1462
  def _get_from_entry(self, path: str, entry: FilesystemEntry) -> FilesystemEntry:
1463
+ """Get a :class:`FilesystemEntry` relative to a specific entry."""
1394
1464
  parts = path.split("/")
1395
1465
 
1396
1466
  for part in parts:
@@ -1405,11 +1475,11 @@ class RootFilesystem(Filesystem):
1405
1475
  class EntryList(list):
1406
1476
  """Wrapper list for filesystem entries.
1407
1477
 
1408
- Expose a getattr on a list of items. Useful in cases where
1409
- there's a virtual filesystem entry as well as a real one.
1478
+ Exposes a ``__getattr__`` on a list of items. Useful to access internal objects from filesystem implementations.
1479
+ For example, access the underlying NTFS object from a list of virtual and NTFS entries.
1410
1480
  """
1411
1481
 
1412
- def __init__(self, value: Any):
1482
+ def __init__(self, value: FilesystemEntry | list[FilesystemEntry]):
1413
1483
  if not isinstance(value, list):
1414
1484
  value = [value]
1415
1485
  super().__init__(value)
@@ -1424,20 +1494,12 @@ class EntryList(list):
1424
1494
  return object.__getattribute__(self, attr)
1425
1495
 
1426
1496
 
1427
- class RootFilesystemEntry(FilesystemEntry):
1497
+ class LayerFilesystemEntry(FilesystemEntry):
1428
1498
  def __init__(self, fs: Filesystem, path: str, entry: FilesystemEntry):
1429
1499
  super().__init__(fs, path, EntryList(entry))
1430
- self.entries = self.entry
1500
+ self.entries: EntryList = self.entry
1431
1501
  self._link = None
1432
1502
 
1433
- def __getattr__(self, attr):
1434
- for entry in self.entries:
1435
- try:
1436
- return getattr(entry, attr)
1437
- except AttributeError:
1438
- continue
1439
- return object.__getattribute__(self, attr)
1440
-
1441
1503
  def _exec(self, func: str, *args, **kwargs) -> Any:
1442
1504
  """Helper method to execute a method over all contained entries."""
1443
1505
  exc = []
@@ -1451,18 +1513,16 @@ class RootFilesystemEntry(FilesystemEntry):
1451
1513
  exceptions = ",".join(exc)
1452
1514
  else:
1453
1515
  exceptions = "No entries"
1516
+
1454
1517
  raise FilesystemError(f"Can't resolve {func} for {self}: {exceptions}")
1455
1518
 
1456
1519
  def get(self, path: str) -> FilesystemEntry:
1457
- self.fs.target.log.debug("%r::get(%r)", self, path)
1458
1520
  return self.fs.get(path, self._resolve())
1459
1521
 
1460
1522
  def open(self) -> BinaryIO:
1461
- self.fs.target.log.debug("%r::open()", self)
1462
1523
  return self._resolve()._exec("open")
1463
1524
 
1464
1525
  def iterdir(self) -> Iterator[str]:
1465
- self.fs.target.log.debug("%r::iterdir()", self)
1466
1526
  yielded = {".", ".."}
1467
1527
  selfentry = self._resolve()
1468
1528
  for fsentry in selfentry.entries:
@@ -1474,8 +1534,7 @@ class RootFilesystemEntry(FilesystemEntry):
1474
1534
  yield entry_name
1475
1535
  yielded.add(name)
1476
1536
 
1477
- def scandir(self) -> Iterator[FilesystemEntry]:
1478
- self.fs.target.log.debug("%r::scandir()", self)
1537
+ def scandir(self) -> Iterator[LayerFilesystemEntry]:
1479
1538
  # Every entry is actually a list of entries from the different
1480
1539
  # overlaying FSes, of which each may implement a different function
1481
1540
  # like .stat() or .open()
@@ -1495,49 +1554,115 @@ class RootFilesystemEntry(FilesystemEntry):
1495
1554
  # overlaying FSes may have different casing of the name.
1496
1555
  entry_name = entries[0].name
1497
1556
  path = fsutil.join(selfentry.path, entry_name, alt_separator=selfentry.fs.alt_separator)
1498
- yield RootFilesystemEntry(selfentry.fs, path, entries)
1557
+ yield LayerFilesystemEntry(selfentry.fs, path, entries)
1499
1558
 
1500
1559
  def is_file(self, follow_symlinks: bool = True) -> bool:
1501
- self.fs.target.log.debug("%r::is_file()", self)
1502
1560
  try:
1503
1561
  return self._resolve(follow_symlinks=follow_symlinks)._exec("is_file", follow_symlinks=follow_symlinks)
1504
1562
  except FileNotFoundError:
1505
1563
  return False
1506
1564
 
1507
1565
  def is_dir(self, follow_symlinks: bool = True) -> bool:
1508
- self.fs.target.log.debug("%r::is_dir()", self)
1509
1566
  try:
1510
1567
  return self._resolve(follow_symlinks=follow_symlinks)._exec("is_dir", follow_symlinks=follow_symlinks)
1511
1568
  except FileNotFoundError:
1512
1569
  return False
1513
1570
 
1514
1571
  def is_symlink(self) -> bool:
1515
- self.fs.target.log.debug("%r::is_symlink()", self)
1516
1572
  return self._exec("is_symlink")
1517
1573
 
1518
1574
  def readlink(self) -> str:
1519
- self.fs.target.log.debug("%r::readlink()", self)
1520
1575
  if not self.is_symlink():
1521
1576
  raise NotASymlinkError(f"Not a link: {self}")
1522
1577
  return self._exec("readlink")
1523
1578
 
1524
1579
  def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
1525
- self.fs.target.log.debug("%r::stat()", self)
1526
1580
  return self._resolve(follow_symlinks=follow_symlinks)._exec("stat", follow_symlinks=follow_symlinks)
1527
1581
 
1528
1582
  def lstat(self) -> fsutil.stat_result:
1529
- self.fs.target.log.debug("%r::lstat()", self)
1530
1583
  return self._exec("lstat")
1531
1584
 
1532
1585
  def attr(self) -> Any:
1533
- self.fs.target.log.debug("%r::attr()", self)
1534
1586
  return self._resolve()._exec("attr")
1535
1587
 
1536
1588
  def lattr(self) -> Any:
1537
- self.fs.target.log.debug("%r::lattr()", self)
1538
1589
  return self._exec("lattr")
1539
1590
 
1540
1591
 
1592
+ class RootFilesystem(LayerFilesystem):
1593
+ __type__ = "root"
1594
+
1595
+ def __init__(self, target: Target):
1596
+ self.target = target
1597
+ super().__init__()
1598
+
1599
+ @staticmethod
1600
+ def detect(fh: BinaryIO) -> bool:
1601
+ raise TypeError("Detect is not allowed on RootFilesystem class")
1602
+
1603
+ def get(self, path: str, relentry: Optional[LayerFilesystemEntry] = None) -> RootFilesystemEntry:
1604
+ self.target.log.debug("%r::get(%r)", self, path)
1605
+ entry = super().get(path, relentry)
1606
+ entry.__class__ = RootFilesystemEntry
1607
+ return entry
1608
+
1609
+
1610
+ class RootFilesystemEntry(LayerFilesystemEntry):
1611
+ fs: RootFilesystem
1612
+
1613
+ def get(self, path: str) -> RootFilesystemEntry:
1614
+ self.fs.target.log.debug("%r::get(%r)", self, path)
1615
+ entry = super().get(path)
1616
+ entry.__class__ = RootFilesystemEntry
1617
+ return entry
1618
+
1619
+ def open(self) -> BinaryIO:
1620
+ self.fs.target.log.debug("%r::open()", self)
1621
+ return super().open()
1622
+
1623
+ def iterdir(self) -> Iterator[str]:
1624
+ self.fs.target.log.debug("%r::iterdir()", self)
1625
+ yield from super().iterdir()
1626
+
1627
+ def scandir(self) -> Iterator[RootFilesystemEntry]:
1628
+ self.fs.target.log.debug("%r::scandir()", self)
1629
+ for entry in super().scandir():
1630
+ entry.__class__ = RootFilesystemEntry
1631
+ yield entry
1632
+
1633
+ def is_file(self, follow_symlinks: bool = True) -> bool:
1634
+ self.fs.target.log.debug("%r::is_file()", self)
1635
+ return super().is_file(follow_symlinks=follow_symlinks)
1636
+
1637
+ def is_dir(self, follow_symlinks: bool = True) -> bool:
1638
+ self.fs.target.log.debug("%r::is_dir()", self)
1639
+ return super().is_dir(follow_symlinks=follow_symlinks)
1640
+
1641
+ def is_symlink(self) -> bool:
1642
+ self.fs.target.log.debug("%r::is_symlink()", self)
1643
+ return super().is_symlink()
1644
+
1645
+ def readlink(self) -> str:
1646
+ self.fs.target.log.debug("%r::readlink()", self)
1647
+ return super().readlink()
1648
+
1649
+ def stat(self, follow_symlinks: bool = True) -> fsutil.stat_result:
1650
+ self.fs.target.log.debug("%r::stat()", self)
1651
+ return super().stat(follow_symlinks=follow_symlinks)
1652
+
1653
+ def lstat(self) -> fsutil.stat_result:
1654
+ self.fs.target.log.debug("%r::lstat()", self)
1655
+ return super().lstat()
1656
+
1657
+ def attr(self) -> Any:
1658
+ self.fs.target.log.debug("%r::attr()", self)
1659
+ return super().attr()
1660
+
1661
+ def lattr(self) -> Any:
1662
+ self.fs.target.log.debug("%r::lattr()", self)
1663
+ return super().lattr()
1664
+
1665
+
1541
1666
  def register(module: str, class_name: str, internal: bool = True) -> None:
1542
1667
  """Register a filesystem implementation to use when opening a filesystem.
1543
1668
 
@@ -14,8 +14,8 @@ class VBLoader(Loader):
14
14
  return (mft_exists or c_drive_exists) and config_exists
15
15
 
16
16
  def map(self, target):
17
- ntfs_overlay = target.fs.add_layer()
18
- remap_overlay = target.fs.add_layer()
17
+ remap_overlay = target.fs.append_layer()
18
+ ntfs_overlay = target.fs.append_layer()
19
19
  dfs = DirectoryFilesystem(self.path, case_sensitive=False)
20
20
  target.filesystems.add(dfs)
21
21
 
@@ -3,7 +3,7 @@ from typing import Iterable
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 RootFilesystemEntry
6
+ from dissect.target.filesystem import LayerFilesystemEntry
7
7
  from dissect.target.helpers.fsutil import TargetPath
8
8
  from dissect.target.helpers.record import TargetRecordDescriptor
9
9
  from dissect.target.plugin import Plugin, export
@@ -50,7 +50,7 @@ def generate_record(target: Target, path: TargetPath) -> FilesystemRecord:
50
50
  stat = path.lstat()
51
51
  btime = from_unix(stat.st_birthtime) if stat.st_birthtime else None
52
52
  entry = path.get()
53
- if isinstance(entry, RootFilesystemEntry):
53
+ if isinstance(entry, LayerFilesystemEntry):
54
54
  fs_types = [sub_entry.fs.__type__ for sub_entry in entry.entries]
55
55
  else:
56
56
  fs_types = [entry.fs.__type__]
@@ -97,7 +97,7 @@ class ESXiPlugin(UnixPlugin):
97
97
 
98
98
  # Create a root layer for the "local state" filesystem
99
99
  # This stores persistent configuration data
100
- local_layer = target.fs.add_layer()
100
+ local_layer = target.fs.append_layer()
101
101
 
102
102
  # Mount all the visor tars in individual filesystem layers
103
103
  _mount_modules(target, sysvol, cfg)
@@ -209,7 +209,7 @@ def _mount_modules(target: Target, sysvol: Filesystem, cfg: dict[str, str]):
209
209
  tfs = tar.TarFilesystem(cfile, tarinfo=vmtar.VisorTarInfo)
210
210
 
211
211
  if tfs:
212
- target.fs.add_layer().mount("/", tfs)
212
+ target.fs.append_layer().mount("/", tfs)
213
213
 
214
214
 
215
215
  def _mount_local(target: Target, local_layer: VirtualFilesystem):
@@ -24,7 +24,7 @@ class VyosPlugin(LinuxPlugin):
24
24
  self._version, rootpath = latest
25
25
 
26
26
  # VyOS does some additional magic with base system files
27
- layer = target.fs.add_layer()
27
+ layer = target.fs.append_layer()
28
28
  layer.map_file_entry("/", target.fs.root.get(f"/boot/{self._version}/{rootpath}"))
29
29
  super().__init__(target)
30
30
 
@@ -113,7 +113,7 @@ class FortiOSPlugin(LinuxPlugin):
113
113
 
114
114
  # FortiGate
115
115
  if (datafs_tar := sysvol.path("/datafs.tar.gz")).exists():
116
- target.fs.add_layer().mount("/data", TarFilesystem(datafs_tar.open("rb")))
116
+ target.fs.append_layer().mount("/data", TarFilesystem(datafs_tar.open("rb")))
117
117
 
118
118
  # Additional FortiGate or FortiManager tars with corrupt XZ streams
119
119
  target.log.warning("Attempting to load XZ files, this can take a while.")
@@ -127,11 +127,11 @@ class FortiOSPlugin(LinuxPlugin):
127
127
  ):
128
128
  if (tar := target.fs.path(path)).exists() or (tar := sysvol.path(path)).exists():
129
129
  fh = xz.repair_checksum(tar.open("rb"))
130
- target.fs.add_layer().mount("/", TarFilesystem(fh))
130
+ target.fs.append_layer().mount("/", TarFilesystem(fh))
131
131
 
132
132
  # FortiAnalyzer and FortiManager
133
133
  if (rootfs_ext_tar := sysvol.path("rootfs-ext.tar.xz")).exists():
134
- target.fs.add_layer().mount("/", TarFilesystem(rootfs_ext_tar.open("rb")))
134
+ target.fs.append_layer().mount("/", TarFilesystem(rootfs_ext_tar.open("rb")))
135
135
 
136
136
  # Filesystem mounts can be discovered in the FortiCare debug report
137
137
  # or using ``fnsysctl ls`` and ``fnsysctl df`` in the cli.
@@ -31,7 +31,7 @@ from dissect.target.exceptions import (
31
31
  RegistryValueNotFoundError,
32
32
  TargetError,
33
33
  )
34
- from dissect.target.filesystem import FilesystemEntry, RootFilesystemEntry
34
+ from dissect.target.filesystem import FilesystemEntry, LayerFilesystemEntry
35
35
  from dissect.target.helpers import cyber, fsutil, regutil
36
36
  from dissect.target.plugin import arg
37
37
  from dissect.target.target import Target
@@ -469,7 +469,7 @@ class TargetCli(TargetCmd):
469
469
  # If we happen to scan an NTFS filesystem see if any of the
470
470
  # entries has an alternative data stream and also list them.
471
471
  entry = file_.get()
472
- if isinstance(entry, RootFilesystemEntry):
472
+ if isinstance(entry, LayerFilesystemEntry):
473
473
  if entry.entries.fs.__type__ == "ntfs":
474
474
  attrs = entry.lattr()
475
475
  for data_stream in attrs.DATA:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.17.dev19
3
+ Version: 3.17.dev20
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=VVW_Rq_vQinapz-2mbJ3UkxBEZpb2pE_7JlhMukdtrY,2877
4
- dissect/target/filesystem.py,sha256=VD1BA6hLqH_FPWFZ-wliEuCxnFrUK61S9VbGK7CtA5w,55597
4
+ dissect/target/filesystem.py,sha256=1i-lToeTX-HgQXQOYxPXH-90M_eq43W4FFzNDRdpgpk,60094
5
5
  dissect/target/loader.py,sha256=_mrMOzKdpb7nlZJpLENOLuU4Ty92PzJem9GFDuo0PK4,7298
6
6
  dissect/target/plugin.py,sha256=HAN8maaDt-Rlqt8Rr1IW7gXQpzNQZjCVz-i4aSPphSw,48677
7
7
  dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
@@ -102,7 +102,7 @@ dissect/target/loaders/tar.py,sha256=ah5t9tzplJp-fJrvwTTtfqdjXTpZLQIikWglnaTKQHs
102
102
  dissect/target/loaders/target.py,sha256=MU_HUtg58YdhdZu6ga1sYG7fK61Dn7N0TBkWXDCWwyc,798
103
103
  dissect/target/loaders/targetd.py,sha256=sfbn2_j3il2G-rPywAoNT5YPtD5KmKkmBv1zrPDRs6I,8250
104
104
  dissect/target/loaders/utm.py,sha256=e5x5ZI3HeL0STh4S-CaQb68Rnug4SVZR9zlmHaGFj0M,978
105
- dissect/target/loaders/vb.py,sha256=CnQcn7bAkMzIB1y-lWLtPPXdIVsyeDaT6hTZEurjkV4,2072
105
+ dissect/target/loaders/vb.py,sha256=CdimOMeoJEDq8xYDgtldGSiwhR-dY5uxac1L0sYwAEU,2078
106
106
  dissect/target/loaders/vbox.py,sha256=8JD7D8iAY9JRvTHsrosp5ZMsZezuLhZ10Zt8sEL7KBI,732
107
107
  dissect/target/loaders/velociraptor.py,sha256=FNxZgs_ehmgGO_Giw5oNl7cVOWNqI2nEiPWT4GjF2e0,4955
108
108
  dissect/target/loaders/vma.py,sha256=AAY5-s-nz6wgvmcFkptJD7nNXhpkdf6SqEKVOrJaIKs,644
@@ -160,7 +160,7 @@ dissect/target/plugins/filesystem/acquire_handles.py,sha256=-pX_akH5GrYe0HofXOa2
160
160
  dissect/target/plugins/filesystem/acquire_hash.py,sha256=OVxI19-Bl1tdqCiFMscFMLmyoiBOsuAjL-Q8aQpEwl0,1441
161
161
  dissect/target/plugins/filesystem/icat.py,sha256=bOMi04IlljnKwxTWTZJKtK7RxKnabFu3WcXyUwzkE-4,4090
162
162
  dissect/target/plugins/filesystem/resolver.py,sha256=HfyASUFV4F9uD-yFXilFpPTORAsRDvdmTvuYHgOaOWg,4776
163
- dissect/target/plugins/filesystem/walkfs.py,sha256=aCEBmT3uoQdMdSGUshMOsKpcjrzAFg3HzeYW24PJZwk,2296
163
+ dissect/target/plugins/filesystem/walkfs.py,sha256=e8HEZcV5Wiua26FGWL3xgiQ_PIhcNvGI5KCdsAx2Nmo,2298
164
164
  dissect/target/plugins/filesystem/yara.py,sha256=q_pbrQArNaWP4ILRzK7VQhukIw16LhUvntoviHmZ38Q,2241
165
165
  dissect/target/plugins/filesystem/ntfs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
166
  dissect/target/plugins/filesystem/ntfs/mft.py,sha256=Za-fsTcKlAlhm9ugJlMdwsJVf2Osrh4PrEGSFuv-Eeo,9564
@@ -205,7 +205,7 @@ dissect/target/plugins/os/unix/bsd/osx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
205
205
  dissect/target/plugins/os/unix/bsd/osx/_os.py,sha256=KvP7YJ7apVwoIop7MR-8q5QbVGoB6MdR42l6ssEe6es,4081
206
206
  dissect/target/plugins/os/unix/bsd/osx/user.py,sha256=qopB0s3n7e6Q7NjWzn8Z-dKtDtU7e6In4Vm7hIvvedo,2322
207
207
  dissect/target/plugins/os/unix/esxi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
208
- dissect/target/plugins/os/unix/esxi/_os.py,sha256=jqw71St-L_BiREai8bw27oFOrLK4_GuEDLUTK5FMGLU,17498
208
+ dissect/target/plugins/os/unix/esxi/_os.py,sha256=8kFFK9986zN8hXmDUWwdQHtbV33nWKerRuisg_xbsoQ,17504
209
209
  dissect/target/plugins/os/unix/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
210
210
  dissect/target/plugins/os/unix/linux/_os.py,sha256=YJYwuq_iAinOrPqTE49Q4DLYMWBeRCly1uTbDvPhp6Q,2796
211
211
  dissect/target/plugins/os/unix/linux/cmdline.py,sha256=XIvaTL42DzeQGhqHN_RTMI5g8hbI2_wjzb7KZ0kPOM0,1591
@@ -224,10 +224,10 @@ dissect/target/plugins/os/unix/linux/debian/_os.py,sha256=GI19ZqcyfZ1mUYg2NCx93H
224
224
  dissect/target/plugins/os/unix/linux/debian/apt.py,sha256=dkTfLrS-MS8wfrXILFLHDoLqBkM_w16KTRQ7ysiZkZY,4316
225
225
  dissect/target/plugins/os/unix/linux/debian/dpkg.py,sha256=DPBLQiHAF7ZS8IorRsGAiBj4HhvwuJbmkMEHOuTZisw,5735
226
226
  dissect/target/plugins/os/unix/linux/debian/vyos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
- dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=q8qG2FLJhUbpjfwlNCmWAhFdTWMzSWUh7s7H8m4x7Fw,1741
227
+ dissect/target/plugins/os/unix/linux/debian/vyos/_os.py,sha256=TPjcfv1n68RCe3Er4aCVQwQDCZwJT-NLvje3kPjDfhk,1744
228
228
  dissect/target/plugins/os/unix/linux/fortios/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
229
229
  dissect/target/plugins/os/unix/linux/fortios/_keys.py,sha256=jDDHObfsUn9BGoIir9p4J_-rg9rI1rgoOfnL3R3lg4o,123358
230
- dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=gFFzByku_3qpSrHpnqJv6xIbIe3V4iGXdUxxGD_-EFA,19435
230
+ dissect/target/plugins/os/unix/linux/fortios/_os.py,sha256=lyURdGaIsPmEhRZ6P5DQtwOggOLe9TZuY1cTirLSukA,19444
231
231
  dissect/target/plugins/os/unix/linux/fortios/generic.py,sha256=tT4-lE0Z_DeDIN3zHrQbE8JB3cRJop1_TiEst-Au0bs,1230
232
232
  dissect/target/plugins/os/unix/linux/fortios/locale.py,sha256=VDdk60sqe2JTfftssO05C667-_BpI3kcqKOTVzO3ueU,5209
233
233
  dissect/target/plugins/os/unix/linux/redhat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -322,7 +322,7 @@ dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLc
322
322
  dissect/target/tools/mount.py,sha256=L_0tSmiBdW4aSaF0vXjB0bAkTC0kmT2N1hrbW6s5Jow,3254
323
323
  dissect/target/tools/query.py,sha256=6zz9SXS6YnHj7eguORS8Je7N4iM0i1PZDIQ-gyJ1nPY,15593
324
324
  dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
325
- dissect/target/tools/shell.py,sha256=AuFNydQpRjZ0PKXFNTqm4OQwD-xGXShPqtixVgCcwSo,44825
325
+ dissect/target/tools/shell.py,sha256=4v6Z06YJDjKv6e6SRvWNjQ2n_KHo_CjL4P0w1_gY_ro,44827
326
326
  dissect/target/tools/utils.py,sha256=sQizexY3ui5vmWw4KOBLg5ecK3TPFjD-uxDqRn56ZTY,11304
327
327
  dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
328
  dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
336
336
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
337
337
  dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
338
338
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
339
- dissect.target-3.17.dev19.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
- dissect.target-3.17.dev19.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
- dissect.target-3.17.dev19.dist-info/METADATA,sha256=c7p2Gn3T7tWRcVQUquv4BDpD2l-3fbvNkkS1VQ2vriA,11300
342
- dissect.target-3.17.dev19.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
- dissect.target-3.17.dev19.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
- dissect.target-3.17.dev19.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
- dissect.target-3.17.dev19.dist-info/RECORD,,
339
+ dissect.target-3.17.dev20.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
340
+ dissect.target-3.17.dev20.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
341
+ dissect.target-3.17.dev20.dist-info/METADATA,sha256=w19ITKpjeWu52HPdEU4SD_axvpgGpBr8_nsETBOLeEw,11300
342
+ dissect.target-3.17.dev20.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
343
+ dissect.target-3.17.dev20.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
344
+ dissect.target-3.17.dev20.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
345
+ dissect.target-3.17.dev20.dist-info/RECORD,,