VHDX.cpp revision abebc94c58a78d5a80809dfb6aa4b17c1ab8fe33
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX - VHDX Disk image, Core Code.
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Copyright (C) 2012-2013 Oracle Corporation
45e9809aff7304721fddb95654901b32195c9c7avboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
45e9809aff7304721fddb95654901b32195c9c7avboxsync * available from http://www.virtualbox.org. This file is free software;
45e9809aff7304721fddb95654901b32195c9c7avboxsync * you can redistribute it and/or modify it under the terms of the GNU
45e9809aff7304721fddb95654901b32195c9c7avboxsync * General Public License (GPL) as published by the Free Software
45e9809aff7304721fddb95654901b32195c9c7avboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
45e9809aff7304721fddb95654901b32195c9c7avboxsync/*******************************************************************************
45e9809aff7304721fddb95654901b32195c9c7avboxsync* Header Files *
45e9809aff7304721fddb95654901b32195c9c7avboxsync*******************************************************************************/
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo: Log group */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/*******************************************************************************
45e9809aff7304721fddb95654901b32195c9c7avboxsync* On disk data structures *
45e9809aff7304721fddb95654901b32195c9c7avboxsync*******************************************************************************/
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX file type identifier.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Creator ID - UTF-16 string (not neccessarily null terminated). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX file type identifier. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** VHDX file type identifier signature ("vhdxfile"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_FILE_IDENTIFIER_SIGNATURE UINT64_C(0x656c696678646876)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Start offset of the VHDX file type identifier. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX header.
45e9809aff7304721fddb95654901b32195c9c7avboxsynctypedef struct VhdxHeader
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Checksum. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Sequence number. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** File write UUID. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Data write UUID. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Log UUID. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Version of the log format. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** VHDX format version.. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Length of the log region. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Start offset of the log offset in the file. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Reserved bytes. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** VHDX header signature ("head"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Start offset of the first VHDX header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Start offset of the second VHDX header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Current Log format version. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Current VHDX format version. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX region table header
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Checksum. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Number of region table entries following this header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Reserved. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX region table header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** VHDX region table header signature. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_REGION_TBL_HDR_SIGNATURE UINT32_C(0x69676572)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Maximum number of entries which can follow. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_REGION_TBL_HDR_ENTRY_COUNT_MAX UINT32_C(2047)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Offset where the region table is stored (192 KB). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Maximum size of the region table. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX region table entry.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Object UUID. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** File offset of the region. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Length of the region in bytes. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Flags for this object. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX region table entry. */
45e9809aff7304721fddb95654901b32195c9c7avboxsynctypedef struct VhdxRegionTblEntry *PVhdxRegionTblEntry;
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Flag whether this region is required. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED RT_BIT_32(0)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** UUID for the BAT region. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_REGION_TBL_ENTRY_UUID_BAT "2dc27766-f623-4200-9d64-115e9bfd4a08"
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** UUID for the metadata region. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_REGION_TBL_ENTRY_UUID_METADATA "8b7ca206-4790-4b9a-b8fe-575f050f886e"
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX Log entry header.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Checksum. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Total length of the entry in bytes. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Tail of the log entries. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Sequence number. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Number of descriptors in this log entry. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Reserved. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Log UUID. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** VHDX file size in bytes while the log entry was written. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** File size in bytes all allocated file structures fit into when the
45e9809aff7304721fddb95654901b32195c9c7avboxsync * log entry was written. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX log entry header. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** VHDX log entry signature ("loge"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_LOG_ENTRY_HEADER_SIGNATURE UINT32_C(0x65676f6c)
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX log zero descriptor.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature of this descriptor. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Reserved. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Length of the section to zero. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** File offset to write zeros to. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Sequence number (must macht the field in the log entry header). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX log zero descriptor. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Signature of a VHDX log zero descriptor ("zero"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_LOG_ZERO_DESC_SIGNATURE UINT32_C(0x6f72657a)
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX log data descriptor.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature of this descriptor. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Trailing 4 bytes removed from the update. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Leading 8 bytes removed from the update. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** File offset to write zeros to. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Sequence number (must macht the field in the log entry header). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX log data descriptor. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Signature of a VHDX log data descriptor ("desc"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_LOG_DATA_DESC_SIGNATURE UINT32_C(0x63736564)
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX log data sector.
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Signature of the data sector. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** 4 most significant bytes of the sequence number. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** Raw data associated with the update. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** 4 least significant bytes of the sequence number. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Pointer to an on disk VHDX log data sector. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Signature of a VHDX log data sector ("data"). */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_LOG_DATA_SECTOR_SIGNATURE UINT32_C(0x61746164)
45e9809aff7304721fddb95654901b32195c9c7avboxsync * VHDX BAT entry.
45e9809aff7304721fddb95654901b32195c9c7avboxsynctypedef struct VhdxBatEntry
45e9809aff7304721fddb95654901b32195c9c7avboxsync /** The BAT entry, contains state and offset. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Return the BAT state from a given entry. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_GET_STATE(bat) ((bat) & UINT64_C(0x7))
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Get the FileOffsetMB field from a given BAT entry. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_GET_FILE_OFFSET_MB(bat) (((bat) & UINT64_C(0xfffffffffff00000)) >> 20)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Get a byte offset from the BAT entry. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_GET_FILE_OFFSET(bat) (VHDX_BAT_ENTRY_GET_FILE_OFFSET_MB(bat) * (uint64_t)_1M)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Block not present and the data is undefined. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_NOT_PRESENT (0)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Data in this block is undefined. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Data in this block contains zeros. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Block was unmapped by the application or system and data is either zero or
45e9809aff7304721fddb95654901b32195c9c7avboxsync * the data before the block was unmapped. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Block data is in the file pointed to by the FileOffsetMB field. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_FULLY_PRESENT (6)
45e9809aff7304721fddb95654901b32195c9c7avboxsync/** Block is partially present, use sector bitmap to get present sectors. */
45e9809aff7304721fddb95654901b32195c9c7avboxsync#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT (7)
#define VHDX_BAT_ENTRY_SB_BLOCK_NOT_PRESENT (0)
typedef struct VhdxMetadataTblHdr
#pragma pack()
typedef struct VhdxMetadataTblEntry
#pragma pack()
typedef struct VhdxFileParameters
#pragma pack()
typedef struct VhdxVDiskSize
#pragma pack()
typedef struct VhdxPage83Data
#pragma pack()
typedef struct VhdxVDiskLogicalSectorSize
#pragma pack()
typedef struct VhdxVDiskPhysicalSectorSize
#pragma pack()
typedef struct VhdxParentLocatorHeader
#pragma pack()
typedef struct VhdxParentLocatorEntry
#pragma pack()
typedef enum VHDXMETADATAITEM
typedef struct VHDXMETADATAITEMPROPS
const char *pszItemUuid;
bool fIsUser;
bool fIsVDisk;
bool fIsRequired;
typedef struct VHDXIMAGE
const char *pszFilename;
unsigned uOpenFlags;
unsigned uImageFlags;
unsigned uVersion;
typedef enum VHDXECONV
VHDXECONV_H2F = 0,
} VHDXECONV;
{VHDX_METADATA_TBL_ENTRY_ITEM_LOG_SECT_SIZE, false, true, true, VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE},
{VHDX_METADATA_TBL_ENTRY_ITEM_PHYS_SECT_SIZE, false, true, true, VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE},
DECLINLINE(void) vhdxConvFileIdentifierEndianess(VHDXECONV enmConv, PVhdxFileIdentifier pFileIdentifierConv,
DECLINLINE(void) vhdxConvRegionTblEntryEndianess(VHDXECONV enmConv, PVhdxRegionTblEntry pRegTblEntConv,
DECLINLINE(void) vhdxConvLogDataSectorEndianess(VHDXECONV enmConv, PVhdxLogDataSector pLogDataSectorConv,
DECLINLINE(void) vhdxConvMetadataTblHdrEndianess(VHDXECONV enmConv, PVhdxMetadataTblHdr pMetadataTblHdrConv,
DECLINLINE(void) vhdxConvMetadataTblEntryEndianess(VHDXECONV enmConv, PVhdxMetadataTblEntry pMetadataTblEntryConv,
DECLINLINE(void) vhdxConvFileParamsEndianess(VHDXECONV enmConv, PVhdxFileParameters pFileParamsConv,
DECLINLINE(void) vhdxConvVDiskLogSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskLogicalSectorSize pVDiskLogSectSizeConv,
pVDiskLogSectSizeConv->u32LogicalSectorSize = SET_ENDIAN_U32(pVDiskLogSectSize->u32LogicalSectorSize);
DECLINLINE(void) vhdxConvVDiskPhysSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskPhysicalSectorSize pVDiskPhysSectSizeConv,
pVDiskPhysSectSizeConv->u64PhysicalSectorSize = SET_ENDIAN_U64(pVDiskPhysSectSize->u64PhysicalSectorSize);
DECLINLINE(void) vhdxConvParentLocatorHeaderEndianness(VHDXECONV enmConv, PVhdxParentLocatorHeader pParentLocatorHdrConv,
vhdxConvUuidEndianess(enmConv, &pParentLocatorHdrConv->UuidLocatorType, &pParentLocatorHdr->UuidLocatorType);
DECLINLINE(void) vhdxConvParentLocatorEntryEndianess(VHDXECONV enmConv, PVhdxParentLocatorEntry pParentLocatorEntryConv,
if (pImage)
return rc;
return rc;
bool fHdr1Valid = false;
bool fHdr2Valid = false;
fHdr1Valid = true;
fHdr2Valid = true;
if (pHdr1)
if (pHdr2)
return rc;
cDataBlocks++;
if (paBatEntries)
for (unsigned i = 0; i < cBatEntries; i++)
&& (i % uChunkRatio) == 0)
"VHDX: Sector bitmap block at entry %u of image \'%s\' marked as present, violation of the specification",
"VHDX: Payload block at entry %u of image \'%s\' marked as partially present, violation of the specification",
"VHDX: Mismatch between calculated number of BAT entries and region size (expected %u got %u) for image \'%s\'",
&& paBatEntries)
return rc;
return rc;
return rc;
return rc;
else if (cbRegion < (MetadataTblHdr.u16EntryCount * sizeof(VhdxMetadataTblEntry) + sizeof(VhdxMetadataTblHdr)))
switch (enmMetadataItem)
case VHDXMETADATAITEM_UNKNOWN:
return rc;
if (pbRegionTbl)
bool fBatRegPresent = false;
fBatRegPresent = true;
pRegTblEntry++;
if (fBatRegPresent)
if (pbRegionTbl)
return rc;
return VERR_NOT_SUPPORTED;
return rc;
LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
|| !*pszFilename)
&pStorage);
if (pStorage)
return rc;
LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
int rc;
|| !*pszFilename)
if (!pImage)
return rc;
if ( !pImage
|| !pszFilename
|| !*pszFilename)
return rc;
int rc;
return rc;
|| cbToRead == 0)
uint32_t idxBat = (uint32_t)(uOffset / pImage->cbBlock); Assert(idxBat == uOffset / pImage->cbBlock);
if (pcbActuallyRead)
return rc;
LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
int rc;
|| cbToWrite == 0)
return rc;
int rc;
return rc;
if (pImage)
return cb;
return cb;
if (pImage)
return cb;
int rc;
if (pImage)
LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
return rc;
LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
if (pImage)
return rc;
if (pImage)
LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
return rc;
LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
if (pImage)
return rc;
unsigned uImageFlags;
if (pImage)
uImageFlags = 0;
return uImageFlags;
unsigned uOpenFlags;
if (pImage)
uOpenFlags = 0;
return uOpenFlags;
if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
return rc;
LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
int rc;
if (pImage)
return rc;
if (pImage)
sizeof(VBOXHDDBACKEND),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,