Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Parallels HDD/HDS and PVS #16

Merged
merged 6 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions dissect/hypervisor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from dissect.hypervisor.backup import vma, wim, xva
from dissect.hypervisor.descriptor import hyperv, ovf, vbox, vmx
from dissect.hypervisor.disk import qcow2, vdi, vhd, vhdx, vmdk
from dissect.hypervisor.descriptor import hyperv, ovf, pvs, vbox, vmx
from dissect.hypervisor.disk import hdd, qcow2, vdi, vhd, vhdx, vmdk
from dissect.hypervisor.util import envelope, vmtar

__all__ = [
"envelope",
"hdd",
"hyperv",
"ovf",
"pvs",
"qcow2",
"vbox",
"vdi",
Expand Down
25 changes: 25 additions & 0 deletions dissect/hypervisor/descriptor/pvs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import IO, Iterator
from xml.etree.ElementTree import Element

try:
from defusedxml import ElementTree
except ImportError:
from xml.etree import ElementTree


class PVS:
"""Parallels VM settings file.

Args:
fh: The file-like object to a PVS file.
"""

def __init__(self, fh: IO):
self._xml: Element = ElementTree.fromstring(fh.read())

def disks(self) -> Iterator[str]:
"""Yield the disk file names."""
for hdd_elem in self._xml.iterfind(".//Hdd"):
system_name = hdd_elem.find("SystemName")
if system_name is not None:
yield system_name.text
14 changes: 10 additions & 4 deletions dissect/hypervisor/descriptor/vbox.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
from xml.etree import ElementTree
from typing import IO, Iterator
from xml.etree.ElementTree import Element

try:
from defusedxml import ElementTree
except ImportError:
from xml.etree import ElementTree


class VBox:
VBOX_XML_NAMESPACE = "{http://www.virtualbox.org/}"

def __init__(self, fh):
self._xml = ElementTree.fromstring(fh.read())
def __init__(self, fh: IO):
self._xml: Element = ElementTree.fromstring(fh.read())

def disks(self):
def disks(self) -> Iterator[str]:
for hdd_elem in self._xml.findall(
f".//{self.VBOX_XML_NAMESPACE}HardDisk[@location][@format='VDI'][@type='Normal']"
):
Expand Down
68 changes: 68 additions & 0 deletions dissect/hypervisor/disk/c_hdd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# References:
# - https://src.openvz.org/projects/OVZ/repos/ploop/browse/include/ploop1_image.h
# - https://github.com/qemu/qemu/blob/master/docs/interop/parallels.txt


from dissect import cstruct

hdd_def = """
/* Compressed disk v1 signature */
#define SIGNATURE_STRUCTURED_DISK_V1 b"WithoutFreeSpace"

/* Compressed disk v2 signature */
#define SIGNATURE_STRUCTURED_DISK_V2 b"WithouFreSpacExt"

/* Sign that the disk is in "using" state */
#define SIGNATURE_DISK_IN_USE 0x746F6E59

#define SECTOR_LOG 9
#define SECTOR_SIZE (1 << SECTOR_LOG)

struct pvd_header {
char m_Sig[16]; /* Signature */
uint32 m_Type; /* Disk type */
uint32 m_Heads; /* heads count */
uint32 m_Cylinders; /* tracks count */
uint32 m_Sectors; /* Sectors per track count */
uint32 m_Size; /* Size of disk in tracks */
union { /* Size of disk in 512-byte sectors */
struct {
uint32 m_SizeInSectors_v1;
uint32 Unused;
};
uint64 m_SizeInSectors_v2;
};
uint32 m_DiskInUse; /* Disk in use */
uint32 m_FirstBlockOffset; /* First data block offset (in sectors) */
uint32 m_Flags; /* Misc flags */
uint64 m_FormatExtensionOffset; /* Optional header offset in bytes */
};

struct pvd_ext_block_check {
// Format Extension magic = 0xAB234CEF23DCEA87
uint64 m_Magic;
// Md5 checksum of the whole (without top 24 bytes of block check)
// Format Extension Block.
uint8 m_Md5[16];
};

struct pvd_ext_block_element_header {
uint64 magic;
uint64 flags;
uint32 size;
uint32 unused32;
};

struct pvd_dirty_bitmap_raw {
uint64 m_Size;
uint8 m_Id[16];
uint32 m_Granularity;
uint32 m_L1Size;
uint64 m_L1[m_L1Size];
};
"""

c_hdd = cstruct.cstruct()
c_hdd.load(hdd_def)

SECTOR_SIZE = c_hdd.SECTOR_SIZE
Loading