dissect.hypervisor 3.16.dev3__tar.gz → 3.17.dev1__tar.gz

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.
Files changed (84) hide show
  1. {dissect_hypervisor-3.16.dev3/dissect.hypervisor.egg-info → dissect_hypervisor-3.17.dev1}/PKG-INFO +1 -1
  2. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/vmdk.py +90 -33
  3. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1/dissect.hypervisor.egg-info}/PKG-INFO +1 -1
  4. dissect_hypervisor-3.17.dev1/tests/test_vmdk.py +199 -0
  5. dissect_hypervisor-3.16.dev3/tests/test_vmdk.py +0 -20
  6. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/COPYRIGHT +0 -0
  7. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/LICENSE +0 -0
  8. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/MANIFEST.in +0 -0
  9. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/README.md +0 -0
  10. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/__init__.py +0 -0
  11. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/__init__.py +0 -0
  12. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/c_hyperv.py +0 -0
  13. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/hyperv.py +0 -0
  14. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/ovf.py +0 -0
  15. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/pvs.py +0 -0
  16. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/vbox.py +0 -0
  17. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/descriptor/vmx.py +0 -0
  18. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/__init__.py +0 -0
  19. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_hdd.py +0 -0
  20. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_qcow2.py +0 -0
  21. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_vdi.py +0 -0
  22. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_vhd.py +0 -0
  23. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_vhdx.py +0 -0
  24. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/c_vmdk.py +0 -0
  25. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/hdd.py +0 -0
  26. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/qcow2.py +0 -0
  27. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/vdi.py +0 -0
  28. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/vhd.py +0 -0
  29. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/disk/vhdx.py +0 -0
  30. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/exceptions.py +0 -0
  31. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/tools/__init__.py +0 -0
  32. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/tools/envelope.py +0 -0
  33. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/util/__init__.py +0 -0
  34. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/util/envelope.py +0 -0
  35. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect/hypervisor/util/vmtar.py +0 -0
  36. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect.hypervisor.egg-info/SOURCES.txt +0 -0
  37. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect.hypervisor.egg-info/dependency_links.txt +0 -0
  38. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect.hypervisor.egg-info/entry_points.txt +0 -0
  39. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect.hypervisor.egg-info/requires.txt +0 -0
  40. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/dissect.hypervisor.egg-info/top_level.txt +0 -0
  41. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/pyproject.toml +0 -0
  42. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/setup.cfg +0 -0
  43. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/__init__.py +0 -0
  44. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/conftest.py +0 -0
  45. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/differencing.avhdx.gz +0 -0
  46. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/dynamic.vhd.gz +0 -0
  47. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/dynamic.vhdx.gz +0 -0
  48. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/encrypted.vmx +0 -0
  49. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/encryption.info +0 -0
  50. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/expanding.hdd/DiskDescriptor.xml +0 -0
  51. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/expanding.hdd/expanding.hdd +0 -0
  52. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/expanding.hdd/expanding.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  53. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/fixed.vhd.gz +0 -0
  54. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/fixed.vhdx.gz +0 -0
  55. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/local.tgz.ve +0 -0
  56. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/plain.hdd/DiskDescriptor.xml +0 -0
  57. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/plain.hdd/plain.hdd +0 -0
  58. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/plain.hdd/plain.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  59. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/sesparse.vmdk.gz +0 -0
  60. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/DiskDescriptor.xml +0 -0
  61. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd +0 -0
  62. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  63. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.1.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  64. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.2.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  65. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.3.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  66. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.4.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  67. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/split.hdd/split.hdd.5.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  68. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/test.VMRS +0 -0
  69. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/test.vgz +0 -0
  70. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/data/test.vmcx +0 -0
  71. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/docs/Makefile +0 -0
  72. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/docs/conf.py +0 -0
  73. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/docs/index.rst +0 -0
  74. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_envelope.py +0 -0
  75. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_hdd.py +0 -0
  76. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_hyperv.py +0 -0
  77. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_ovf.py +0 -0
  78. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_pvs.py +0 -0
  79. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_vbox.py +0 -0
  80. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_vhd.py +0 -0
  81. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_vhdx.py +0 -0
  82. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_vmtar.py +0 -0
  83. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tests/test_vmx.py +0 -0
  84. {dissect_hypervisor-3.16.dev3 → dissect_hypervisor-3.17.dev1}/tox.ini +0 -0
@@ -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
@@ -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
@@ -0,0 +1,199 @@
1
+ import pytest
2
+
3
+ from dissect.hypervisor.disk.c_vmdk import c_vmdk
4
+ from dissect.hypervisor.disk.vmdk import VMDK, DiskDescriptor, ExtentDescriptor
5
+
6
+
7
+ def test_vmdk_sesparse(sesparse_vmdk):
8
+ vmdk = VMDK(sesparse_vmdk)
9
+
10
+ disk = vmdk.disks[0]
11
+
12
+ assert disk.is_sesparse
13
+ assert disk._grain_directory_size == 0x20000
14
+ assert disk._grain_table_size == 0x1000
15
+ assert disk._grain_entry_type == c_vmdk.uint64
16
+ assert disk._grain_directory[0] == 0x1000000000000000
17
+
18
+ header = disk.header
19
+ assert header.magic == c_vmdk.SESPARSE_CONST_HEADER_MAGIC
20
+ assert header.version == 0x200000001
21
+
22
+ assert vmdk.read(0x1000000) == b"a" * 0x1000000
23
+
24
+
25
+ @pytest.mark.parametrize(
26
+ "extent_description, expected_extents",
27
+ [
28
+ (
29
+ 'RW 123456789 SPARSE "disk.vmdk"',
30
+ [
31
+ ExtentDescriptor(
32
+ raw='RW 123456789 SPARSE "disk.vmdk"',
33
+ access_mode="RW",
34
+ sectors=123456789,
35
+ type="SPARSE",
36
+ filename='"disk.vmdk"',
37
+ start_sector=None,
38
+ partition_uuid=None,
39
+ device_identifier=None,
40
+ ),
41
+ ],
42
+ ),
43
+ (
44
+ 'RW 123456789 FLAT "disk-flat.vmdk" 0',
45
+ [
46
+ ExtentDescriptor(
47
+ raw='RW 123456789 FLAT "disk-flat.vmdk" 0',
48
+ access_mode="RW",
49
+ sectors=123456789,
50
+ type="FLAT",
51
+ filename='"disk-flat.vmdk"',
52
+ start_sector=0,
53
+ partition_uuid=None,
54
+ device_identifier=None,
55
+ )
56
+ ],
57
+ ),
58
+ (
59
+ "RDONLY 0 ZERO",
60
+ [
61
+ ExtentDescriptor(
62
+ raw="RDONLY 0 ZERO",
63
+ access_mode="RDONLY",
64
+ sectors=0,
65
+ type="ZERO",
66
+ ),
67
+ ],
68
+ ),
69
+ (
70
+ 'NOACCESS 123456789 SPARSE "disk-sparse.vmdk" 123 partition-uuid device-id',
71
+ [
72
+ ExtentDescriptor(
73
+ raw='NOACCESS 123456789 SPARSE "disk-sparse.vmdk" 123 partition-uuid device-id',
74
+ access_mode="NOACCESS",
75
+ sectors=123456789,
76
+ type="SPARSE",
77
+ filename='"disk-sparse.vmdk"',
78
+ start_sector=123,
79
+ partition_uuid="partition-uuid",
80
+ device_identifier="device-id",
81
+ ),
82
+ ],
83
+ ),
84
+ ("RW 1234567890", []),
85
+ ('RDONLY "file.vmdk"', []),
86
+ ("NOACCESS", []),
87
+ (
88
+ 'RW 1234567890 SPARSE "disk with spaces.vmdk"',
89
+ [
90
+ ExtentDescriptor(
91
+ raw='RW 1234567890 SPARSE "disk with spaces.vmdk"',
92
+ access_mode="RW",
93
+ sectors=1234567890,
94
+ type="SPARSE",
95
+ filename='"disk with spaces.vmdk"',
96
+ start_sector=None,
97
+ partition_uuid=None,
98
+ device_identifier=None,
99
+ )
100
+ ],
101
+ ),
102
+ (
103
+ 'RW 1234567890 SPARSE "disk with spaces.vmdk" 123',
104
+ [
105
+ ExtentDescriptor(
106
+ raw='RW 1234567890 SPARSE "disk with spaces.vmdk" 123',
107
+ access_mode="RW",
108
+ sectors=1234567890,
109
+ type="SPARSE",
110
+ filename='"disk with spaces.vmdk"',
111
+ start_sector=123,
112
+ partition_uuid=None,
113
+ device_identifier=None,
114
+ )
115
+ ],
116
+ ),
117
+ (
118
+ 'RW 1234567890 SPARSE "disk with spaces.vmdk" 123 part-uuid',
119
+ [
120
+ ExtentDescriptor(
121
+ raw='RW 1234567890 SPARSE "disk with spaces.vmdk" 123 part-uuid',
122
+ access_mode="RW",
123
+ sectors=1234567890,
124
+ type="SPARSE",
125
+ filename='"disk with spaces.vmdk"',
126
+ start_sector=123,
127
+ partition_uuid="part-uuid",
128
+ device_identifier=None,
129
+ )
130
+ ],
131
+ ),
132
+ (
133
+ 'RW 1234567890 SPARSE "disk with spaces.vmdk" 123 part-uuid device-id',
134
+ [
135
+ ExtentDescriptor(
136
+ raw='RW 1234567890 SPARSE "disk with spaces.vmdk" 123 part-uuid device-id',
137
+ access_mode="RW",
138
+ sectors=1234567890,
139
+ type="SPARSE",
140
+ filename='"disk with spaces.vmdk"',
141
+ start_sector=123,
142
+ partition_uuid="part-uuid",
143
+ device_identifier="device-id",
144
+ )
145
+ ],
146
+ ),
147
+ (
148
+ r'RW 16777216 SPARSE "this is an example "\' diskëäô:)\\\'`\foo.vmdk" 123',
149
+ [
150
+ ExtentDescriptor(
151
+ raw=r'RW 16777216 SPARSE "this is an example "\' diskëäô:)\\\'`\foo.vmdk" 123',
152
+ access_mode="RW",
153
+ sectors=16777216,
154
+ type="SPARSE",
155
+ filename=r'"this is an example "\' diskëäô:)\\\'`\foo.vmdk"',
156
+ start_sector=123,
157
+ partition_uuid=None,
158
+ device_identifier=None,
159
+ )
160
+ ],
161
+ ),
162
+ (
163
+ r'RW 13371337 SPARSE "🦊 🦊 🦊.vmdk"',
164
+ [
165
+ ExtentDescriptor(
166
+ raw=r'RW 13371337 SPARSE "🦊 🦊 🦊.vmdk"',
167
+ access_mode="RW",
168
+ sectors=13371337,
169
+ type="SPARSE",
170
+ filename='"🦊 🦊 🦊.vmdk"',
171
+ )
172
+ ],
173
+ ),
174
+ ],
175
+ ids=(
176
+ "sparse",
177
+ "flat",
178
+ "zero",
179
+ "sparse-ids",
180
+ "bad-1",
181
+ "bad-2",
182
+ "bad-3",
183
+ "spaces-four-parts",
184
+ "spaces-five-parts",
185
+ "spaces-six-parts",
186
+ "spaces-seven-parts",
187
+ "specials-five-parts",
188
+ "emoji-four-parts",
189
+ ),
190
+ )
191
+ def test_vmdk_extent_description(extent_description: str, expected_extents: list) -> None:
192
+ """test if we correctly parse VMDK sparse and flat extent descriptions.
193
+
194
+ Resources:
195
+ - https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#22-extent-descriptions
196
+ """ # noqa: E501
197
+
198
+ descriptor = DiskDescriptor.parse(extent_description)
199
+ assert descriptor.extents == expected_extents
@@ -1,20 +0,0 @@
1
- from dissect.hypervisor.disk.c_vmdk import c_vmdk
2
- from dissect.hypervisor.disk.vmdk import VMDK
3
-
4
-
5
- def test_vmdk_sesparse(sesparse_vmdk):
6
- vmdk = VMDK(sesparse_vmdk)
7
-
8
- disk = vmdk.disks[0]
9
-
10
- assert disk.is_sesparse
11
- assert disk._grain_directory_size == 0x20000
12
- assert disk._grain_table_size == 0x1000
13
- assert disk._grain_entry_type == c_vmdk.uint64
14
- assert disk._grain_directory[0] == 0x1000000000000000
15
-
16
- header = disk.header
17
- assert header.magic == c_vmdk.SESPARSE_CONST_HEADER_MAGIC
18
- assert header.version == 0x200000001
19
-
20
- assert vmdk.read(0x1000000) == b"a" * 0x1000000