Skip to content

Commit

Permalink
Revert "Improve VMDK extent descriptor parsing (#44)"
Browse files Browse the repository at this point in the history
This reverts commit 8ad6252.
  • Loading branch information
Miauwkeru committed Nov 14, 2024
1 parent c3c2bb1 commit 50464c1
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 148 deletions.
107 changes: 33 additions & 74 deletions dissect/hypervisor/disk/vmdk.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from __future__ import annotations

import ctypes
import io
import logging
import os
import textwrap
import zlib
from bisect import bisect_right
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path

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

for extent in self.descriptor.extents:
if extent.type in ["SPARSE", "VMFSSPARSE", "SESPARSE"]:
sdisk_fh = path.with_name(extent.filename).open("rb")
for _, size, extent_type, filename in self.descriptor.extents:
if extent_type in ["SPARSE", "VMFSSPARSE", "SESPARSE"]:
sdisk_fh = path.with_name(filename).open("rb")
self.disks.append(SparseDisk(sdisk_fh, parent=self.parent))
elif extent.type in ["VMFS", "FLAT"]:
rdisk_fh = path.with_name(extent.filename).open("rb")
self.disks.append(RawDisk(rdisk_fh, extent.sectors * SECTOR_SIZE))
elif extent_type in ["VMFS", "FLAT"]:
rdisk_fh = path.with_name(filename).open("rb")
self.disks.append(RawDisk(rdisk_fh, size * SECTOR_SIZE))

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


@dataclass
class ExtentDescriptor:
access_mode: str
sectors: int
type: str
filename: str | None = None
start_sector: int | None = None
partition_uuid: str | None = None
device_identifier: str | None = None

def __post_init__(self) -> None:
self._raw = " ".join(map(str, [v for v in self.__dict__.values() if v is not None]))
self.sectors = int(self.sectors)

if self.filename:
self.filename = self.filename.strip('"')

if self.start_sector:
self.start_sector = int(self.start_sector)

def __repr__(self) -> str:
return f"<ExtentDescriptor {self._raw}>"

def __str__(self) -> str:
return self._raw


class DiskDescriptor:
def __init__(
self, attr: dict, extents: list[ExtentDescriptor], disk_db: dict, sectors: int, raw_config: str | None = None
):
def __init__(self, attr, extents, disk_db, sectors, raw_config=None):
self.attr = attr
self.extents = extents
self.ddb = disk_db
self.sectors = sectors
self.raw = raw_config

@classmethod
def parse(cls, vmdk_config: str) -> DiskDescriptor:
"""Return :class:`DiskDescriptor` based on the provided ``vmdk_config``.
Resources:
- https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc
""" # noqa: E501

def parse(cls, vmdk_config):
descriptor_settings = {}
extents: list[ExtentDescriptor] = []
extents = []
disk_db = {}
sectors = 0

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

if line.startswith("RW ") or line.startswith("RDONLY ") or line.startswith("NOACCESS "):
# Extent descriptors can have up to seven values according to libvmdk documentation.
parts = line.split(" ", maxsplit=6)

if len(parts) < 3:
log.warning("Unexpected ExtentDescriptor format in vmdk config: %s, ignoring", line)
continue

extent = ExtentDescriptor(*parts)
sectors += extent.sectors
extents.append(extent)
access_type, size, extent_type, filename = line.split(" ", 3)
filename = filename.strip('"')
size = int(size)
sectors += size
extents.append([access_type, size, extent_type, filename])
continue

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

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

def __str__(self) -> str:
str_template = textwrap.dedent(
"""\
# Disk DescriptorFile
version=1
{}
# Extent Description
{}
def __str__(self):
str_template = """\
# Disk DescriptorFile
version=1
{}
# The Disk Data Base
#DDB
# Extent Description
{}
{}"""
)
# The Disk Data Base
#DDB
{}"""
str_template = textwrap.dedent(str_template)
descriptor_settings = []
for setting, value in self.attr.items():
if setting != "version":
descriptor_settings.append(f"{setting}={value}")
if setting == "version":
continue
descriptor_settings.append("{}={}".format(setting, value))
descriptor_settings = "\n".join(descriptor_settings)

extents = "\n".join(map(str, self.extents))
extents = []
for access_type, size, extent_type, filename in self.extents:
extents.append('{} {} {} "{}"'.format(access_type, size, extent_type, filename))
extents = "\n".join(extents)

disk_db = []
for setting, value in self.ddb.items():
disk_db.append(f'{setting} = "{value}"')
disk_db.append('{} = "{}"'.format(setting, value))
disk_db = "\n".join(disk_db)

return str_template.format(descriptor_settings, extents, disk_db)
Expand Down
75 changes: 1 addition & 74 deletions tests/test_vmdk.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import pytest

from dissect.hypervisor.disk.c_vmdk import c_vmdk
from dissect.hypervisor.disk.vmdk import VMDK, DiskDescriptor, ExtentDescriptor
from dissect.hypervisor.disk.vmdk import VMDK


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

assert vmdk.read(0x1000000) == b"a" * 0x1000000


@pytest.mark.parametrize(
"extent_description, expected_extents",
[
(
'RW 123456789 SPARSE "disk.vmdk"',
[
ExtentDescriptor(
access_mode="RW",
sectors=123456789,
type="SPARSE",
filename="disk.vmdk",
partition_uuid=None,
device_identifier=None,
),
],
),
(
'RW 123456789 FLAT "disk-flat.vmdk" 0',
[
ExtentDescriptor(
access_mode="RW",
sectors=123456789,
type="FLAT",
filename="disk-flat.vmdk",
start_sector=0,
partition_uuid=None,
device_identifier=None,
)
],
),
(
"RDONLY 0 ZERO",
[
ExtentDescriptor(
access_mode="RDONLY",
sectors=0,
type="ZERO",
),
],
),
(
'NOACCESS 123456789 SPARSE "disk-sparse.vmdk" 123 partition-uuid device-id',
[
ExtentDescriptor(
access_mode="NOACCESS",
sectors=123456789,
type="SPARSE",
filename="disk-sparse.vmdk",
start_sector=123,
partition_uuid="partition-uuid",
device_identifier="device-id",
),
],
),
("RW 1234567890", []),
('RDONLY "file.vmdk"', []),
("NOACCESS", []),
],
ids=("sparse", "flat", "zero", "sparse-ids", "bad-1", "bad-2", "bad-3"),
)
def test_vmdk_extent_description(extent_description: str, expected_extents: list) -> None:
"""test if we correctly parse VMDK sparse and flat extent descriptions.
Resources:
- https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#22-extent-descriptions
""" # noqa: E501

descriptor = DiskDescriptor.parse(extent_description)
assert descriptor.extents == expected_extents

0 comments on commit 50464c1

Please sign in to comment.