dissect.hypervisor 3.16.dev3__py3-none-any.whl → 3.17.dev1__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.
@@ -1,10 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  import ctypes
2
4
  import io
3
5
  import logging
4
6
  import os
7
+ import re
5
8
  import textwrap
6
9
  import zlib
7
10
  from bisect import bisect_right
11
+ from dataclasses import dataclass
8
12
  from functools import lru_cache
9
13
  from pathlib import Path
10
14
 
@@ -59,13 +63,13 @@ class VMDK(AlignedStream):
59
63
  if self.descriptor.attr["parentCID"] != "ffffffff":
60
64
  self.parent = open_parent(path.parent, self.descriptor.attr["parentFileNameHint"])
61
65
 
62
- for _, size, extent_type, filename in self.descriptor.extents:
63
- if extent_type in ["SPARSE", "VMFSSPARSE", "SESPARSE"]:
64
- sdisk_fh = path.with_name(filename).open("rb")
66
+ for extent in self.descriptor.extents:
67
+ if extent.type in ["SPARSE", "VMFSSPARSE", "SESPARSE"]:
68
+ sdisk_fh = path.with_name(extent.filename).open("rb")
65
69
  self.disks.append(SparseDisk(sdisk_fh, parent=self.parent))
66
- elif extent_type in ["VMFS", "FLAT"]:
67
- rdisk_fh = path.with_name(filename).open("rb")
68
- self.disks.append(RawDisk(rdisk_fh, size * SECTOR_SIZE))
70
+ elif extent.type in ["VMFS", "FLAT"]:
71
+ rdisk_fh = path.with_name(extent.filename).open("rb")
72
+ self.disks.append(RawDisk(rdisk_fh, extent.sectors * SECTOR_SIZE))
69
73
 
70
74
  elif magic in (COWD_MAGIC, VMDK_MAGIC, SESPARSE_MAGIC):
71
75
  sparse_disk = SparseDisk(fh)
@@ -398,8 +402,53 @@ class SparseExtentHeader:
398
402
  return getattr(self.hdr, attr)
399
403
 
400
404
 
405
+ RE_EXTENT_DESCRIPTOR = re.compile(
406
+ r"""
407
+ ^
408
+ (?P<access_mode>RW|RDONLY|NOACCESS)\s
409
+ (?P<sectors>\d+)\s
410
+ (?P<type>SPARSE|ZERO|FLAT|VMFS|VMFSSPARSE|VMFSRDM|VMFSRAW)
411
+ (\s(?P<filename>\".+\"))?
412
+ (\s(?P<start_sector>\d+))?
413
+ (\s(?P<partition_uuid>\S+))?
414
+ (\s(?P<device_identifier>\S+))?
415
+ $
416
+ """,
417
+ re.VERBOSE,
418
+ )
419
+
420
+
421
+ @dataclass
422
+ class ExtentDescriptor:
423
+ raw: str
424
+ access_mode: str
425
+ sectors: int
426
+ type: str
427
+ filename: str | None = None
428
+ start_sector: int | None = None
429
+ partition_uuid: str | None = None
430
+ device_identifier: str | None = None
431
+
432
+ def __post_init__(self) -> None:
433
+ self.sectors = int(self.sectors)
434
+
435
+ if self.filename:
436
+ self.filename = self.filename.strip('"')
437
+
438
+ if self.start_sector:
439
+ self.start_sector = int(self.start_sector)
440
+
441
+ def __repr__(self) -> str:
442
+ return f"<ExtentDescriptor {self.raw}>"
443
+
444
+ def __str__(self) -> str:
445
+ return self.raw
446
+
447
+
401
448
  class DiskDescriptor:
402
- def __init__(self, attr, extents, disk_db, sectors, raw_config=None):
449
+ def __init__(
450
+ self, attr: dict, extents: list[ExtentDescriptor], disk_db: dict, sectors: int, raw_config: str | None = None
451
+ ):
403
452
  self.attr = attr
404
453
  self.extents = extents
405
454
  self.ddb = disk_db
@@ -407,9 +456,15 @@ class DiskDescriptor:
407
456
  self.raw = raw_config
408
457
 
409
458
  @classmethod
410
- def parse(cls, vmdk_config):
459
+ def parse(cls, vmdk_config: str) -> DiskDescriptor:
460
+ """Return :class:`DiskDescriptor` based on the provided ``vmdk_config``.
461
+
462
+ Resources:
463
+ - https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc
464
+ """ # noqa: E501
465
+
411
466
  descriptor_settings = {}
412
- extents = []
467
+ extents: list[ExtentDescriptor] = []
413
468
  disk_db = {}
414
469
  sectors = 0
415
470
 
@@ -420,11 +475,15 @@ class DiskDescriptor:
420
475
  continue
421
476
 
422
477
  if line.startswith("RW ") or line.startswith("RDONLY ") or line.startswith("NOACCESS "):
423
- access_type, size, extent_type, filename = line.split(" ", 3)
424
- filename = filename.strip('"')
425
- size = int(size)
426
- sectors += size
427
- extents.append([access_type, size, extent_type, filename])
478
+ match = RE_EXTENT_DESCRIPTOR.search(line)
479
+
480
+ if not match:
481
+ log.warning("Unexpected ExtentDescriptor format in vmdk config: %s, ignoring", line)
482
+ continue
483
+
484
+ extent = ExtentDescriptor(raw=line, **match.groupdict())
485
+ sectors += extent.sectors
486
+ extents.append(extent)
428
487
  continue
429
488
 
430
489
  setting, _, value = line.partition("=")
@@ -438,35 +497,33 @@ class DiskDescriptor:
438
497
 
439
498
  return cls(descriptor_settings, extents, disk_db, sectors, vmdk_config)
440
499
 
441
- def __str__(self):
442
- str_template = """\
443
- # Disk DescriptorFile
444
- version=1
445
- {}
500
+ def __str__(self) -> str:
501
+ str_template = textwrap.dedent(
502
+ """\
503
+ # Disk DescriptorFile
504
+ version=1
505
+ {}
446
506
 
447
- # Extent Description
448
- {}
507
+ # Extent Description
508
+ {}
449
509
 
450
- # The Disk Data Base
451
- #DDB
510
+ # The Disk Data Base
511
+ #DDB
512
+
513
+ {}"""
514
+ )
452
515
 
453
- {}"""
454
- str_template = textwrap.dedent(str_template)
455
516
  descriptor_settings = []
456
517
  for setting, value in self.attr.items():
457
- if setting == "version":
458
- continue
459
- descriptor_settings.append("{}={}".format(setting, value))
518
+ if setting != "version":
519
+ descriptor_settings.append(f"{setting}={value}")
460
520
  descriptor_settings = "\n".join(descriptor_settings)
461
521
 
462
- extents = []
463
- for access_type, size, extent_type, filename in self.extents:
464
- extents.append('{} {} {} "{}"'.format(access_type, size, extent_type, filename))
465
- extents = "\n".join(extents)
522
+ extents = "\n".join(map(str, self.extents))
466
523
 
467
524
  disk_db = []
468
525
  for setting, value in self.ddb.items():
469
- disk_db.append('{} = "{}"'.format(setting, value))
526
+ disk_db.append(f'{setting} = "{value}"')
470
527
  disk_db = "\n".join(disk_db)
471
528
 
472
529
  return str_template.format(descriptor_settings, extents, disk_db)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.hypervisor
3
- Version: 3.16.dev3
3
+ Version: 3.17.dev1
4
4
  Summary: A Dissect module implementing parsers for various hypervisor disk, backup and configuration files
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -23,14 +23,14 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  License-File: COPYRIGHT
25
25
  Requires-Dist: defusedxml
26
- Requires-Dist: dissect.cstruct <5,>=4
27
- Requires-Dist: dissect.util <4,>=3
28
- Provides-Extra: dev
29
- Requires-Dist: dissect.hypervisor[full] ; extra == 'dev'
30
- Requires-Dist: dissect.cstruct <5.0.dev,>=4.0.dev ; extra == 'dev'
31
- Requires-Dist: dissect.util <4.0.dev,>=3.0.dev ; extra == 'dev'
26
+ Requires-Dist: dissect.cstruct<5,>=4
27
+ Requires-Dist: dissect.util<4,>=3
32
28
  Provides-Extra: full
33
- Requires-Dist: pycryptodome ; extra == 'full'
29
+ Requires-Dist: pycryptodome; extra == "full"
30
+ Provides-Extra: dev
31
+ Requires-Dist: dissect.hypervisor[full]; extra == "dev"
32
+ Requires-Dist: dissect.cstruct<5.0.dev,>=4.0.dev; extra == "dev"
33
+ Requires-Dist: dissect.util<4.0.dev,>=3.0.dev; extra == "dev"
34
34
 
35
35
  # dissect.hypervisor
36
36
 
@@ -19,16 +19,16 @@ dissect/hypervisor/disk/qcow2.py,sha256=K4zstjte7SxX2psSbAj4YqwGeplfIvfbq5ScP4mz
19
19
  dissect/hypervisor/disk/vdi.py,sha256=_kX7ZGOVo_98ckMaiaDEmw4VjNgM57cY4YUSYrk2JGs,1851
20
20
  dissect/hypervisor/disk/vhd.py,sha256=OqSdEO2NGMI5DjgxWFbtZp8bPjSon7moMJn_5152MbI,3660
21
21
  dissect/hypervisor/disk/vhdx.py,sha256=FVGTY5mUKFb7oRNKNiLGd5ngGfaGvERtFCGhAlyZD_k,12631
22
- dissect/hypervisor/disk/vmdk.py,sha256=KSz_76X_QcInImHNGfCJdN6nF4TmUZ7vqsbZtuUMgbg,18126
22
+ dissect/hypervisor/disk/vmdk.py,sha256=CNA2zCWZYKl6G3jWF23_ToJCqHkPR_GngtAH5rGWY30,19298
23
23
  dissect/hypervisor/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  dissect/hypervisor/tools/envelope.py,sha256=6_RLtKmFnZ69fx8HlvFgsjDjNrfnXD73dgszpG1mYQE,967
25
25
  dissect/hypervisor/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  dissect/hypervisor/util/envelope.py,sha256=6nEJfgsxj696qxaLOQ2k5MrVWA2uLkbB5zxxRAvboF4,10405
27
27
  dissect/hypervisor/util/vmtar.py,sha256=oNJ-qTvQVOl7al_vExpg_T4LnGyE72O9hjOmQBiTKSA,1469
28
- dissect.hypervisor-3.16.dev3.dist-info/COPYRIGHT,sha256=EOOoIwk_inOMUD4c1ylpzMtYLjGzmc-MLEVAEdLLr20,305
29
- dissect.hypervisor-3.16.dev3.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
30
- dissect.hypervisor-3.16.dev3.dist-info/METADATA,sha256=kNdewiPG4XwfRU8k2lRLqAbPL5QWlTnRi4SJCmtILlQ,3420
31
- dissect.hypervisor-3.16.dev3.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
32
- dissect.hypervisor-3.16.dev3.dist-info/entry_points.txt,sha256=oM21bFD0YBHKjgCBk-DQ2398PUZdKV2VNR_IWzd1yog,76
33
- dissect.hypervisor-3.16.dev3.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
34
- dissect.hypervisor-3.16.dev3.dist-info/RECORD,,
28
+ dissect.hypervisor-3.17.dev1.dist-info/COPYRIGHT,sha256=EOOoIwk_inOMUD4c1ylpzMtYLjGzmc-MLEVAEdLLr20,305
29
+ dissect.hypervisor-3.17.dev1.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
30
+ dissect.hypervisor-3.17.dev1.dist-info/METADATA,sha256=eNMw0l3Ej2zgY_n-4RvjtpkMdopxd598cH04W6KvlTw,3412
31
+ dissect.hypervisor-3.17.dev1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
32
+ dissect.hypervisor-3.17.dev1.dist-info/entry_points.txt,sha256=oM21bFD0YBHKjgCBk-DQ2398PUZdKV2VNR_IWzd1yog,76
33
+ dissect.hypervisor-3.17.dev1.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
34
+ dissect.hypervisor-3.17.dev1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5