qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Add support for VDI images
@ 2007-07-02 18:34 Even Rouault
  2007-07-27  0:25 ` Alex Beregszaszi
  0 siblings, 1 reply; 2+ messages in thread
From: Even Rouault @ 2007-07-02 18:34 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 530 bytes --]

This patch adds support for reading, writing and creation (and thus 
conversion) of VDI images (VirtualBox image format). Creation of derived 
images above read-only base images also works, through a hack (the base image 
filename is stored as the comment of the derived image)
It's mainly a thin wrapper around VirtualBox OSE source code.
It may not compile on non-Linux hosts, and it definitely won't work on big 
endian hosts. So, there's still a bit of work to do for those interested by 
running it on those platforms.

Even

[-- Attachment #2: qemu_cvs20070630_vdi.patch --]
[-- Type: text/x-diff, Size: 105345 bytes --]

--- qemu.ori/Makefile	2007-06-30 22:52:01.000000000 +0200
+++ qemu_vdi/Makefile	2007-07-02 20:24:05.000000000 +0200
@@ -32,7 +32,7 @@
 
 recurse-all: $(patsubst %,subdir-%, $(TARGET_DIRS))
 
-qemu-img$(EXESUF): qemu-img.c cutils.c block.c block-raw.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c block-qcow2.c
+qemu-img$(EXESUF): qemu-img.c cutils.c block.c block-raw.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c block-vvfat.c block-qcow2.c block-vdi.c
 	$(CC) -DQEMU_TOOL $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ -lz $(LIBS)
 
 dyngen$(EXESUF): dyngen.c
--- qemu.ori/Makefile.target	2007-06-30 22:52:01.000000000 +0200
+++ qemu_vdi/Makefile.target	2007-07-02 20:24:05.000000000 +0200
@@ -367,7 +367,7 @@
 VL_OBJS+=cutils.o
 VL_OBJS+=host-utils.o
 VL_OBJS+=block.o block-raw.o
-VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o
+VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o block-vvfat.o block-qcow2.o block-vdi.o
 VL_OBJS+=irq.o
 ifdef CONFIG_WIN32
 VL_OBJS+=tap-win32.o
--- qemu.ori/block-vdi.c	1970-01-01 01:00:00.000000000 +0100
+++ qemu_vdi/block-vdi.c	2007-07-02 20:33:18.000000000 +0200
@@ -0,0 +1,3111 @@
+/* Copyright(C) 2007. E. Rouault. */
+/* VDI images support. Wrapper aroud VirtualBox OSE code */
+/* FIXME : endianness issues and support for non Linux hosts */
+
+/*
+ * Copyright (C) 2006-2007 innotek GmbH
+ *
+ * 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 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.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+#include <assert.h>
+#include "vl.h"
+#include "block_int.h"
+
+#define AssertFailed() assert(0)
+#define AssertReturn(cond, ret) do { if (!(cond)) return ret; } while(0)
+#define AssertMsg(x, y) do { if (!(x)) printf y;} while(0) 
+#define Assert(x) assert(x)
+#define AssertMsgFailed(x) printf x
+#define AssertMsgRC(rc, x) if (!RT_SUCCESS(rc)) printf x
+#define LogRel(x) printf x
+#define Log(x) printf x
+#define LogFlow(x) 
+
+#define RT_DONT_CONVERT_FILENAMES
+
+#define DECLINLINE(x) static x
+#define VBOXDDU_DECL(x) static x
+#define RTDECL(x) static x
+
+#define RT_SUCCESS(rc)   ( (int)(rc) >= VINF_SUCCESS )
+#define RT_FAILURE(rc)      ( (!RT_SUCCESS(rc)) )
+
+#define VBOX_SUCCESS(rc)    RT_SUCCESS(rc)
+#define VBOX_FAILURE(rc)    RT_FAILURE(rc)
+
+
+/** @def RT_ALIGN_T
+ * Align macro.
+ * @param   u           Value to align.
+ * @param   uAlignment  The alignment. Power of two!
+ * @param   type        Integer type to use while aligning.
+ * @remark  This macro is the prefered alignment macro, it doesn't have any of the pitfalls RT_ALIGN has.
+ */
+#define RT_ALIGN_T(u, uAlignment, type) ( ((type)(u) + ((uAlignment) - 1)) & ~(type)((uAlignment) - 1) )
+
+/** @def RT_ALIGN_32
+ * Align macro for a 32-bit value.
+ * @param   u32         Value to align.
+ * @param   uAlignment  The alignment. Power of two!
+ */
+#define RT_ALIGN_32(u32, uAlignment)            RT_ALIGN_T(u32, uAlignment, uint32_t)
+
+
+/**
+ * UUID data type.
+ */
+typedef union RTUUID
+{
+    /** 8-bit view. */
+    uint8_t     au8[16];
+    /** 16-bit view. */
+    uint16_t    au16[8];
+    /** 32-bit view. */
+    uint32_t    au32[4];
+    /** 64-bit view. */
+    uint64_t    au64[2];
+    /** The way the UUID is declared by the ext2 guys. */
+    struct
+    {
+        uint32_t    u32TimeLow;
+        uint16_t    u16TimeMid;
+        uint16_t    u16TimeHiAndVersion;
+        uint16_t    u16ClockSeq;
+        uint8_t     au8Node[6];
+    } Gen;
+    /** @deprecated */
+    unsigned char aUuid[16];
+} RTUUID;
+typedef RTUUID *PRTUUID;
+typedef const RTUUID *PCRTUUID;
+
+/** @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;
+/** @} */
+
+/**
+ * BIOS translation mode.
+ */
+typedef enum PDMBIOSTRANSLATION
+{
+    /** No translation. */
+    PDMBIOSTRANSLATION_NONE = 1,
+    /** LBA translation. */
+    PDMBIOSTRANSLATION_LBA,
+    /** Automatic select mode. */
+    PDMBIOSTRANSLATION_AUTO
+} PDMBIOSTRANSLATION;
+
+/** Pointer to BIOS translation mode. */
+typedef PDMBIOSTRANSLATION *PPDMBIOSTRANSLATION;
+
+/** Unsigned integer. */
+typedef uint32_t        RTUINT;
+/** File handle. */
+typedef RTUINT                                      RTFILE;
+/** Pointer to file handle. */
+typedef RTFILE                                     *PRTFILE;
+/** Nil file handle. */
+#define NIL_RTFILE                                  (~(RTFILE)0)
+
+typedef unsigned char bool;
+#define false 0
+#define true 1
+
+#define RTPATH_MAX   (4096 + 4)      /* (PATH_MAX + 1) on linux w/ some alignment */
+/*******************************************************************************
+*   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   "<<< innotek 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];
+    /** Image geometry. */
+    VDIDISKGEOMETRY Geometry;
+    /** 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.
+ * 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 begining of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offBlocks;
+    /** Offset of image data from the begining of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offData;
+    /** Image geometry. */
+    VDIDISKGEOMETRY Geometry;
+    /** BIOS HDD translation mode, see PDMBIOSTRANSLATION. */
+    uint32_t        u32Translation;
+    /** 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 structure for all versions.
+ */
+typedef struct VDIHEADER
+{
+    unsigned        uVersion;
+    union
+    {
+        VDIHEADER0    v0;
+        VDIHEADER1    v1;
+    } u;
+} VDIHEADER, *PVDIHEADER;
+
+/** Block 'pointer'. */
+typedef uint32_t    VDIIMAGEBLOCKPOINTER;
+/** Pointer to a block 'pointer'. */
+typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
+
+
+/**
+ * Default image block size, may be changed by setBlockSize/getBlockSize.
+ *
+ * Note: for speed reasons block size should be a power of 2 !
+ */
+#define _1M                     0x00100000
+#define VDI_IMAGE_DEFAULT_BLOCK_SIZE            _1M
+
+#define BIT(x) (1 << (x))
+/**
+ * fModified bit flags.
+ */
+#define VDI_IMAGE_MODIFIED_FLAG                 BIT(0)
+#define VDI_IMAGE_MODIFIED_FIRST                BIT(1)
+#define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE  BIT(2)
+
+/**
+ * Image structure
+ */
+typedef struct VDIIMAGEDESC
+{
+    /** Link to parent image descriptor, if any. */
+    struct VDIIMAGEDESC    *pPrev;
+    /** Link to child image descriptor, if any. */
+    struct VDIIMAGEDESC    *pNext;
+    /** File handle. */
+    RTFILE                  File;
+    /** True if the image is operating in readonly mode. */
+    bool                    fReadOnly;
+    /** Image open flags, VDI_OPEN_FLAGS_*. */
+    unsigned                fOpen;
+    /** 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                fFlags;
+    /** 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;
+    /** Block shift value for converting block index into offset in image. */
+    unsigned                uShiftIndex2Offset;
+    /** Offset of data from the beginning of block. */
+    unsigned                offStartBlockData;
+    /** Image is modified flags (VDI_IMAGE_MODIFIED*). */
+    unsigned                fModified;
+    /** Container filename. (UTF-8)
+     * @todo Make this variable length to save a bunch of bytes. (low prio) */
+    char                    szFilename[RTPATH_MAX];
+} VDIIMAGEDESC, *PVDIIMAGEDESC;
+
+/**
+ * Default work buffer size, may be changed by setBufferSize() method.
+ *
+ * For best speed performance it must be equal to image block size.
+ */
+#define VDIDISK_DEFAULT_BUFFER_SIZE   (VDI_IMAGE_DEFAULT_BLOCK_SIZE)
+
+/** VDIDISK Signature. */
+#define VDIDISK_SIGNATURE (0xbedafeda)
+
+/**
+ * VBox HDD Container main structure, private part.
+ */
+struct VDIDISK
+{
+    /** Structure signature (VDIDISK_SIGNATURE). */
+    uint32_t        u32Signature;
+
+    /** Number of opened images. */
+    unsigned        cImages;
+
+    /** Base image. */
+    PVDIIMAGEDESC   pBase;
+
+    /** Last opened image in the chain.
+     * The same as pBase if only one image is used or the last opened diff image. */
+    PVDIIMAGEDESC   pLast;
+
+    /** Default block size for newly created images. */
+    unsigned        cbBlock;
+
+    /** Working buffer size, allocated only while committing data,
+     * copying block from primary image to secondary and saving previously
+     * zero block. Buffer deallocated after operation complete.
+     * @remark  For best performance buffer size must be equal to image's
+     *          block size, however it may be decreased for memory saving.
+     */
+    unsigned        cbBuf;
+
+    /** Flag whether zero writes should be handled normally or optimized
+     * away if possible. */
+    bool            fHonorZeroWrites;
+
+    /** The media interface. */
+    //PDMIMEDIA       IMedia;
+    /** Pointer to the driver instance. */
+    //PPDMDRVINS      pDrvIns;
+};
+typedef struct VDIDISK VDIDISK;
+typedef VDIDISK *PVDIDISK;
+
+
+/**
+ * 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))
+
+/** Get major version from combined version. */
+#define VDI_GET_VERSION_MAJOR(uVer)    ((uVer) >> 16)
+/** Get minor version from combined version. */
+#define VDI_GET_VERSION_MINOR(uVer)    ((uVer) & 0xffff)
+
+
+/* WARNING: This implementation ASSUMES little endian. */
+
+
+
+/** @name Misc. Status Codes
+ * @{
+ */
+/** Success. */
+#define VINF_SUCCESS                        0
+
+/** General failure - DON'T USE THIS!!!
+ * (aka SUPDRV_ERR_GENERAL_FAILURE) */
+#define VERR_GENERAL_FAILURE                (-1)
+/** Invalid parameter.
+ * (aka SUPDRV_ERR_INVALID_PARAM) */
+#define VERR_INVALID_PARAMETER              (-2)
+/** Invalid magic or cookie.
+ * (aka SUPDRV_ERR_INVALID_MAGIC) */
+#define VERR_INVALID_MAGIC                  (-3)
+/** Invalid loader handle.
+ * (aka SUPDRV_ERR_INVALID_HANDLE) */
+#define VERR_INVALID_HANDLE                 (-4)
+/** Failed to lock the address range.
+ * (aka SUPDRV_ERR_INVALID_HANDLE) */
+#define VERR_LOCK_FAILED                    (-5)
+/** Invalid memory pointer.
+ * (aka SUPDRV_ERR_INVALID_POINTER) */
+#define VERR_INVALID_POINTER                (-6)
+/** Failed to patch the IDT.
+ * (aka SUPDRV_ERR_IDT_FAILED) */
+#define VERR_IDT_FAILED                     (-7)
+/** Memory allocation failed.
+ * (aka SUPDRV_ERR_NO_MEMORY) */
+#define VERR_NO_MEMORY                      (-8)
+/** Already loaded.
+ * (aka SUPDRV_ERR_ALREADY_LOADED) */
+#define VERR_ALREADY_LOADED                 (-9)
+/** Permission denied.
+ * (aka SUPDRV_ERR_PERMISSION_DENIED) */
+#define VERR_PERMISSION_DENIED              (-10)
+/** Version mismatch.
+ * (aka SUPDRV_ERR_VERSION_MISMATCH) */
+#define VERR_VERSION_MISMATCH               (-11)
+/** The request function is not implemented. */
+#define VERR_NOT_IMPLEMENTED                (-12)
+
+/** Not supported. */
+#define VERR_NOT_SUPPORTED                  (-37)
+
+/** @name VBox HDD Container (VDI) Status Codes
+ * @{
+ */
+/** Invalid image file header. */
+#define VERR_VDI_INVALID_HEADER                     (-3200)
+/** Invalid image file header: invalid signature. */
+#define VERR_VDI_INVALID_SIGNATURE                  (-3201)
+/** Invalid image file header: invalid version. */
+#define VERR_VDI_UNSUPPORTED_VERSION                (-3202)
+/** Invalid image type. */
+#define VERR_VDI_INVALID_TYPE                       (-3203)
+/** Invalid image flags. */
+#define VERR_VDI_INVALID_FLAGS                      (-3204)
+/** Operation can't be done in current HDD container state. */
+#define VERR_VDI_INVALID_STATE                      (-3205)
+/** Differencing image can't be used with current base image. */
+#define VERR_VDI_WRONG_DIFF_IMAGE                   (-3206)
+/** Two or more images of one HDD has different versions. */
+#define VERR_VDI_IMAGES_VERSION_MISMATCH            (-3207)
+/** Differencing and parent images can't be used together due to UUID. */
+#define VERR_VDI_IMAGES_UUID_MISMATCH               (-3208)
+/** No differencing images to commit. */
+#define VERR_VDI_NO_DIFF_IMAGES                     (-3209)
+/** Virtual HDD is not opened. */
+#define VERR_VDI_NOT_OPENED                         (-3210)
+/** Requested image is not opened. */
+#define VERR_VDI_IMAGE_NOT_FOUND                    (-3211)
+/** Image is read-only. */
+#define VERR_VDI_IMAGE_READ_ONLY                    (-3212)
+/** Comment string is too long. */
+#define VERR_VDI_COMMENT_TOO_LONG                   (-3213)
+/** Geometry hasn't been set. */
+#define VERR_VDI_GEOMETRY_NOT_SET                   (-3214)
+/** No data for this block in image. */
+#define VINF_VDI_BLOCK_FREE                         3215
+/** Configuration value not found. */
+#define VERR_VDI_VALUE_NOT_FOUND                    (-3216)
+/** @} */
+
+/** @name Common File/Disk/Pipe/etc Status Codes
+ * @{
+ */
+/** Unresolved (unknown) file i/o error. */
+#define VERR_FILE_IO_ERROR                  (-100)
+/** File/Device open failed. */
+#define VERR_OPEN_FAILED                    (-101)
+/** File not found. */
+#define VERR_FILE_NOT_FOUND                 (-102)
+/** Path not found. */
+#define VERR_PATH_NOT_FOUND                 (-103)
+/** Invalid (malformed) file/path name. */
+#define VERR_INVALID_NAME                   (-104)
+/** File/Device already exists. */
+#define VERR_ALREADY_EXISTS                 (-105)
+/** Too many open files. */
+#define VERR_TOO_MANY_OPEN_FILES            (-106)
+/** Seek error. */
+#define VERR_SEEK                           (-107)
+/** Seek below file start. */
+#define VERR_NEGATIVE_SEEK                  (-108)
+/** Trying to seek on device. */
+#define VERR_SEEK_ON_DEVICE                 (-109)
+/** Reached the end of the file. */
+#define VERR_EOF                            (-110)
+/** Reached the end of the file. */
+#define VINF_EOF                            110
+/** Generic file read error. */
+#define VERR_READ_ERROR                     (-111)
+/** Generic file write error. */
+#define VERR_WRITE_ERROR                    (-112)
+/** Write protect error. */
+#define VERR_WRITE_PROTECT                  (-113)
+/** Sharing violetion, file is being used by another process. */
+#define VERR_SHARING_VIOLATION              (-114)
+/** Unable to lock a region of a file. */
+#define VERR_FILE_LOCK_FAILED               (-115)
+/** File access error, another process has locked a portion of the file. */
+#define VERR_FILE_LOCK_VIOLATION            (-116)
+/** File or directory can't be created. */
+#define VERR_CANT_CREATE                    (-117)
+/** Directory can't be deleted. */
+#define VERR_CANT_DELETE_DIRECTORY          (-118)
+/** Can't move file to another disk. */
+#define VERR_NOT_SAME_DEVICE                (-119)
+/** The filename or extension is too long. */
+#define VERR_FILENAME_TOO_LONG              (-120)
+/** Media not present in drive. */
+#define VERR_MEDIA_NOT_PRESENT              (-121)
+/** The type of media was not recognized. Not formatted? */
+#define VERR_MEDIA_NOT_RECOGNIZED           (-122)
+/** Can't unlock - region was not locked. */
+#define VERR_FILE_NOT_LOCKED                (-123)
+/** Unrecoverable error: lock was lost. */
+#define VERR_FILE_LOCK_LOST                 (-124)
+/** Can't delete directory with files. */
+#define VERR_DIR_NOT_EMPTY                  (-125)
+/** A directory operation was attempted on a non-directory object. */
+#define VERR_NOT_A_DIRECTORY                (-126)
+/** A non-directory operation was attempted on a directory object. */
+#define VERR_IS_A_DIRECTORY                 (-127)
+/** Tried to grow a file beyond the limit imposed by the process or the filesystem. */
+#define VERR_FILE_TOO_BIG                   (-128)
+/** @} */
+
+
+/** Current VDI image major version. */
+#define VDI_IMAGE_VERSION_MAJOR     (0x0001)
+/** Current VDI image minor version. */
+#define VDI_IMAGE_VERSION_MINOR     (0x0001)
+/** Current VDI image version. */
+#define VDI_IMAGE_VERSION           ((VDI_IMAGE_VERSION_MAJOR << 16) | VDI_IMAGE_VERSION_MINOR)
+
+/** Get VDI major version from combined version. */
+#define VDI_GET_VERSION_MAJOR(uVer)    ((uVer) >> 16)
+/** Get VDI minor version from combined version. */
+#define VDI_GET_VERSION_MINOR(uVer)    ((uVer) & 0xffff)
+
+
+/** @name VBox HDD container image flags
+ * @{
+ */
+/** No flags. */
+#define VD_IMAGE_FLAGS_NONE                 (0)
+/** VMDK: Split image into 2GB extents. */
+#define VD_VMDK_IMAGE_FLAGS_SPLIT_2G        (0x0001)
+/** VMDK: Raw disk image (giving access to a number of host partitions). */
+#define VD_VMDK_IMAGE_FLAGS_RAWDISK         (0x0002)
+/** VDI: Fill new blocks with zeroes while expanding image file. Only valid
+ * for newly created images, never set for opened existing images. */
+#define VD_VDI_IMAGE_FLAGS_ZERO_EXPAND      (0x0100)
+
+/** Mask of valid image flags for VMDK. */
+#define VD_VMDK_IMAGE_FLAGS_MASK            (VD_IMAGE_FLAGS_NONE | VD_VMDK_IMAGE_FLAGS_SPLIT_2G | VD_VMDK_IMAGE_FLAGS_RAWDISK)
+
+/** Mask of valid image flags for VDI. */
+#define VD_VDI_IMAGE_FLAGS_MASK             (VD_IMAGE_FLAGS_NONE | VD_VDI_IMAGE_FLAGS_ZERO_EXPAND)
+
+/** Default image flags. */
+#define VD_IMAGE_FLAGS_DEFAULT              (VD_IMAGE_FLAGS_NONE)
+/** @} */
+
+/** @name VDI image flags
+ * @{  */
+/** No flags. */
+#define VDI_IMAGE_FLAGS_NONE          (0x00)
+/** Fill new blocks with zeroes while expanding image file. */
+#define VDI_IMAGE_FLAGS_ZERO_EXPAND   (0x01)
+
+/** Mask of valid image flags. */
+#define VDI_IMAGE_FLAGS_MASK          (VDI_IMAGE_FLAGS_NONE | VDI_IMAGE_FLAGS_ZERO_EXPAND)
+
+/** Default image flags. */
+#define VDI_IMAGE_FLAGS_DEFAULT       (VDI_IMAGE_FLAGS_NONE)
+/** @} */
+
+/** @name VDI image open mode flags
+ * @{
+ */
+/** Try to open image in read/write exclusive access mode if possible, or in read-only elsewhere. */
+#define VDI_OPEN_FLAGS_NORMAL     (0)
+/** Open image in read-only mode with sharing access with others. */
+#define VDI_OPEN_FLAGS_READONLY   (1)
+/** Mask of valid flags. */
+#define VDI_OPEN_FLAGS_MASK (VDI_OPEN_FLAGS_NORMAL | VDI_OPEN_FLAGS_READONLY)
+/** @}*/
+
+/** @name Open flags
+ * @{ */
+/** Open the file with read access. */
+#define RTFILE_O_READ               0x00000001
+/** Open the file with write access. */
+#define RTFILE_O_WRITE              0x00000002
+/** Open the file with read & write access. */
+#define RTFILE_O_READWRITE          0x00000003
+/** The file access mask.
+ * @remark The value 0 is invalid. */
+#define RTFILE_O_ACCESS_MASK        0x00000003
+
+
+
+/** Sharing mode: deny none (the default mode). */
+#define RTFILE_O_DENY_NONE          0x00000000
+/** Sharing mode: deny read. */
+#define RTFILE_O_DENY_READ          0x00000010
+/** Sharing mode: deny write. */
+#define RTFILE_O_DENY_WRITE         0x00000020
+/** Sharing mode: deny read and write. */
+#define RTFILE_O_DENY_READWRITE     0x00000030
+/** Sharing mode: deny all. */
+#define RTFILE_O_DENY_ALL           RTFILE_O_DENY_READWRITE
+/** Sharing mode: do NOT deny delete (NT).
+ * @remark  This might not be implemented on all platforms,
+ *          and will be defaulted & ignored on those.
+ */
+#define RTFILE_O_DENY_NOT_DELETE    0x00000040
+/** Sharing mode mask. */
+#define RTFILE_O_DENY_MASK          0x00000070
+
+/** Action: Open an existing file (the default action). */
+#define RTFILE_O_OPEN               0x00000000
+/** Action: Create a new file or open an existing one. */
+#define RTFILE_O_OPEN_CREATE        0x00000100
+/** Action: Create a new a file. */
+#define RTFILE_O_CREATE             0x00000200
+/** Action: Create a new file or replace an existing one. */
+#define RTFILE_O_CREATE_REPLACE     0x00000300
+/** Action mask. */
+#define RTFILE_O_ACTION_MASK        0x00000300
+
+/** Truncate the file.
+ * @remark  This will not truncate files opened for read-only.
+ * @remark  The trunction doesn't have to be atomically, so anyone
+ *          else opening the file may be racing us. The caller is
+ *          responsible for not causing this race. */
+#define RTFILE_O_TRUNCATE           0x00001000
+/** Make the handle inheritable on RTProcessCreate(/exec). */
+#define RTFILE_O_INHERIT            0x00002000
+/** Open file in non-blocking mode - non-portable.
+ * @remark  This flag may not be supported on all platforms, in which
+ *          case it's considered an invalid parameter.
+ */
+#define RTFILE_O_NON_BLOCK          0x00004000
+/** Write through directly to disk. Workaround to avoid iSCSI
+ * initiator deadlocks on Windows hosts.
+ * @remark  This might not be implemented on all platforms,
+ *          and will be ignored on those.
+ */
+#define RTFILE_O_WRITE_THROUGH      0x00008000
+
+/** Mask of all valid flags.
+ * @remark  This doesn't validate the access mode properly.
+ */
+#define RTFILE_O_VALID_MASK         0x0000F333
+
+
+
+/** Default file permissions for newly created files. */
+#if defined(S_IRUSR) && defined(S_IWUSR)
+# define RT_FILE_PERMISSION  (S_IRUSR | S_IWUSR)
+#else
+# define RT_FILE_PERMISSION  (00600)
+#endif
+
+
+/** @name Lock flags (bit masks).
+ * @{ */
+/** Read access, can be shared with others. */
+#define RTFILE_LOCK_READ            0x00
+/** Write access, one at a time. */
+#define RTFILE_LOCK_WRITE           0x01
+/** Don't wait for other locks to be released. */
+#define RTFILE_LOCK_IMMEDIATELY     0x00
+/** Wait till conflicting locks have been released. */
+#define RTFILE_LOCK_WAIT            0x02
+/** Valid flags mask */
+#define RTFILE_LOCK_MASK            0x03
+/** @} */
+
+/** @name Seek flags.
+ * @{ */
+/** Seek from the start of the file. */
+#define RTFILE_SEEK_BEGIN     0x00
+/** Seek from the current file position. */
+#define RTFILE_SEEK_CURRENT   0x01
+/** Seek from the end of the file. */
+#define RTFILE_SEEK_END       0x02
+/** @internal */
+#define RTFILE_SEEK_FIRST     RTFILE_SEEK_BEGIN
+/** @internal */
+#define RTFILE_SEEK_LAST      RTFILE_SEEK_END
+/** @} */
+
+
+static void* RTMemAllocZ(int size)
+{
+  void* ptr = malloc(size);
+  memset(ptr, 0, size);
+  return ptr;
+}
+#define RTMemTmpAllocZ RTMemAllocZ
+#define RTMemTmpAlloc malloc
+#define RTMemTmpFree free
+
+#define RTMemAlloc malloc
+#define RTMemFree free
+#define VALID_PTR(x) ((x) !=  NULL)
+
+
+
+static void RTRandBytes(void* ptr, int len)
+{
+  char* c = (char*)ptr;
+  int i;
+  for(i=0;i<len;i++)
+    c[i] = i;
+}
+
+/**
+ * Generates a new UUID value.
+ *
+ * @returns iprt status code.
+ * @param   pUuid           Where to store generated uuid.
+ */
+RTDECL(int)  RTUuidCreate(PRTUUID pUuid)
+{
+    /* validate input. */
+    AssertReturn(pUuid, VERR_INVALID_PARAMETER);
+
+    RTRandBytes(pUuid, sizeof(*pUuid));
+    pUuid->Gen.u16ClockSeq = (pUuid->Gen.u16ClockSeq & 0x3fff) | 0x8000;
+    pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Makes a null UUID value.
+ *
+ * @returns iprt status code.
+ * @param   pUuid           Where to store generated null uuid.
+ */
+RTDECL(int)  RTUuidClear(PRTUUID pUuid)
+{
+    AssertReturn(pUuid, VERR_INVALID_PARAMETER);
+    pUuid->au64[0] = 0;
+    pUuid->au64[1] = 0;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Checks if UUID is null.
+ *
+ * @returns true if UUID is null.
+ * @param   pUuid           uuid to check.
+ */
+RTDECL(int)  RTUuidIsNull(PCRTUUID pUuid)
+{
+    AssertReturn(pUuid, VERR_INVALID_PARAMETER);
+    return !pUuid->au64[0]
+        && !pUuid->au64[1];
+}
+
+
+/**
+ * Compares two UUID values.
+ *
+ * @returns 0 if eq, < 0 or > 0.
+ * @param   pUuid1          First value to compare.
+ * @param   pUuid2          Second value to compare.
+ */
+int  RTUuidCompare(PCRTUUID pUuid1, PCRTUUID pUuid2)
+{
+    /*
+     * Special cases.
+     */
+    if (pUuid1 == pUuid2)
+        return 0;
+    if (!pUuid1)
+        return RTUuidIsNull(pUuid2) ? 0 : -1;
+    if (!pUuid2)
+        return RTUuidIsNull(pUuid1) ? 0 : 1;
+
+    /*
+     * Standard cases.
+     */
+    if (pUuid1->Gen.u32TimeLow != pUuid2->Gen.u32TimeLow)
+        return pUuid1->Gen.u32TimeLow < pUuid2->Gen.u32TimeLow ? -1 : 1;
+    if (pUuid1->Gen.u16TimeMid != pUuid2->Gen.u16TimeMid)
+        return pUuid1->Gen.u16TimeMid < pUuid2->Gen.u16TimeMid ? -1 : 1;
+    if (pUuid1->Gen.u16TimeHiAndVersion != pUuid2->Gen.u16TimeHiAndVersion)
+        return pUuid1->Gen.u16TimeHiAndVersion < pUuid2->Gen.u16TimeHiAndVersion ? -1 : 1;
+    if (pUuid1->Gen.u16ClockSeq != pUuid2->Gen.u16ClockSeq)
+        return pUuid1->Gen.u16ClockSeq < pUuid2->Gen.u16ClockSeq ? -1 : 1;
+    if (pUuid1->Gen.au8Node[0] != pUuid2->Gen.au8Node[0])
+        return pUuid1->Gen.au8Node[0] < pUuid2->Gen.au8Node[0] ? -1 : 1;
+    if (pUuid1->Gen.au8Node[1] != pUuid2->Gen.au8Node[1])
+        return pUuid1->Gen.au8Node[1] < pUuid2->Gen.au8Node[1] ? -1 : 1;
+    if (pUuid1->Gen.au8Node[2] != pUuid2->Gen.au8Node[2])
+        return pUuid1->Gen.au8Node[2] < pUuid2->Gen.au8Node[2] ? -1 : 1;
+    if (pUuid1->Gen.au8Node[3] != pUuid2->Gen.au8Node[3])
+        return pUuid1->Gen.au8Node[3] < pUuid2->Gen.au8Node[3] ? -1 : 1;
+    if (pUuid1->Gen.au8Node[4] != pUuid2->Gen.au8Node[4])
+        return pUuid1->Gen.au8Node[4] < pUuid2->Gen.au8Node[4] ? -1 : 1;
+    if (pUuid1->Gen.au8Node[5] != pUuid2->Gen.au8Node[5])
+        return pUuid1->Gen.au8Node[5] < pUuid2->Gen.au8Node[5] ? -1 : 1;
+    return 0;
+}
+
+/*******************************************************************************
+*   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: return ph->u.v0.fFlags;
+        case 1: return ph->u.v1.fFlags;
+    }
+    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(unsigned) 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(PVDIDISKGEOMETRY) getImageGeometry(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return &ph->u.v0.Geometry;
+        case 1: return &ph->u.v1.Geometry;
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(PDMBIOSTRANSLATION) getImageTranslation(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return PDMBIOSTRANSLATION_AUTO;
+        case 1: return (PDMBIOSTRANSLATION)ph->u.v1.u32Translation;
+    }
+    AssertFailed();
+    return PDMBIOSTRANSLATION_NONE;
+}
+
+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(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(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;
+}
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+VBOXDDU_DECL(void) vdiInitVDIDisk(PVDIDISK pDisk);
+VBOXDDU_DECL(void) vdiFlushImage(PVDIIMAGEDESC pImage);
+
+typedef int (*PFNVMPROGRESS)(void*, unsigned int, void*);
+
+static unsigned getPowerOfTwo(unsigned uNumber);
+static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
+static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
+static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
+                          const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
+                          uint32_t cbBlockExtra);
+static int  vdiValidateHeader(PVDIHEADER pHeader);
+static int  vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
+                          uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
+                          PFNVMPROGRESS pfnProgress, void *pvUser);
+static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
+static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
+static int  vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename, unsigned fOpen,
+                        PVDIIMAGEDESC pParent);
+static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
+static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
+static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
+static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
+static void vdiCloseImage(PVDIIMAGEDESC pImage);
+static int  vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
+                           unsigned cbToRead, void *pvBuf);
+static int  vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
+static int  vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock,
+                            unsigned offWrite, unsigned cbToWrite, const void *pvBuf);
+static int  vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
+
+static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
+static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
+
+
+
+/**
+ * internal: return power of 2 or 0 if num error.
+ */
+static unsigned getPowerOfTwo(unsigned uNumber)
+{
+    if (uNumber == 0)
+        return 0;
+    unsigned uPower2 = 0;
+    while ((uNumber & 1) == 0)
+    {
+        uNumber >>= 1;
+        uPower2++;
+    }
+    return uNumber == 1 ? uPower2 : 0;
+}
+
+/**
+ * internal: init HDD preheader.
+ */
+static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
+{
+    pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
+    pPreHdr->u32Version = VDI_IMAGE_VERSION;
+    memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
+    strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
+}
+
+/**
+ * internal: check HDD preheader.
+ */
+static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
+{
+    if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
+        return VERR_VDI_INVALID_SIGNATURE;
+
+    if (    pPreHdr->u32Version != VDI_IMAGE_VERSION
+        &&  pPreHdr->u32Version != 0x00000002)    /* old version. */
+        return VERR_VDI_UNSUPPORTED_VERSION;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * internal: init HDD header. Always use latest header version.
+ * @param   pHeader     Assumes it was initially initialized to all zeros.
+ */
+static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
+                          const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
+                          uint32_t cbBlockExtra)
+{
+    pHeader->uVersion = VDI_IMAGE_VERSION;
+    pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
+    pHeader->u.v1.u32Type = (uint32_t)enmType;
+    pHeader->u.v1.fFlags = fFlags;
+#ifdef VBOX_STRICT
+    char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
+    Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
+#endif
+    pHeader->u.v1.szComment[0] = '\0';
+    if (pszComment)
+    {
+        AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
+                  ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
+        strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
+    }
+
+    /* Mark the geometry not-calculated. */
+    pHeader->u.v1.Geometry.cCylinders = 0;
+    pHeader->u.v1.Geometry.cHeads = 0;
+    pHeader->u.v1.Geometry.cSectors = 0;
+    pHeader->u.v1.Geometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+    pHeader->u.v1.u32Translation = PDMBIOSTRANSLATION_AUTO;
+
+    pHeader->u.v1.cbDisk = cbDisk;
+    pHeader->u.v1.cbBlock = cbBlock;
+    pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
+    if (cbDisk % cbBlock)
+        pHeader->u.v1.cBlocks++;
+    pHeader->u.v1.cbBlockExtra = cbBlockExtra;
+    pHeader->u.v1.cBlocksAllocated = 0;
+
+    /* Init offsets. */
+    pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
+    pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
+
+    /* Init uuids. */
+    RTUuidCreate(&pHeader->u.v1.uuidCreate);
+    RTUuidClear(&pHeader->u.v1.uuidModify);
+    RTUuidClear(&pHeader->u.v1.uuidLinkage);
+    RTUuidClear(&pHeader->u.v1.uuidParentModify);
+}
+
+/**
+ * internal: check HDD header.
+ */
+static int vdiValidateHeader(PVDIHEADER pHeader)
+{
+    /* Check verion-dependend header parameters. */
+    switch (GET_MAJOR_HEADER_VERSION(pHeader))
+    {
+        case 0:
+        {
+            /* Old header version. */
+            break;
+        }
+        case 1:
+        {
+            /* Current header version. */
+
+            if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
+            {
+                LogRel(("VDI: v1 header size wrong (%d < %d)\n",
+                       pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
+                return VERR_VDI_INVALID_HEADER;
+            }
+
+            if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
+            {
+                LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
+                       getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
+                return VERR_VDI_INVALID_HEADER;
+            }
+
+            if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
+            {
+                LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
+                       getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
+                return VERR_VDI_INVALID_HEADER;
+            }
+
+            if (    getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
+                ||  getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
+            {
+                if (RTUuidIsNull(getImageParentUUID(pHeader)))
+                {
+                    LogRel(("VDI: v1 uuid of parent is 0)\n"));
+                    return VERR_VDI_INVALID_HEADER;
+                }
+                if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
+                {
+                    LogRel(("VDI: v1 uuid of parent modification is 0\n"));
+                    return VERR_VDI_INVALID_HEADER;
+                }
+            }
+
+            break;
+        }
+        default:
+            /* Unsupported. */
+            return VERR_VDI_UNSUPPORTED_VERSION;
+    }
+
+    /* Check common header parameters. */
+
+    bool fFailed = false;
+
+    if (    getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
+        ||  getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
+    {
+        LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
+        fFailed = true;
+    }
+
+    if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
+    {
+        LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
+        fFailed = true;
+    }
+
+    if ((getImageGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
+    {
+        LogRel(("VDI: wrong sector size (%d != %d)\n",
+               (getImageGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
+        fFailed = true;
+    }
+
+    if (    getImageDiskSize(pHeader) == 0
+        ||  getImageBlockSize(pHeader) == 0
+        ||  getImageBlocks(pHeader) == 0
+        ||  getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
+    {
+        LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
+              getImageDiskSize(pHeader), getImageBlockSize(pHeader),
+              getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
+        fFailed = true;
+    }
+
+    if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
+    {
+        LogRel(("VDI: too many blocks allocated (%d > %d)\n"
+                "     blocksize=%d disksize=%lld\n",
+              getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
+              getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
+        fFailed = true;
+    }
+
+    if (    getImageExtraBlockSize(pHeader) != 0
+        &&  getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
+    {
+        LogRel(("VDI: wrong extra size (%d, %d)\n",
+               getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
+        fFailed = true;
+    }
+
+    if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
+    {
+        LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
+               getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
+        fFailed = true;
+    }
+
+    if (RTUuidIsNull(getImageCreationUUID(pHeader)))
+    {
+        LogRel(("VDI: uuid of creator is 0\n"));
+        fFailed = true;
+    }
+
+    if (RTUuidIsNull(getImageModificationUUID(pHeader)))
+    {
+        LogRel(("VDI: uuid of modificator is 0\n"));
+        fFailed = true;
+    }
+
+    return fFailed ? VERR_VDI_INVALID_HEADER : VINF_SUCCESS;
+}
+
+/**
+ * internal: init VDIIMAGEDESC structure.
+ */
+static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
+{
+    pImage->pPrev = NULL;
+    pImage->pNext = NULL;
+    pImage->File = NIL_RTFILE;
+    pImage->paBlocks = NULL;
+}
+
+/**
+ * internal: setup VDIIMAGEDESC structure by image header.
+ */
+static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
+{
+    pImage->fFlags             = getImageFlags(&pImage->Header);
+    pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
+    pImage->offStartData       = getImageDataOffset(&pImage->Header);
+    pImage->uBlockMask         = getImageBlockSize(&pImage->Header) - 1;
+    pImage->uShiftIndex2Offset =
+    pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
+    pImage->offStartBlockData  = getImageExtraBlockSize(&pImage->Header);
+    if (pImage->offStartBlockData != 0)
+        pImage->uShiftIndex2Offset += getPowerOfTwo(pImage->offStartBlockData);
+}
+
+
+static int  RTFileOpen(PRTFILE pFile, const char *pszFilename, unsigned fOpen)
+{
+    /*
+     * Validate input.
+     */
+    if (!VALID_PTR(pFile))
+    {
+        AssertMsgFailed(("Invalid pFile %p\n", pFile));
+        return VERR_INVALID_PARAMETER;
+    }
+    *pFile = NIL_RTFILE;
+    if (!VALID_PTR(pszFilename))
+    {
+        AssertMsgFailed(("Invalid pszFilename %p\n", pszFilename));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Calculate open mode flags.
+     */
+    int fOpenMode = 0;
+#ifdef O_BINARY
+    fOpenMode |= O_BINARY;              /* (pc) */
+#endif
+#ifdef O_LARGEFILE
+    fOpenMode |= O_LARGEFILE;           /* (linux) */
+#endif
+#ifdef O_NOINHERIT
+    if (!(fOpen & RTFILE_O_INHERIT))
+        fOpenMode |= O_NOINHERIT;
+#endif
+#ifndef O_NONBLOCK
+    if (fOpen & RTFILE_O_NON_BLOCK)
+        fOpenMode |= O_NONBLOCK
+#endif
+#ifdef O_SYNC
+    if (fOpen & RTFILE_O_WRITE_THROUGH)
+        fOpenMode |= O_SYNC;
+#endif
+
+    /* create/truncate file */
+    switch (fOpen & RTFILE_O_ACTION_MASK)
+    {
+        case RTFILE_O_OPEN:             break;
+        case RTFILE_O_OPEN_CREATE:      fOpenMode |= O_CREAT; break;
+        case RTFILE_O_CREATE:           fOpenMode |= O_CREAT | O_EXCL; break;
+        case RTFILE_O_CREATE_REPLACE:   fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
+    }
+    if (fOpen & RTFILE_O_TRUNCATE)
+        fOpenMode |= O_TRUNC;
+
+    switch (fOpen & RTFILE_O_ACCESS_MASK)
+    {
+        case RTFILE_O_READ:             fOpenMode |= O_RDONLY; break;
+        case RTFILE_O_WRITE:            fOpenMode |= O_WRONLY; break;
+        case RTFILE_O_READWRITE:        fOpenMode |= O_RDWR; break;
+        default:
+            AssertMsgFailed(("RTFileOpen received an invalid RW value, fOpen=%#x\n", fOpen));
+            return VERR_INVALID_PARAMETER;
+    }
+
+    /** @todo sharing! */
+
+    /*
+     * Open/create the file.
+     */
+#ifdef RT_DONT_CONVERT_FILENAMES
+    int fh = open(pszFilename, fOpenMode, RT_FILE_PERMISSION);
+    int iErr = errno;
+    if (fh < 0)
+    {
+      //perror("");
+      //fprintf(stderr, "errno = %d\n", iErr);
+    }
+#else
+    char *pszNativeFilename;
+    rc = rtPathToNative(&pszNativeFilename, pszFilename);
+    if (RT_FAILURE(rc))
+        return (rc);
+
+    int fh = open(pszNativeFilename, fOpenMode, RT_FILE_PERMISSION);
+    int iErr = errno;
+    rtPathFreeNative(pszNativeFilename);
+#endif
+    if (fh >= 0)
+    {
+        /*
+         * Mark the file handle close on exec, unless inherit is specified.
+         */
+        if (    !(fOpen & RTFILE_O_INHERIT)
+#ifdef O_NOINHERIT
+            ||  (fOpenMode & O_NOINHERIT) /* careful since it could be a dummy. */
+#endif
+            ||  fcntl(fh, F_SETFD, FD_CLOEXEC) >= 0)
+        {
+            *pFile = (RTFILE)fh;
+            Assert((int)*pFile == fh);
+            LogFlow(("RTFileOpen(%p:{%RTfile}, %p:{%s}, %#x): returns %Rrc\n",
+                     pFile, *pFile, pszFilename, pszFilename, fOpen, rc));
+            return VINF_SUCCESS;
+        }
+        iErr = errno;
+        close(fh);
+    }
+    return VERR_GENERAL_FAILURE;
+}
+
+int  RTFileSetSize(RTFILE File, uint64_t cbSize)
+{
+    /*
+     * Validate offset.
+     */
+    if (    sizeof(off_t) < sizeof(cbSize)
+        &&  (cbSize >> 32) != 0)
+    {
+        AssertMsgFailed(("64-bit filesize not supported! cbSize=%lld\n", cbSize));
+        return VERR_NOT_SUPPORTED;
+    }
+
+#if defined(_MSC_VER) || (defined(__OS2__) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006))
+    if (chsize((int)File, (off_t)cbSize) == 0)
+#else
+    /* This relies on a non-standard feature of FreeBSD, Linux, and OS/2
+     * LIBC v0.6 and higher. (SuS doesn't define ftruncate() and size bigger
+     * than the file.)
+     */
+    if (ftruncate((int)File, (off_t)cbSize) == 0)
+#endif
+        return VINF_SUCCESS;
+    return VERR_GENERAL_FAILURE;
+}
+
+int  RTFileWrite(RTFILE File, const void *pvBuf, unsigned cbToWrite, unsigned *pcbWritten)
+{
+    if (cbToWrite <= 0)
+        return VINF_SUCCESS;
+
+    /*
+     * Attempt write.
+     */
+    ssize_t cbWritten = write((int)File, pvBuf, cbToWrite);
+    if (cbWritten >= 0)
+    {
+        if (pcbWritten)
+            /* caller can handle partial write. */
+            *pcbWritten = cbWritten;
+        else
+        {
+            /* Caller expects all to be write. */
+            while ((ssize_t)cbToWrite > cbWritten)
+            {
+                ssize_t cbWrittenPart = write((int)File, (const char *)pvBuf + cbWritten, cbToWrite - cbWritten);
+                if (cbWrittenPart <= 0)
+                    return VERR_GENERAL_FAILURE;
+                cbWritten += cbWrittenPart;
+            }
+        }
+        return VINF_SUCCESS;
+    }
+    return VERR_GENERAL_FAILURE;
+}
+
+int  RTFileSeek(RTFILE File, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+    static const unsigned aSeekRecode[] =
+    {
+        SEEK_SET,
+        SEEK_CUR,
+        SEEK_END,
+    };
+
+    /*
+     * Validate input.
+     */
+    if (uMethod > RTFILE_SEEK_END)
+    {
+        AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /* check that within off_t range. */
+    if (    sizeof(off_t) < sizeof(offSeek)
+        && (    (offSeek > 0 && (unsigned)(offSeek >> 32) != 0)
+            ||  (offSeek < 0 && (unsigned)(-offSeek >> 32) != 0)))
+    {
+        AssertMsgFailed(("64-bit search not supported\n"));
+        return VERR_NOT_SUPPORTED;
+    }
+
+    off_t offCurrent = lseek((int)File, (off_t)offSeek, aSeekRecode[uMethod]);
+    if (offCurrent != ~0)
+    {
+        if (poffActual)
+            *poffActual = (uint64_t)offCurrent;
+        return VINF_SUCCESS;
+    }
+    return VERR_GENERAL_FAILURE;
+}
+
+#define RT_MIN(a,b) (((a)<(b)) ? (a) : (b))
+
+int  RTFileClose(RTFILE File)
+{
+    if (close((int)File) == 0)
+        return VINF_SUCCESS;
+    return VERR_GENERAL_FAILURE;
+}
+
+
+int  RTFileDelete(const char *pszFilename)
+{
+    /*
+    char *pszNativeFilename;
+    int rc = rtPathToNative(&pszNativeFilename, pszFilename);
+    if (RT_SUCCESS(rc))
+    {
+        if (unlink(pszNativeFilename) != 0)
+            rc = RTErrConvertFromErrno(errno);
+        rtPathFreeNative(pszNativeFilename);
+    }
+    */
+  if (unlink(pszFilename) == 0)
+    return VINF_SUCCESS;
+  return VERR_GENERAL_FAILURE;
+}
+/**
+ * internal: create image.
+ */
+static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
+                          uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
+                          PFNVMPROGRESS pfnProgress, void *pvUser)
+{
+    /* Check args. */
+    Assert(pszFilename);
+    Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
+    Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
+    Assert(cbSize);
+
+    /* Special check for comment length. */
+    if (    pszComment
+        &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
+    {
+        Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
+        return VERR_VDI_COMMENT_TOO_LONG;
+    }
+
+    if (    enmType == VDI_IMAGE_TYPE_UNDO
+        ||  enmType == VDI_IMAGE_TYPE_DIFF)
+    {
+        Assert(pParent);
+        if ((pParent->PreHeader.u32Version >> 16) != VDI_IMAGE_VERSION_MAJOR)
+        {
+            /* Invalid parent image version. */
+            Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
+            return VERR_VDI_UNSUPPORTED_VERSION;
+        }
+
+        /* get image params from the parent image. */
+        fFlags = getImageFlags(&pParent->Header);
+        cbSize = getImageDiskSize(&pParent->Header);
+    }
+
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
+    if (!pImage)
+        return VERR_NO_MEMORY;
+    vdiInitImageDesc(pImage);
+
+    vdiInitPreHeader(&pImage->PreHeader);
+    vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
+
+    if (    enmType == VDI_IMAGE_TYPE_UNDO
+        ||  enmType == VDI_IMAGE_TYPE_DIFF)
+    {
+        /* Set up linkage information. */
+        pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
+        pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
+    }
+
+    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
+    if (!pImage->paBlocks)
+    {
+        RTMemFree(pImage);
+        return VERR_NO_MEMORY;
+    }
+
+    if (enmType != VDI_IMAGE_TYPE_FIXED)
+    {
+        /* for growing images mark all blocks in paBlocks as free. */
+        unsigned i;
+        for (i = 0; i < pImage->Header.u.v1.cBlocks; i++)
+            pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
+    }
+    else
+    {
+        /* for fixed images mark all blocks in paBlocks as allocated */
+        unsigned i;
+        for (i = 0; i < pImage->Header.u.v1.cBlocks; i++)
+            pImage->paBlocks[i] = i;
+        pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
+    }
+
+    /* Setup image parameters. */
+    vdiSetupImageDesc(pImage);
+
+    /* create file */
+    int rc = RTFileOpen(&pImage->File,
+                        pszFilename,
+                        RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
+    if (VBOX_SUCCESS(rc))
+    {
+#if 0
+        /* Lock image exclusively to close any wrong access by VDI API calls. */
+        uint64_t cbLock = pImage->offStartData
+                        + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
+        rc = RTFileLock(pImage->File,
+                        RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
+        if (VBOX_FAILURE(rc))
+        {
+            cbLock = 0;    /* Not locked. */
+            goto l_create_failed;
+        }
+#endif
+
+        if (enmType == VDI_IMAGE_TYPE_FIXED)
+        {
+            /*
+             * Allocate & commit whole file if fixed image, it must be more
+             * effective than expanding file by write operations.
+             */
+            rc = RTFileSetSize(pImage->File,
+                               pImage->offStartData
+                             + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
+        }
+        else
+        {
+            /* Set file size to hold header and blocks array. */
+            rc = RTFileSetSize(pImage->File, pImage->offStartData);
+        }
+        if (VBOX_FAILURE(rc))
+            goto l_create_failed;
+
+        /* Generate image last-modify uuid */
+        RTUuidCreate(getImageModificationUUID(&pImage->Header));
+
+        /* Write pre-header. */
+        rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
+        if (VBOX_FAILURE(rc))
+            goto l_create_failed;
+
+        /* Write header. */
+        rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
+        if (VBOX_FAILURE(rc))
+            goto l_create_failed;
+
+        /* Write blocks array. */
+        rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
+        if (VBOX_FAILURE(rc))
+            goto l_create_failed;
+        rc = RTFileWrite(pImage->File,
+                         pImage->paBlocks,
+                         getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
+                         NULL);
+        if (VBOX_FAILURE(rc))
+            goto l_create_failed;
+
+        if (    (enmType == VDI_IMAGE_TYPE_FIXED)
+            &&  (fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND))
+        {
+            /* Fill image with zeroes. */
+
+            rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
+            if (VBOX_FAILURE(rc))
+                goto l_create_failed;
+
+            /* alloc tmp zero-filled buffer */
+            void *pvBuf = RTMemTmpAllocZ(VDIDISK_DEFAULT_BUFFER_SIZE);
+            if (pvBuf)
+            {
+                uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
+                uint64_t cbDisk = cbFill;
+
+                /* do loop to fill all image. */
+                while (cbFill > 0)
+                {
+                    unsigned to_fill = (unsigned)RT_MIN(cbFill, VDIDISK_DEFAULT_BUFFER_SIZE);
+
+                    rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
+                    if (VBOX_FAILURE(rc))
+                        break;
+
+                    cbFill -= to_fill;
+
+                    if (pfnProgress)
+                    {
+                        rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
+                                         (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
+                                         pvUser);
+                        if (VBOX_FAILURE(rc))
+                            break;
+                    }
+                }
+                RTMemTmpFree(pvBuf);
+            }
+            else
+            {
+                /* alloc error */
+                rc = VERR_NO_MEMORY;
+            }
+        }
+
+    l_create_failed:
+
+#if 0
+        if (cbLock)
+            RTFileUnlock(pImage->File, 0, cbLock);
+#endif
+        RTFileClose(pImage->File);
+
+        /* Delete image file if error occured while creating */
+        if (VBOX_FAILURE(rc))
+            RTFileDelete(pszFilename);
+    }
+
+    RTMemFree(pImage->paBlocks);
+    RTMemFree(pImage);
+
+    if (    VBOX_SUCCESS(rc)
+        &&  pfnProgress)
+        pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
+
+    Log(("vdiCreateImage: done, filename=\"%s\", rc=%d\n", pszFilename, rc));
+
+    return rc;
+}
+
+int  RTFileRead(RTFILE File, void *pvBuf, unsigned cbToRead, unsigned *pcbRead)
+{
+    if (cbToRead <= 0)
+        return VINF_SUCCESS;
+
+    /*
+     * Attempt read.
+     */
+    ssize_t cbRead = read((int)File, pvBuf, cbToRead);
+    if (cbRead >= 0)
+    {
+        if (pcbRead)
+            /* caller can handle partial read. */
+            *pcbRead = cbRead;
+        else
+        {
+            /* Caller expects all to be read. */
+            while ((ssize_t)cbToRead > cbRead)
+            {
+                ssize_t cbReadPart = read((int)File, (char*)pvBuf + cbRead, cbToRead - cbRead);
+                if (cbReadPart <= 0)
+                {
+                    if (cbReadPart == 0)
+                        return VERR_EOF;
+                    else
+                        return VERR_GENERAL_FAILURE; //RTErrConvertFromErrno(errno);
+                }
+                cbRead += cbReadPart;
+            }
+        }
+        return VINF_SUCCESS;
+    }
+
+    return VERR_GENERAL_FAILURE; //RTErrConvertFromErrno(errno);
+}
+
+
+/**
+ * Open an image.
+ * @internal
+ */
+static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename,
+                        unsigned fOpen, PVDIIMAGEDESC pParent)
+{
+    /*
+     * Validate input.
+     */
+    Assert(ppImage);
+    Assert(pszFilename);
+    Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
+
+    PVDIIMAGEDESC   pImage;
+    size_t          cchFilename = strlen(pszFilename);
+    if (cchFilename >= sizeof(pImage->szFilename))
+    {
+        AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
+        return VERR_FILENAME_TOO_LONG;
+    }
+
+    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
+    if (!pImage)
+        return VERR_NO_MEMORY;
+    vdiInitImageDesc(pImage);
+
+    memcpy(pImage->szFilename, pszFilename, cchFilename);
+    pImage->fOpen = fOpen;
+
+    /*
+     * Open the image.
+     */
+    int rc = RTFileOpen(&pImage->File,
+                        pImage->szFilename,
+                        fOpen & VDI_OPEN_FLAGS_READONLY
+                        ? RTFILE_O_READ      | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
+                        : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+    if (VBOX_FAILURE(rc))
+    {
+        if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
+        {
+            /* Try to open image for reading only. */
+            rc = RTFileOpen(&pImage->File,
+                            pImage->szFilename,
+                            RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+            if (VBOX_SUCCESS(rc))
+                pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
+        }
+        if (VBOX_FAILURE(rc))
+        {
+            RTMemFree(pImage);
+            return rc;
+        }
+    }
+    /* Set up current image r/w state. */
+    pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
+
+    /*
+     * Set initial file lock for reading header only.
+     * Length of lock doesn't matter, it just must include image header.
+     */
+#if 0
+    uint64_t cbLock = _1M;
+    rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
+    if (VBOX_FAILURE(rc))
+    {
+        cbLock = 0;
+        goto l_open_failed;
+    }
+#endif
+    
+    /* Read pre-header. */
+    rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+    rc = vdiValidatePreHeader(&pImage->PreHeader);
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+
+    /* Read header. */
+    pImage->Header.uVersion = pImage->PreHeader.u32Version;
+    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
+    {
+        case 0:
+            rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
+            break;
+        case 1:
+            rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
+            break;
+        default:
+            rc = VERR_VDI_UNSUPPORTED_VERSION;
+            break;
+    }
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+
+    rc = vdiValidateHeader(&pImage->Header);
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+
+    /* Check diff image correctness. */
+    if (pParent)
+    {
+        if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
+        {
+            rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
+            goto l_open_failed;
+        }
+
+        if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
+            &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
+        {
+            rc = VERR_VDI_WRONG_DIFF_IMAGE;
+            goto l_open_failed;
+        }
+
+        if (    getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
+            ||  getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
+            ||  getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
+            ||  getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
+        {
+            rc = VERR_VDI_WRONG_DIFF_IMAGE;
+            goto l_open_failed;
+        }
+
+        /* Check linkage data. */
+        if (    RTUuidCompare(getImageParentUUID(&pImage->Header),
+                              getImageCreationUUID(&pParent->Header))
+            ||  RTUuidCompare(getImageParentModificationUUID(&pImage->Header),
+                              getImageModificationUUID(&pParent->Header)))
+        {
+            rc = VERR_VDI_IMAGES_UUID_MISMATCH;
+            goto l_open_failed;
+        }
+    }
+
+    /* Setup image parameters by header. */
+    vdiSetupImageDesc(pImage);
+
+    /* reset modified flag into first-modified state. */
+    pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
+
+#if 0
+    /* Image is validated, set working file lock on it. */
+    rc = RTFileUnlock(pImage->File, 0, cbLock);
+    AssertRC(rc);
+    cbLock = pImage->offStartData
+           + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
+    rc = RTFileLock(pImage->File,
+                    (pImage->fReadOnly) ?
+                        RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
+                        RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
+                    0,
+                    cbLock);
+    if (    VBOX_FAILURE(rc)
+        &&  !pImage->fReadOnly)
+    {
+        /* Failed to lock image for writing, try read-only lock. */
+        rc = RTFileLock(pImage->File,
+                        RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
+        if (VBOX_SUCCESS(rc))
+            pImage->fReadOnly = true;
+    }
+    if (VBOX_FAILURE(rc))
+    {
+        cbLock = 0;    /* Not locked. */
+        goto l_open_failed;
+    }
+#endif
+    
+    /* Allocate memory for blocks array. */
+    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
+    if (!pImage->paBlocks)
+    {
+        rc = VERR_NO_MEMORY;
+        goto l_open_failed;
+    }
+
+    /* Read blocks array. */
+    rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+    rc = RTFileRead(pImage->File, pImage->paBlocks,
+                    getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), NULL);
+    if (VBOX_FAILURE(rc))
+        goto l_open_failed;
+
+    /* all done. */
+    *ppImage = pImage;
+    return VINF_SUCCESS;
+
+l_open_failed:
+    /* Clean up. */
+    if (pImage->paBlocks)
+        RTMemFree(pImage->paBlocks);
+#if 0
+    if (cbLock)
+        RTFileUnlock(pImage->File, 0, cbLock);
+#endif
+    RTFileClose(pImage->File);
+    RTMemFree(pImage);
+    Log(("vdiOpenImage: failed, filename=\"%s\", rc=%d\n", pszFilename, rc));
+    return rc;
+}
+
+/**
+ * internal: save header to file.
+ */
+static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
+{
+    /* Seek to header start. */
+    int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
+    if (VBOX_SUCCESS(rc))
+    {
+        switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
+        {
+            case 0:
+                rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
+                break;
+            case 1:
+                rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
+                break;
+            default:
+                rc = VERR_VDI_UNSUPPORTED_VERSION;
+                break;
+        }
+    }
+    AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%d\n", pImage->szFilename, rc));
+    return rc;
+}
+
+/**
+ * internal: save block pointer to file, save header to file.
+ */
+static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
+{
+    /* Update image header. */
+    int rc = vdiUpdateHeader(pImage);
+    if (VBOX_SUCCESS(rc))
+    {
+        /* write only one block pointer. */
+        rc = RTFileSeek(pImage->File,
+                        pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
+                        RTFILE_SEEK_BEGIN,
+                        NULL);
+        if (VBOX_SUCCESS(rc))
+            rc = RTFileWrite(pImage->File,
+                             &pImage->paBlocks[uBlock],
+                             sizeof(VDIIMAGEBLOCKPOINTER),
+                             NULL);
+        AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%d\n",
+                         uBlock, pImage->szFilename, rc));
+    }
+    return rc;
+}
+
+/**
+ * internal: mark image as modified, if this is the first change - update image header
+ * on disk with a new uuidModify value.
+ */
+static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
+{
+    pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
+    if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
+    {
+        pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
+
+        /* first modify - generate uuidModify and save to file. */
+        vdiResetModifiedFlag(pImage);
+
+        if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
+        {
+            /* save header to file,
+             * note: no rc checking.
+             */
+            vdiUpdateHeader(pImage);
+        }
+    }
+}
+
+/**
+ * internal: generate new uuidModify if the image was changed.
+ */
+static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
+{
+    if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
+    {
+        /* generate new last-modified uuid */
+        if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
+            RTUuidCreate(getImageModificationUUID(&pImage->Header));
+
+        pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
+    }
+}
+
+int  RTFileFlush(RTFILE File)
+{
+    if (fsync((int)File))
+        return VERR_GENERAL_FAILURE;//RTErrConvertFromErrno(errno);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Flush the image file to disk.
+ */
+void vdiFlushImage(PVDIIMAGEDESC pImage)
+{
+    if (!pImage->fReadOnly)
+    {
+        /* Update last-modified uuid if need. */
+        vdiResetModifiedFlag(pImage);
+
+        /* Save header. */
+        int rc = vdiUpdateHeader(pImage);
+        AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%d\n",
+                         pImage->szFilename, rc));
+        RTFileFlush(pImage->File);
+    }
+}
+
+/**
+ * internal: close image file.
+ */
+static void vdiCloseImage(PVDIIMAGEDESC pImage)
+{
+    /* Params checking. */
+    Assert(pImage);
+    Assert(pImage->File != NIL_RTFILE);
+
+    vdiFlushImage(pImage);
+#if 0
+    RTFileUnlock(pImage->File,
+                 0,
+                 pImage->offStartData
+               + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
+#endif
+    RTFileClose(pImage->File);
+
+    /* free image resources */
+    RTMemFree(pImage->paBlocks);
+    RTMemFree(pImage);
+}
+
+/**
+ * internal: read data inside image block.
+ *
+ * note: uBlock must be valid, readed data must not overlap block bounds.
+ */
+static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
+                          unsigned cbToRead, void *pvBuf)
+{
+    if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+    {
+        /* block present in image file */
+        uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
+                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
+        int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
+        if (VBOX_SUCCESS(rc))
+            rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
+        if (VBOX_FAILURE(rc))
+            Log(("vdiReadInBlock: rc=%d filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
+                 rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
+        return rc;
+    }
+
+    /* Returns zeroes for both free and zero block types. */
+    memset(pvBuf, 0, cbToRead);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Read data from virtual HDD.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to VDI HDD container.
+ * @param   offStart        Offset of first reading byte from start of disk.
+ * @param   pvBuf           Pointer to buffer for reading data.
+ * @param   cbToRead        Number of bytes to read.
+ */
+VBOXDDU_DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, unsigned cbToRead)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    PVDIIMAGEDESC pImage = pDisk->pLast;
+    Assert(pImage);
+
+    /* Check params. */
+    if (    offStart + cbToRead > getImageDiskSize(&pImage->Header)
+        ||  cbToRead == 0)
+    {
+        AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /* Calculate starting block number and offset inside it. */
+    unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
+    unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
+
+    /* Save block size here for speed optimization. */
+    unsigned cbBlock = getImageBlockSize(&pImage->Header);
+
+    /* loop through blocks */
+    int rc;
+    for (;;)
+    {
+        unsigned to_read;
+        if ((offRead + cbToRead) <= cbBlock)
+            to_read = cbToRead;
+        else
+            to_read = cbBlock - offRead;
+
+        if (pDisk->cImages > 1)
+        {
+            /* Differencing images are used, handle them. */
+            pImage = pDisk->pLast;
+
+            /* Search for image with allocated block. */
+            while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
+            {
+                pImage = pImage->pPrev;
+                if (!pImage)
+                {
+                    /* Block is not allocated in all images of chain. */
+                    pImage = pDisk->pLast;
+                    break;
+                }
+            }
+        }
+
+        rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
+
+        cbToRead -= to_read;
+        if (    cbToRead == 0
+            ||  VBOX_FAILURE(rc))
+            break;
+
+        /* goto next block */
+        uBlock++;
+        offRead = 0;
+        pvBuf = (char *)pvBuf + to_read;
+    }
+
+    return rc;
+}
+
+/**
+ * internal: fill the whole block with zeroes.
+ *
+ * note: block id must be valid, block must be already allocated in file.
+ * note: if pDisk is NULL, the default buffer size is used
+ */
+static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
+{
+    int rc;
+
+    /* seek to start of block in file. */
+    uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
+                       + (pImage->offStartData + pImage->offStartBlockData);
+    rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
+    if (VBOX_FAILURE(rc))
+    {
+        Log(("vdiFillBlockByZeroes: seek rc=%d filename=\"%s\" uBlock=%u u64Offset=%llu\n",
+             rc, pImage->szFilename, uBlock, u64Offset));
+        return rc;
+    }
+
+    /* alloc tmp zero-filled buffer */
+    void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
+    if (!pvBuf)
+        return VERR_NO_MEMORY;
+
+    unsigned cbFill = getImageBlockSize(&pImage->Header);
+
+    /* do loop, because buffer size may be less then block size */
+    while (cbFill > 0)
+    {
+        unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
+        rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
+        if (VBOX_FAILURE(rc))
+        {
+            Log(("vdiFillBlockByZeroes: write rc=%d filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
+                 rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
+            break;
+        }
+
+        cbFill -= to_fill;
+    }
+
+    RTMemTmpFree(pvBuf);
+    return rc;
+}
+
+/**
+ * Finds the first set bit in a bitmap.
+ *
+ * @returns Index of the first set bit.
+ * @returns -1 if no clear bit was found.
+ * @param   pvBitmap    Pointer to the bitmap.
+ * @param   cBits       The number of bits in the bitmap. Multiple of 32.
+ */
+
+typedef unsigned long            RTCCUINTREG;
+
+#define RT_INLINE_ASM_GNU_STYLE 1
+
+int ASMBitFirstSet(volatile void *pvBitmap, uint32_t cBits)
+{
+    if (cBits)
+    {
+        int32_t iBit;
+# if RT_INLINE_ASM_GNU_STYLE
+        RTCCUINTREG uEAX, uECX, uEDI;
+        cBits = RT_ALIGN_32(cBits, 32);
+        __asm__ __volatile__("repe; scasl\n\t"
+                             "je    1f\n\t"
+#  ifdef __AMD64__
+                             "lea   -4(%%rdi), %%rdi\n\t"
+                             "movl  (%%rdi), %%eax\n\t"
+                             "subq  %5, %%rdi\n\t"
+#  else
+                             "lea   -4(%%edi), %%edi\n\t"
+                             "movl  (%%edi), %%eax\n\t"
+                             "subl  %5, %%edi\n\t"
+#  endif
+                             "shll  $3, %%edi\n\t"
+                             "bsfl  %%eax, %%edx\n\t"
+                             "addl  %%edi, %%edx\n\t"
+                             "1:\t\n"
+                             : "=d" (iBit),
+                               "=&c" (uECX),
+                               "=&D" (uEDI),
+                               "=&a" (uEAX)
+                             : "0" (0xffffffff),
+                               "mr" (pvBitmap),
+                               "1" (cBits >> 5),
+                               "2" (pvBitmap),
+                               "3" (0));
+# else
+        cBits = RT_ALIGN_32(cBits, 32);
+        __asm
+        {
+#  ifdef __AMD64__
+            mov     rdi, [pvBitmap]
+            mov     rbx, rdi
+#  else
+            mov     edi, [pvBitmap]
+            mov     ebx, edi
+#  endif
+            mov     edx, 0ffffffffh
+            xor     eax, eax
+            mov     ecx, [cBits]
+            shr     ecx, 5
+            repe    scasd
+            je      done
+#  ifdef __AMD64__
+            lea     rdi, [rdi - 4]
+            mov     eax, [rdi]
+            sub     rdi, rbx
+#  else
+            lea     edi, [edi - 4]
+            mov     eax, [edi]
+            sub     edi, ebx
+#  endif
+            shl     edi, 3
+            bsf     edx, eax
+            add     edx, edi
+        done:
+            mov   [iBit], edx
+        }
+# endif
+        return iBit;
+    }
+    return -1;
+}
+
+
+/**
+ * internal: write data inside image block.
+ *
+ * note: uBlock must be valid, written data must not overlap block bounds.
+ */
+static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, unsigned cbToWrite, const void *pvBuf)
+{
+    int rc;
+
+    /* Check if we can write into file. */
+    if (pImage->fReadOnly)
+    {
+        Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
+        return VERR_WRITE_PROTECT;
+    }
+
+    /* This could be optimized a little (not setting it when writing zeroes
+     * to a zeroed block). Won't buy us much, because it's very unlikely
+     * that only such zero data block writes occur while the VDI is opened. */
+    vdiSetModifiedFlag(pImage);
+
+    if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+    {
+        if (!pDisk || !pDisk->fHonorZeroWrites)
+        {
+            /* If the destination block is unallocated at this point, it's either
+             * a zero block or a block which hasn't been used so far (which also
+             * means that it's a zero block. Don't need to write anything to this
+             * block if the data consists of just zeroes. */
+            Assert(cbToWrite % 4 == 0);
+            if (ASMBitFirstSet((volatile void *)pvBuf, cbToWrite * 8) == -1)
+            {
+                pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
+                return VINF_SUCCESS;
+            }
+        }
+
+        /* need to allocate a new block in image file */
+
+        /* expand file by one block */
+        uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
+                         + pImage->offStartData;
+        rc = RTFileSetSize(pImage->File, u64Size);
+        if (VBOX_FAILURE(rc))
+        {
+            Log(("vdiWriteInBlock: set size rc=%d filename=\"%s\" uBlock=%u u64Size=%llu\n",
+                 rc, pImage->szFilename, uBlock, u64Size));
+            return rc;
+        }
+
+        unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
+        pImage->paBlocks[uBlock] = cBlocksAllocated;
+        setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
+
+        if (    pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
+            ||  pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
+        {
+            /* Fill newly allocated block by zeroes. */
+
+            if (offWrite || cbToWrite != getImageBlockSize(&pImage->Header))
+            {
+                rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
+                if (VBOX_FAILURE(rc))
+                    return rc;
+            }
+        }
+
+        rc = vdiUpdateBlockInfo(pImage, uBlock);
+        if (VBOX_FAILURE(rc))
+            return rc;
+    }
+
+    /* Now block present in image file, write data inside it. */
+    uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
+                       + (pImage->offStartData + pImage->offStartBlockData + offWrite);
+    rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
+    if (VBOX_SUCCESS(rc))
+    {
+        rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
+        if (VBOX_FAILURE(rc))
+            Log(("vdiWriteInBlock: write rc=%d filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
+                 rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
+    }
+    else
+        Log(("vdiWriteInBlock: seek rc=%d filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
+             rc, pImage->szFilename, uBlock, offWrite, u64Offset));
+
+    return rc;
+}
+
+/**
+ * internal: copy data block from one (parent) image to last image.
+ */
+static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
+{
+    Assert(pImage != pDisk->pLast);
+
+    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
+    {
+        /*
+         * if src block is zero, set dst block to zero too.
+         */
+        pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
+        return VINF_SUCCESS;
+    }
+
+    /* alloc tmp buffer */
+    void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
+    if (!pvBuf)
+        return VERR_NO_MEMORY;
+
+    int rc = VINF_SUCCESS;
+
+    unsigned cbCopy = getImageBlockSize(&pImage->Header);
+    unsigned offCopy = 0;
+
+    /* do loop, because buffer size may be less then block size */
+    while (cbCopy > 0)
+    {
+        unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
+        rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
+        if (VBOX_FAILURE(rc))
+            break;
+
+        rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
+        if (VBOX_FAILURE(rc))
+            break;
+
+        cbCopy -= to_copy;
+        offCopy += to_copy;
+    }
+
+    RTMemTmpFree(pvBuf);
+    return rc;
+}
+
+/**
+ * Write data to virtual HDD.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to VDI HDD container.
+ * @param   offStart        Offset of first writing byte from start of HDD.
+ * @param   pvBuf           Pointer to buffer of writing data.
+ * @param   cbToWrite       Number of bytes to write.
+ */
+VBOXDDU_DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, unsigned cbToWrite)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    PVDIIMAGEDESC pImage = pDisk->pLast;
+    Assert(pImage);
+
+    /* Check params. */
+    if (    offStart + cbToWrite > getImageDiskSize(&pImage->Header)
+        ||  cbToWrite == 0)
+    {
+        AssertMsgFailed(("offStart=%llu cbToWrite=%u, imageDisk=%Ld\n", offStart, cbToWrite, getImageDiskSize(&pImage->Header)));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /* Calculate starting block number and offset inside it. */
+    unsigned uBlock   = (unsigned)(offStart >> pImage->uShiftOffset2Index);
+    unsigned offWrite = (unsigned)offStart   & pImage->uBlockMask;
+    unsigned cbBlock  = getImageBlockSize(&pImage->Header);
+
+    /* loop through blocks */
+    int rc;
+    for (;;)
+    {
+        unsigned to_write;
+        if (offWrite + cbToWrite <= cbBlock)
+            to_write = cbToWrite;
+        else
+            to_write = cbBlock - offWrite;
+
+        /* All callers write less than a VDI block right now (assuming
+         * default VDI block size). So not worth optimizing for the case
+         * where a full block is overwritten (no copying required).
+         * Checking whether a block is all zeroes after the write is too
+         * expensive (would require reading the rest of the block). */
+
+        if (pDisk->cImages > 1)
+        {
+            /* Differencing images are used, handle them. */
+
+            /* Search for image with allocated block. */
+            while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
+            {
+                pImage = pImage->pPrev;
+                if (!pImage)
+                {
+                    /* Block is not allocated in all images of chain. */
+                    pImage = pDisk->pLast;
+                    break;
+                }
+            }
+
+            if (pImage != pDisk->pLast)
+            {
+                /* One of parent image has a block data, copy it into last image. */
+                rc = vdiCopyBlock(pDisk, pImage, uBlock);
+                if (VBOX_FAILURE(rc))
+                    break;
+                pImage = pDisk->pLast;
+            }
+        }
+
+        /* Actually write the data into block. */
+        rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
+
+        cbToWrite -= to_write;
+        if (    cbToWrite == 0
+            || VBOX_FAILURE(rc))
+            break;
+
+        /* goto next block */
+        uBlock++;
+        offWrite = 0;
+        pvBuf = (char *)pvBuf + to_write;
+    }
+
+    return rc;
+}
+
+/**
+ * Creates a new base image file.
+ *
+ * @returns VBox status code.
+ * @param   pszFilename     Name of the creating image file.
+ * @param   enmType         Image type, only base image types are acceptable.
+ * @param   cbSize          Image size in bytes.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pfnProgress     Progress callback. Optional.
+ * @param   pvUser          User argument for the progress callback.
+ */
+VBOXDDU_DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize,
+                                     const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
+{
+    LogFlow(("VDICreateBaseImage:\n"));
+
+    /* Check arguments. */
+    if (    !pszFilename
+        ||  *pszFilename == '\0'
+        ||  (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
+        ||  cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
+    {
+        AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
+                         pszFilename, enmType, cbSize));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
+                            pfnProgress, pvUser);
+    LogFlow(("VDICreateBaseImage: returns %d for filename=\"%s\"\n", rc, pszFilename));
+    return rc;
+}
+
+
+/**
+ * Creates a differencing dynamically growing image file for specified parent image.
+ *
+ * @returns VBox status code.
+ * @param   pszFilename     Name of the creating differencing image file.
+ * @param   pszParent       Name of the parent image file. May be base or diff image type.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pfnProgress     Progress callback. Optional.
+ * @param   pvUser          User argument for the progress callback.
+ */
+VBOXDDU_DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent,
+                                           const char *pszComment, PFNVMPROGRESS pfnProgress,
+                                           void *pvUser)
+{
+    LogFlow(("VDICreateDifferenceImage:\n"));
+
+    /* Check arguments. */
+    if (    !pszFilename
+        ||  *pszFilename == '\0'
+        ||  !pszParent
+        ||  *pszParent == '\0')
+    {
+        AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
+                         pszFilename, pszParent));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    PVDIIMAGEDESC pParent;
+    int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
+    if (VBOX_SUCCESS(rc))
+    {
+        rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
+                            getImageDiskSize(&pParent->Header), pszComment, pParent,
+                            pfnProgress, pvUser);
+        vdiCloseImage(pParent);
+    }
+    LogFlow(("VDICreateDifferenceImage: returns %d for filename=\"%s\"\n", rc, pszFilename));
+    return rc;
+}
+
+/**
+ * Initialize the VDIDISK structure.
+ */
+void vdiInitVDIDisk(PVDIDISK pDisk)
+{
+    Assert(pDisk);
+    pDisk->u32Signature = VDIDISK_SIGNATURE;
+    pDisk->cImages = 0;
+    pDisk->pBase   = NULL;
+    pDisk->pLast   = NULL;
+    pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
+    pDisk->cbBuf   = VDIDISK_DEFAULT_BUFFER_SIZE;
+    pDisk->fHonorZeroWrites = false;
+}
+
+/**
+ * internal: add image structure to the end of images list.
+ */
+static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
+{
+    pImage->pPrev = NULL;
+    pImage->pNext = NULL;
+
+    if (pDisk->pBase)
+    {
+        Assert(pDisk->cImages > 0);
+        pImage->pPrev = pDisk->pLast;
+        pDisk->pLast->pNext = pImage;
+        pDisk->pLast = pImage;
+    }
+    else
+    {
+        Assert(pDisk->cImages == 0);
+        pDisk->pBase = pImage;
+        pDisk->pLast = pImage;
+    }
+
+    pDisk->cImages++;
+}
+
+/**
+ * internal: remove image structure from the images list.
+ */
+static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
+{
+    Assert(pDisk->cImages > 0);
+
+    if (pImage->pPrev)
+        pImage->pPrev->pNext = pImage->pNext;
+    else
+        pDisk->pBase = pImage->pNext;
+
+    if (pImage->pNext)
+        pImage->pNext->pPrev = pImage->pPrev;
+    else
+        pDisk->pLast = pImage->pPrev;
+
+    pImage->pPrev = NULL;
+    pImage->pNext = NULL;
+
+    pDisk->cImages--;
+}
+
+/**
+ * Allocates and initializes VDI HDD container.
+ *
+ * @returns Pointer to newly created HDD container with no one opened image file.
+ * @returns NULL on failure, typically out of memory.
+ */
+VBOXDDU_DECL(PVDIDISK) VDIDiskCreate(void)
+{
+    PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
+    if (pDisk)
+        vdiInitVDIDisk(pDisk);
+    LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
+    return pDisk;
+}
+
+/**
+ * Closes all opened image files in HDD container.
+ *
+ * @param   pDisk           Pointer to VDI HDD container.
+ */
+VBOXDDU_DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
+{
+    LogFlow(("VDIDiskCloseAllImages:\n"));
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    PVDIIMAGEDESC pImage = pDisk->pLast;
+    while (pImage)
+    {
+        PVDIIMAGEDESC pPrev = pImage->pPrev;
+        vdiRemoveImageFromList(pDisk, pImage);
+        vdiCloseImage(pImage);
+        pImage = pPrev;
+    }
+    Assert(pDisk->pLast == NULL);
+}
+
+/**
+ * Destroys VDI HDD container. If container has opened image files they will be closed.
+ *
+ * @param   pDisk           Pointer to VDI HDD container.
+ */
+VBOXDDU_DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
+{
+    LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    if (pDisk)
+    {
+        VDIDiskCloseAllImages(pDisk);
+        RTMemFree(pDisk);
+    }
+}
+
+/**
+ * Get disk size of VDI HDD container.
+ *
+ * @returns Virtual disk size in bytes.
+ * @returns 0 if no one VDI image is opened in HDD container.
+ */
+VBOXDDU_DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    if (pDisk->pBase)
+    {
+        LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
+        return getImageDiskSize(&pDisk->pBase->Header);
+    }
+
+    AssertMsgFailed(("No one disk image is opened!\n"));
+    return 0;
+}
+
+/**
+ * Get virtual disk geometry stored in image file.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
+ * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
+ * @param   pDisk           Pointer to VDI HDD container.
+ * @param   pcCylinders     Where to store the number of cylinders. NULL is ok.
+ * @param   pcHeads         Where to store the number of heads. NULL is ok.
+ * @param   pcSectors       Where to store the number of sectors. NULL is ok.
+ */
+VBOXDDU_DECL(int) VDIDiskGetGeometry(PVDIDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    if (pDisk->pBase)
+    {
+        int rc = VINF_SUCCESS;
+        PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
+        LogFlow(("VDIDiskGetGeometry: C/H/S = %u/%u/%u\n",
+                 pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
+        if (    pGeometry->cCylinders > 0
+            &&  pGeometry->cHeads > 0
+            &&  pGeometry->cSectors > 0)
+        {
+            if (pcCylinders)
+                *pcCylinders = pGeometry->cCylinders;
+            if (pcHeads)
+                *pcHeads = pGeometry->cHeads;
+            if (pcSectors)
+                *pcSectors = pGeometry->cSectors;
+        }
+        else
+            rc = VERR_VDI_GEOMETRY_NOT_SET;
+
+        LogFlow(("VDIDiskGetGeometry: returns %d\n", rc));
+        return rc;
+    }
+
+    AssertMsgFailed(("No one disk image is opened!\n"));
+    return VERR_VDI_NOT_OPENED;
+}
+
+/**
+ * Get virtual disk translation mode stored in image file.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
+ * @param   pDisk           Pointer to VDI HDD container.
+ * @param   penmTranslation Where to store the translation mode (see pdm.h).
+ */
+VBOXDDU_DECL(int) VDIDiskGetTranslation(PVDIDISK pDisk, PPDMBIOSTRANSLATION penmTranslation)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+    Assert(penmTranslation);
+
+    if (pDisk->pBase)
+    {
+        *penmTranslation = getImageTranslation(&pDisk->pBase->Header);
+        LogFlow(("VDIDiskGetTranslation: translation=%d\n", *penmTranslation));
+        return VINF_SUCCESS;
+    }
+
+    AssertMsgFailed(("No one disk image is opened!\n"));
+    return VERR_VDI_NOT_OPENED;
+}
+
+
+/**
+ * Opens an image file.
+ *
+ * The first opened image file in a HDD container must have a base image type,
+ * others (next opened images) must be a differencing or undo images.
+ * Linkage is checked for differencing image to be in consistence with the previously opened image.
+ * When a next differencing image is opened and the last image was opened in read/write access
+ * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
+ * other processes to use images in read-only mode too.
+ *
+ * Note that the image can be opened in read-only mode if a read/write open is not possible.
+ * Use VDIDiskIsReadOnly to check open mode.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to VDI HDD container.
+ * @param   pszFilename     Name of the image file to open.
+ * @param   fOpen           Image file open mode, see VDI_OPEN_FLAGS_* constants.
+ */
+VBOXDDU_DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
+{
+    /* sanity check */
+    Assert(pDisk);
+    AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+    /* Check arguments. */
+    if (    !pszFilename
+        ||  *pszFilename == '\0'
+        ||  (fOpen & ~VDI_OPEN_FLAGS_MASK))
+    {
+        AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
+        return VERR_INVALID_PARAMETER;
+    }
+    LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
+
+    PVDIIMAGEDESC pImage;
+    int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
+    if (VBOX_SUCCESS(rc))
+    {
+        if (pDisk->pLast == NULL)
+        {
+            /* Opening base image, check its type. */
+            if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
+                &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
+            {
+                /* E. Rouault : add this hack to handle base images */
+                rc = VDIDiskOpenImage(pDisk, getImageComment(&pImage->Header), VDI_OPEN_FLAGS_READONLY);
+                if (VBOX_SUCCESS(rc))
+                {
+                  vdiCloseImage(pImage);
+                  return VDIDiskOpenImage(pDisk, pszFilename, fOpen);
+                }
+                else
+                {
+                  rc = VERR_VDI_INVALID_TYPE;
+                }
+            }
+        }
+
+        if (VBOX_SUCCESS(rc))
+            vdiAddImageToList(pDisk, pImage);
+        else
+            vdiCloseImage(pImage);
+    }
+
+    LogFlow(("VDIDiskOpenImage: returns %d\n", rc));
+    return rc;
+}
+
+static int VDI_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    VDIPREHEADER* preheader = (VDIPREHEADER*)buf;
+    VDIHEADER fakeHeader;
+    assert(buf_size > sizeof(VDIPREHEADER));
+    if (!RT_SUCCESS(vdiValidatePreHeader))
+      return 0;
+    
+    fakeHeader.uVersion = preheader->u32Version;
+    switch (GET_MAJOR_HEADER_VERSION(&fakeHeader))
+    {
+        case 0:
+            return 2;
+            break;
+        case 1:
+            return 2;
+            break;
+        default:
+            return 0;
+            break;
+    }
+}
+
+
+typedef struct BDRVVDIState {
+    PVDIDISK vdiDisk;
+} BDRVVDIState;
+
+static int VDI_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    int rc;
+    BDRVVDIState *s = bs->opaque;
+    PDMBIOSTRANSLATION translation;
+
+    s->vdiDisk = VDIDiskCreate();
+        
+    rc = VDIDiskOpenImage(s->vdiDisk, filename,
+                            ((flags & BDRV_O_ACCESS) == O_RDWR) ? VDI_OPEN_FLAGS_NORMAL : VDI_OPEN_FLAGS_READONLY);
+    assert(RT_SUCCESS(rc));
+    if (!((flags & BDRV_O_ACCESS) == O_RDWR))
+    {
+      bs->read_only = 1;
+    }
+    VDIDiskGetGeometry(s->vdiDisk, &bs->cyls, &bs->heads, &bs->secs);
+    VDIDiskGetTranslation(s->vdiDisk, &translation);
+    //fprintf(stderr, "C=%d,H=%d,S=%d,size=%Ld\n", bs->cyls,bs->headss,bs->secs, getImageDiskSize(&s->imageDesc->Header));
+    if (translation == PDMBIOSTRANSLATION_NONE)
+      bs->translation = BIOS_ATA_TRANSLATION_NONE;
+    else if (translation == PDMBIOSTRANSLATION_LBA)
+      bs->translation = BIOS_ATA_TRANSLATION_LBA;
+    else
+      bs->translation = BIOS_ATA_TRANSLATION_AUTO;
+    bs->total_sectors = VDIDiskGetSize(s->vdiDisk) / 512;
+
+    return 0;
+}
+
+static int VDI_read(BlockDriverState *bs, int64_t sector_num, 
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVVDIState *s = bs->opaque;
+    int rc;
+    
+    rc = VDIDiskRead(s->vdiDisk, sector_num * 512, buf, nb_sectors * 512);
+    assert(RT_SUCCESS(rc));
+    return 0;
+}
+
+static int VDI_write(BlockDriverState *bs, int64_t sector_num, 
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVVDIState *s = bs->opaque;
+    int rc;
+    
+    rc = VDIDiskWrite(s->vdiDisk, sector_num * 512, buf, nb_sectors * 512);
+    assert(RT_SUCCESS(rc));
+    return 0;
+}
+
+static int64_t VDI_getlength(BlockDriverState *bs)
+{
+  BDRVVDIState *s = bs->opaque;
+  return VDIDiskGetSize(s->vdiDisk);
+}
+
+
+static int VDI_create(const char *filename, int64_t total_size,
+                      const char *backing_file, int flags)
+{
+  //fprintf(stderr, "creationg image of size %Ld\n", total_size * 512);
+  if (backing_file)
+  {
+    assert(RT_SUCCESS(VDICreateDifferenceImage(filename, backing_file, backing_file, NULL, NULL)));
+  }
+  else
+  {
+    assert(RT_SUCCESS(VDICreateBaseImage(filename, VDI_IMAGE_TYPE_NORMAL, total_size * 512, NULL, NULL, NULL)));
+  }
+  return 0;
+}
+
+static void VDI_close(BlockDriverState *bs)
+{
+    BDRVVDIState *s = bs->opaque;
+    VDIDiskDestroy(s->vdiDisk);
+}
+
+BlockDriver bdrv_VDI = {
+    "VDI",
+    sizeof(BDRVVDIState),
+    VDI_probe,
+    VDI_open,
+    VDI_read,
+    VDI_write,
+    VDI_close,
+    VDI_create,
+    .bdrv_getlength = VDI_getlength,
+};
--- qemu.ori/block.c	2007-06-30 22:52:01.000000000 +0200
+++ qemu_vdi/block.c	2007-07-02 20:24:05.000000000 +0200
@@ -1241,6 +1241,7 @@
     bdrv_register(&bdrv_vpc);
     bdrv_register(&bdrv_vvfat);
     bdrv_register(&bdrv_qcow2);
+    bdrv_register(&bdrv_VDI);
 }
 
 void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb,
--- qemu.ori/vl.h	2007-06-30 22:52:01.000000000 +0200
+++ qemu_vdi/vl.h	2007-07-02 20:24:05.000000000 +0200
@@ -569,6 +569,7 @@
 extern BlockDriver bdrv_vpc;
 extern BlockDriver bdrv_vvfat;
 extern BlockDriver bdrv_qcow2;
+extern BlockDriver bdrv_VDI;
 
 typedef struct BlockDriverInfo {
     /* in bytes, 0 if irrelevant */

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [Qemu-devel] [PATCH] Add support for VDI images
  2007-07-02 18:34 [Qemu-devel] [PATCH] Add support for VDI images Even Rouault
@ 2007-07-27  0:25 ` Alex Beregszaszi
  0 siblings, 0 replies; 2+ messages in thread
From: Alex Beregszaszi @ 2007-07-27  0:25 UTC (permalink / raw)
  To: qemu-devel

Hi,

> This patch adds support for reading, writing and creation (and thus 
> conversion) of VDI images (VirtualBox image format). Creation of derived 
> images above read-only base images also works, through a hack (the base image 
> filename is stored as the comment of the derived image)
> It's mainly a thin wrapper around VirtualBox OSE source code.
> It may not compile on non-Linux hosts, and it definitely won't work on big 
> endian hosts. So, there's still a bit of work to do for those interested by 
> running it on those platforms.

What about writing a native implementation? At least with read support,
for importing from Virtualbox.

--
Alex

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2007-07-27  0:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-02 18:34 [Qemu-devel] [PATCH] Add support for VDI images Even Rouault
2007-07-27  0:25 ` Alex Beregszaszi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).