Skip to content

Commit 50464c1

Browse files
committed
Revert "Improve VMDK extent descriptor parsing (#44)"
This reverts commit 8ad6252.
1 parent c3c2bb1 commit 50464c1

File tree

2 files changed

+34
-148
lines changed

2 files changed

+34
-148
lines changed

dissect/hypervisor/disk/vmdk.py

Lines changed: 33 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
from __future__ import annotations
2-
31
import ctypes
42
import io
53
import logging
64
import os
75
import textwrap
86
import zlib
97
from bisect import bisect_right
10-
from dataclasses import dataclass
118
from functools import lru_cache
129
from pathlib import Path
1310

@@ -62,13 +59,13 @@ def __init__(self, fh):
6259
if self.descriptor.attr["parentCID"] != "ffffffff":
6360
self.parent = open_parent(path.parent, self.descriptor.attr["parentFileNameHint"])
6461

65-
for extent in self.descriptor.extents:
66-
if extent.type in ["SPARSE", "VMFSSPARSE", "SESPARSE"]:
67-
sdisk_fh = path.with_name(extent.filename).open("rb")
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")
6865
self.disks.append(SparseDisk(sdisk_fh, parent=self.parent))
69-
elif extent.type in ["VMFS", "FLAT"]:
70-
rdisk_fh = path.with_name(extent.filename).open("rb")
71-
self.disks.append(RawDisk(rdisk_fh, extent.sectors * SECTOR_SIZE))
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))
7269

7370
elif magic in (COWD_MAGIC, VMDK_MAGIC, SESPARSE_MAGIC):
7471
sparse_disk = SparseDisk(fh)
@@ -401,53 +398,18 @@ def __getattr__(self, attr):
401398
return getattr(self.hdr, attr)
402399

403400

404-
@dataclass
405-
class ExtentDescriptor:
406-
access_mode: str
407-
sectors: int
408-
type: str
409-
filename: str | None = None
410-
start_sector: int | None = None
411-
partition_uuid: str | None = None
412-
device_identifier: str | None = None
413-
414-
def __post_init__(self) -> None:
415-
self._raw = " ".join(map(str, [v for v in self.__dict__.values() if v is not None]))
416-
self.sectors = int(self.sectors)
417-
418-
if self.filename:
419-
self.filename = self.filename.strip('"')
420-
421-
if self.start_sector:
422-
self.start_sector = int(self.start_sector)
423-
424-
def __repr__(self) -> str:
425-
return f"<ExtentDescriptor {self._raw}>"
426-
427-
def __str__(self) -> str:
428-
return self._raw
429-
430-
431401
class DiskDescriptor:
432-
def __init__(
433-
self, attr: dict, extents: list[ExtentDescriptor], disk_db: dict, sectors: int, raw_config: str | None = None
434-
):
402+
def __init__(self, attr, extents, disk_db, sectors, raw_config=None):
435403
self.attr = attr
436404
self.extents = extents
437405
self.ddb = disk_db
438406
self.sectors = sectors
439407
self.raw = raw_config
440408

441409
@classmethod
442-
def parse(cls, vmdk_config: str) -> DiskDescriptor:
443-
"""Return :class:`DiskDescriptor` based on the provided ``vmdk_config``.
444-
445-
Resources:
446-
- https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc
447-
""" # noqa: E501
448-
410+
def parse(cls, vmdk_config):
449411
descriptor_settings = {}
450-
extents: list[ExtentDescriptor] = []
412+
extents = []
451413
disk_db = {}
452414
sectors = 0
453415

@@ -458,16 +420,11 @@ def parse(cls, vmdk_config: str) -> DiskDescriptor:
458420
continue
459421

460422
if line.startswith("RW ") or line.startswith("RDONLY ") or line.startswith("NOACCESS "):
461-
# Extent descriptors can have up to seven values according to libvmdk documentation.
462-
parts = line.split(" ", maxsplit=6)
463-
464-
if len(parts) < 3:
465-
log.warning("Unexpected ExtentDescriptor format in vmdk config: %s, ignoring", line)
466-
continue
467-
468-
extent = ExtentDescriptor(*parts)
469-
sectors += extent.sectors
470-
extents.append(extent)
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])
471428
continue
472429

473430
setting, _, value = line.partition("=")
@@ -481,33 +438,35 @@ def parse(cls, vmdk_config: str) -> DiskDescriptor:
481438

482439
return cls(descriptor_settings, extents, disk_db, sectors, vmdk_config)
483440

484-
def __str__(self) -> str:
485-
str_template = textwrap.dedent(
486-
"""\
487-
# Disk DescriptorFile
488-
version=1
489-
{}
490-
491-
# Extent Description
492-
{}
441+
def __str__(self):
442+
str_template = """\
443+
# Disk DescriptorFile
444+
version=1
445+
{}
493446
494-
# The Disk Data Base
495-
#DDB
447+
# Extent Description
448+
{}
496449
497-
{}"""
498-
)
450+
# The Disk Data Base
451+
#DDB
499452
453+
{}"""
454+
str_template = textwrap.dedent(str_template)
500455
descriptor_settings = []
501456
for setting, value in self.attr.items():
502-
if setting != "version":
503-
descriptor_settings.append(f"{setting}={value}")
457+
if setting == "version":
458+
continue
459+
descriptor_settings.append("{}={}".format(setting, value))
504460
descriptor_settings = "\n".join(descriptor_settings)
505461

506-
extents = "\n".join(map(str, self.extents))
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)
507466

508467
disk_db = []
509468
for setting, value in self.ddb.items():
510-
disk_db.append(f'{setting} = "{value}"')
469+
disk_db.append('{} = "{}"'.format(setting, value))
511470
disk_db = "\n".join(disk_db)
512471

513472
return str_template.format(descriptor_settings, extents, disk_db)

tests/test_vmdk.py

Lines changed: 1 addition & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import pytest
2-
31
from dissect.hypervisor.disk.c_vmdk import c_vmdk
4-
from dissect.hypervisor.disk.vmdk import VMDK, DiskDescriptor, ExtentDescriptor
2+
from dissect.hypervisor.disk.vmdk import VMDK
53

64

75
def test_vmdk_sesparse(sesparse_vmdk):
@@ -20,74 +18,3 @@ def test_vmdk_sesparse(sesparse_vmdk):
2018
assert header.version == 0x200000001
2119

2220
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-
access_mode="RW",
33-
sectors=123456789,
34-
type="SPARSE",
35-
filename="disk.vmdk",
36-
partition_uuid=None,
37-
device_identifier=None,
38-
),
39-
],
40-
),
41-
(
42-
'RW 123456789 FLAT "disk-flat.vmdk" 0',
43-
[
44-
ExtentDescriptor(
45-
access_mode="RW",
46-
sectors=123456789,
47-
type="FLAT",
48-
filename="disk-flat.vmdk",
49-
start_sector=0,
50-
partition_uuid=None,
51-
device_identifier=None,
52-
)
53-
],
54-
),
55-
(
56-
"RDONLY 0 ZERO",
57-
[
58-
ExtentDescriptor(
59-
access_mode="RDONLY",
60-
sectors=0,
61-
type="ZERO",
62-
),
63-
],
64-
),
65-
(
66-
'NOACCESS 123456789 SPARSE "disk-sparse.vmdk" 123 partition-uuid device-id',
67-
[
68-
ExtentDescriptor(
69-
access_mode="NOACCESS",
70-
sectors=123456789,
71-
type="SPARSE",
72-
filename="disk-sparse.vmdk",
73-
start_sector=123,
74-
partition_uuid="partition-uuid",
75-
device_identifier="device-id",
76-
),
77-
],
78-
),
79-
("RW 1234567890", []),
80-
('RDONLY "file.vmdk"', []),
81-
("NOACCESS", []),
82-
],
83-
ids=("sparse", "flat", "zero", "sparse-ids", "bad-1", "bad-2", "bad-3"),
84-
)
85-
def test_vmdk_extent_description(extent_description: str, expected_extents: list) -> None:
86-
"""test if we correctly parse VMDK sparse and flat extent descriptions.
87-
88-
Resources:
89-
- https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#22-extent-descriptions
90-
""" # noqa: E501
91-
92-
descriptor = DiskDescriptor.parse(extent_description)
93-
assert descriptor.extents == expected_extents

0 commit comments

Comments
 (0)