VDICore.h revision e4f367251aede667a6de69baa54ef9eb5f150871
/* $Id$ */
/** @file
* Virtual Disk Image (VDI), Core Code Header (internal).
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#ifndef ___VDICore_h___
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <VBox/vd.h>
#include <VBox/err.h>
#include <VBox/log.h>
#include <iprt/alloc.h>
#include <iprt/assert.h>
#include <iprt/uuid.h>
#include <iprt/string.h>
#include <iprt/asm.h>
/*******************************************************************************
* Constants And Macros, Structures and Typedefs *
*******************************************************************************/
/** Image info, not handled anyhow.
* Must be less than 64 bytes in length, including the trailing 0.
*/
#define VDI_IMAGE_FILE_INFO "<<< Oracle VM VirtualBox Disk Image >>>\n"
/** The Sector size.
* Currently we support only 512 bytes sectors.
*/
#define VDI_GEOMETRY_SECTOR_SIZE (512)
/** 512 = 2^^9 */
#define VDI_GEOMETRY_SECTOR_SHIFT (9)
/**
* Harddisk geometry.
*/
#pragma pack(1)
typedef struct VDIDISKGEOMETRY
{
/** Cylinders. */
uint32_t cCylinders;
/** Heads. */
uint32_t cHeads;
/** Sectors per track. */
uint32_t cSectors;
/** Sector size. (bytes per sector) */
uint32_t cbSector;
} VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
#pragma pack()
/** Image signature. */
#define VDI_IMAGE_SIGNATURE (0xbeda107f)
/**
* Pre-Header to be stored in image file - used for version control.
*/
#pragma pack(1)
typedef struct VDIPREHEADER
{
/** Just text info about image type, for eyes only. */
char szFileInfo[64];
/** The image signature (VDI_IMAGE_SIGNATURE). */
uint32_t u32Signature;
/** The image version (VDI_IMAGE_VERSION). */
uint32_t u32Version;
} VDIPREHEADER, *PVDIPREHEADER;
#pragma pack()
/**
* Size of szComment field of HDD image header.
*/
#define VDI_IMAGE_COMMENT_SIZE 256
/**
* Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
* Prepended by VDIPREHEADER.
*/
#pragma pack(1)
typedef struct VDIHEADER0
{
/** The image type (VDI_IMAGE_TYPE_*). */
uint32_t u32Type;
/** Image flags (VDI_IMAGE_FLAGS_*). */
uint32_t fFlags;
/** Image comment. (UTF-8) */
char szComment[VDI_IMAGE_COMMENT_SIZE];
/** Legacy image geometry (previous code stored PCHS there). */
VDIDISKGEOMETRY LegacyGeometry;
/** Size of disk (in bytes). */
uint64_t cbDisk;
/** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
uint32_t cbBlock;
/** Number of blocks. */
uint32_t cBlocks;
/** Number of allocated blocks. */
uint32_t cBlocksAllocated;
/** UUID of image. */
RTUUID uuidCreate;
/** UUID of image's last modification. */
RTUUID uuidModify;
/** Only for secondary images - UUID of primary image. */
RTUUID uuidLinkage;
} VDIHEADER0, *PVDIHEADER0;
#pragma pack()
/**
* Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
* VDI_IMAGE_VERSION_MINOR = 1. Prepended by VDIPREHEADER.
*/
#pragma pack(1)
typedef struct VDIHEADER1
{
/** Size of this structure in bytes. */
uint32_t cbHeader;
/** The image type (VDI_IMAGE_TYPE_*). */
uint32_t u32Type;
/** Image flags (VDI_IMAGE_FLAGS_*). */
uint32_t fFlags;
/** Image comment. (UTF-8) */
char szComment[VDI_IMAGE_COMMENT_SIZE];
/** Offset of Blocks array from the beginning of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offBlocks;
/** Offset of image data from the beginning of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offData;
/** Legacy image geometry (previous code stored PCHS there). */
VDIDISKGEOMETRY LegacyGeometry;
/** Was BIOS HDD translation mode, now unused. */
uint32_t u32Dummy;
/** Size of disk (in bytes). */
uint64_t cbDisk;
/** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
uint32_t cbBlock;
/** Size of additional service information of every data block.
* Prepended before block data. May be 0.
* Should be a power of 2 and sector-aligned for optimization reasons. */
uint32_t cbBlockExtra;
/** Number of blocks. */
uint32_t cBlocks;
/** Number of allocated blocks. */
uint32_t cBlocksAllocated;
/** UUID of image. */
RTUUID uuidCreate;
/** UUID of image's last modification. */
RTUUID uuidModify;
/** Only for secondary images - UUID of previous image. */
RTUUID uuidLinkage;
/** Only for secondary images - UUID of previous image's last modification. */
RTUUID uuidParentModify;
} VDIHEADER1, *PVDIHEADER1;
#pragma pack()
/**
* Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
* VDI_IMAGE_VERSION_MINOR = 1, the slightly changed variant necessary as the
* old released code doesn't support changing the minor version at all.
*/
#pragma pack(1)
typedef struct VDIHEADER1PLUS
{
/** Size of this structure in bytes. */
uint32_t cbHeader;
/** The image type (VDI_IMAGE_TYPE_*). */
uint32_t u32Type;
/** Image flags (VDI_IMAGE_FLAGS_*). */
uint32_t fFlags;
/** Image comment. (UTF-8) */
char szComment[VDI_IMAGE_COMMENT_SIZE];
/** Offset of blocks array from the beginning of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offBlocks;
/** Offset of image data from the beginning of image file.
* Should be sector-aligned for HDD access optimization. */
uint32_t offData;
/** Legacy image geometry (previous code stored PCHS there). */
VDIDISKGEOMETRY LegacyGeometry;
/** Was BIOS HDD translation mode, now unused. */
uint32_t u32Dummy;
/** Size of disk (in bytes). */
uint64_t cbDisk;
/** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
uint32_t cbBlock;
/** Size of additional service information of every data block.
* Prepended before block data. May be 0.
* Should be a power of 2 and sector-aligned for optimization reasons. */
uint32_t cbBlockExtra;
/** Number of blocks. */
uint32_t cBlocks;
/** Number of allocated blocks. */
uint32_t cBlocksAllocated;
/** UUID of image. */
RTUUID uuidCreate;
/** UUID of image's last modification. */
RTUUID uuidModify;
/** Only for secondary images - UUID of previous image. */
RTUUID uuidLinkage;
/** Only for secondary images - UUID of previous image's last modification. */
RTUUID uuidParentModify;
/** LCHS image geometry (new field in VDI1.2 version. */
VDIDISKGEOMETRY LCHSGeometry;
} VDIHEADER1PLUS, *PVDIHEADER1PLUS;
#pragma pack()
/**
* Header structure for all versions.
*/
typedef struct VDIHEADER
{
unsigned uVersion;
union
{
VDIHEADER0 v0;
VDIHEADER1 v1;
VDIHEADER1PLUS v1plus;
} u;
} VDIHEADER, *PVDIHEADER;
/**
* File alignment boundary for both the block array and data area. Should be
* at least the size of a physical sector on disk for performance reasons.
* With the growing market share of disks with 4K sectors this needs to be
* bumped, and maybe again later. */
#define VDI_DATA_ALIGN _4K
/** Block 'pointer'. */
typedef uint32_t VDIIMAGEBLOCKPOINTER;
/** Pointer to a block 'pointer'. */
typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
/**
* Block marked as free is not allocated in image file, read from this
* block may returns any random data.
*/
#define VDI_IMAGE_BLOCK_FREE ((VDIIMAGEBLOCKPOINTER)~0)
/**
* Block marked as zero is not allocated in image file, read from this
* block returns zeroes.
*/
#define VDI_IMAGE_BLOCK_ZERO ((VDIIMAGEBLOCKPOINTER)~1)
/**
* Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
* allocated in image file.
*/
#define VDI_IMAGE_BLOCK_UNALLOCATED (VDI_IMAGE_BLOCK_ZERO)
#define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp) (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
#define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
#define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
/** @name VDI image types
* @{ */
typedef enum VDIIMAGETYPE
{
/** Normal dynamically growing base image file. */
VDI_IMAGE_TYPE_NORMAL = 1,
/** Preallocated base image file of a fixed size. */
VDI_IMAGE_TYPE_FIXED,
/** Dynamically growing image file for undo/commit changes support. */
VDI_IMAGE_TYPE_UNDO,
/** Dynamically growing image file for differencing support. */
VDI_IMAGE_TYPE_DIFF,
/** First valid image type value. */
VDI_IMAGE_TYPE_FIRST = VDI_IMAGE_TYPE_NORMAL,
/** Last valid image type value. */
VDI_IMAGE_TYPE_LAST = VDI_IMAGE_TYPE_DIFF
} VDIIMAGETYPE;
/** Pointer to VDI image type. */
typedef VDIIMAGETYPE *PVDIIMAGETYPE;
/** @} */
/*******************************************************************************
* Internal Functions for header access *
*******************************************************************************/
DECLINLINE(VDIIMAGETYPE) getImageType(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return (VDIIMAGETYPE)ph->u.v0.u32Type;
case 1: return (VDIIMAGETYPE)ph->u.v1.u32Type;
}
AssertFailed();
return (VDIIMAGETYPE)0;
}
DECLINLINE(unsigned) getImageFlags(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0:
/* VDI image flag conversion to VD image flags. */
return ph->u.v0.fFlags << 8;
case 1:
/* VDI image flag conversion to VD image flags. */
return ph->u.v1.fFlags << 8;
}
AssertFailed();
return 0;
}
DECLINLINE(char *) getImageComment(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return &ph->u.v0.szComment[0];
case 1: return &ph->u.v1.szComment[0];
}
AssertFailed();
return NULL;
}
DECLINLINE(unsigned) getImageBlocksOffset(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
case 1: return ph->u.v1.offBlocks;
}
AssertFailed();
return 0;
}
DECLINLINE(uint32_t) getImageDataOffset(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
(ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
case 1: return ph->u.v1.offData;
}
AssertFailed();
return 0;
}
DECLINLINE(void) setImageDataOffset(PVDIHEADER ph, uint32_t offData)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return;
case 1: ph->u.v1.offData = offData; return;
}
AssertFailed();
}
DECLINLINE(PVDIDISKGEOMETRY) getImageLCHSGeometry(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return NULL;
case 1:
switch (GET_MINOR_HEADER_VERSION(ph))
{
case 1:
if (ph->u.v1.cbHeader < sizeof(ph->u.v1plus))
return NULL;
else
return &ph->u.v1plus.LCHSGeometry;
}
}
AssertFailed();
return NULL;
}
DECLINLINE(uint64_t) getImageDiskSize(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return ph->u.v0.cbDisk;
case 1: return ph->u.v1.cbDisk;
}
AssertFailed();
return 0;
}
DECLINLINE(void) setImageDiskSize(PVDIHEADER ph, uint64_t cbDisk)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: ph->u.v0.cbDisk = cbDisk; return;
case 1: ph->u.v1.cbDisk = cbDisk; return;
}
AssertFailed();
}
DECLINLINE(unsigned) getImageBlockSize(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return ph->u.v0.cbBlock;
case 1: return ph->u.v1.cbBlock;
}
AssertFailed();
return 0;
}
DECLINLINE(unsigned) getImageExtraBlockSize(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return 0;
case 1: return ph->u.v1.cbBlockExtra;
}
AssertFailed();
return 0;
}
DECLINLINE(unsigned) getImageBlocks(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return ph->u.v0.cBlocks;
case 1: return ph->u.v1.cBlocks;
}
AssertFailed();
return 0;
}
DECLINLINE(void) setImageBlocks(PVDIHEADER ph, unsigned cBlocks)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: ph->u.v0.cBlocks = cBlocks; return;
case 1: ph->u.v1.cBlocks = cBlocks; return;
}
AssertFailed();
}
DECLINLINE(unsigned) getImageBlocksAllocated(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return ph->u.v0.cBlocksAllocated;
case 1: return ph->u.v1.cBlocksAllocated;
}
AssertFailed();
return 0;
}
DECLINLINE(void) setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: ph->u.v0.cBlocksAllocated = cBlocks; return;
case 1: ph->u.v1.cBlocksAllocated = cBlocks; return;
}
AssertFailed();
}
DECLINLINE(PRTUUID) getImageCreationUUID(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return &ph->u.v0.uuidCreate;
case 1: return &ph->u.v1.uuidCreate;
}
AssertFailed();
return NULL;
}
DECLINLINE(PRTUUID) getImageModificationUUID(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return &ph->u.v0.uuidModify;
case 1: return &ph->u.v1.uuidModify;
}
AssertFailed();
return NULL;
}
DECLINLINE(PRTUUID) getImageParentUUID(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 0: return &ph->u.v0.uuidLinkage;
case 1: return &ph->u.v1.uuidLinkage;
}
AssertFailed();
return NULL;
}
DECLINLINE(PRTUUID) getImageParentModificationUUID(PVDIHEADER ph)
{
switch (GET_MAJOR_HEADER_VERSION(ph))
{
case 1: return &ph->u.v1.uuidParentModify;
}
AssertFailed();
return NULL;
}
/**
* Image structure
*/
typedef struct VDIIMAGEDESC
{
/** Opaque storage handle. */
PVDIOSTORAGE pStorage;
/** Image open flags, VD_OPEN_FLAGS_*. */
unsigned uOpenFlags;
/** Image pre-header. */
VDIPREHEADER PreHeader;
/** Image header. */
VDIHEADER Header;
/** Pointer to a block array. */
PVDIIMAGEBLOCKPOINTER paBlocks;
/** fFlags copy from image header, for speed optimization. */
unsigned uImageFlags;
/** Start offset of block array in image file, here for speed optimization. */
unsigned offStartBlocks;
/** Start offset of data in image file, here for speed optimization. */
unsigned offStartData;
/** Block mask for getting the offset into a block from a byte hdd offset. */
unsigned uBlockMask;
/** Block shift value for converting byte hdd offset into paBlock index. */
unsigned uShiftOffset2Index;
/** Offset of data from the beginning of block. */
unsigned offStartBlockData;
/** Total size of image block (including the extra data). */
unsigned cbTotalBlockData;
/** Container filename. (UTF-8) */
const char *pszFilename;
/** Physical geometry of this image (never actually stored). */
VDGEOMETRY PCHSGeometry;
/** Pointer to the per-disk VD interface list. */
PVDINTERFACE pVDIfsDisk;
/** Pointer to the per-image VD interface list. */
PVDINTERFACE pVDIfsImage;
/** Error interface. */
PVDINTERFACEERROR pIfError;
/** I/O interface. */
PVDINTERFACEIOINT pIfIo;
} VDIIMAGEDESC, *PVDIIMAGEDESC;
#endif