Index: unk/include/VBox/VBoxHDD-CachePlugin.h
===================================================================
--- /trunk/include/VBox/VBoxHDD-CachePlugin.h	(revision 33566)
+++ 	(revision )
@@ -1,356 +1,0 @@
-/** @file
- * Internal hard disk format support API for VBoxHDD cache images.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- * The contents of this file may alternatively be used under the terms
- * of the Common Development and Distribution License Version 1.0
- * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
- * VirtualBox OSE distribution, in which case the provisions of the
- * CDDL are applicable instead of those of the GPL.
- *
- * You may elect to license modified versions of this file under the
- * terms and conditions of either the GPL or the CDDL or both.
- */
-
-#ifndef __VBoxHDD_CachePlugin_h__
-#define __VBoxHDD_CachePlugin_h__
-
-#include <VBox/VBoxHDD.h>
-
-/**
- * Cache format backend interface used by VBox HDD Container implementation.
- */
-typedef struct VDCACHEBACKEND
-{
-    /**
-     * The name of the backend (constant string).
-     */
-    const char *pszBackendName;
-
-    /**
-     * The size of the structure.
-     */
-    uint32_t cbSize;
-
-    /**
-     * The capabilities of the backend.
-     */
-    uint64_t uBackendCaps;
-
-    /**
-     * Pointer to a NULL-terminated array of strings, containing the supported
-     * file extensions. Note that some backends do not work on files, so this
-     * pointer may just contain NULL.
-     */
-    const char * const *papszFileExtensions;
-
-    /**
-     * Pointer to an array of structs describing each supported config key.
-     * Terminated by a NULL config key. Note that some backends do not support
-     * the configuration interface, so this pointer may just contain NULL.
-     * Mandatory if the backend sets VD_CAP_CONFIG.
-     */
-    PCVDCONFIGINFO paConfigInfo;
-
-    /**
-     * Handle of loaded plugin library, NIL_RTLDRMOD for static backends.
-     */
-    RTLDRMOD hPlugin;
-
-    /**
-     * Probes the given image.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnProbe, (const char *pszFilename, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage));
-
-    /**
-     * Open a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file to open. Guaranteed to be available and
-     *                          unchanged during the lifetime of this image.
-     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   ppBackendData   Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnOpen, (const char *pszFilename, unsigned uOpenFlags,
-                                        PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                                        void **ppBackendData));
-
-    /**
-     * Create a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file to create. Guaranteed to be available and
-     *                          unchanged during the lifetime of this image.
-     * @param   cbSize          Image size in bytes.
-     * @param   uImageFlags     Flags specifying special image features.
-     * @param   pszComment      Pointer to image comment. NULL is ok.
-     * @param   pUuid           New UUID of the image. Not NULL.
-     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
-     * @param   uPercentStart   Starting value for progress percentage.
-     * @param   uPercentSpan    Span for varying progress percentage.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
-     * @param   ppBackendData   Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnCreate, (const char *pszFilename, uint64_t cbSize,
-                                          unsigned uImageFlags, const char *pszComment,
-                                          PCRTUUID pUuid, unsigned uOpenFlags,
-                                          unsigned uPercentStart, unsigned uPercentSpan,
-                                          PVDINTERFACE pVDIfsDisk,
-                                          PVDINTERFACE pVDIfsImage,
-                                          PVDINTERFACE pVDIfsOperation,
-                                          void **ppBackendData));
-
-    /**
-     * Close a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   fDelete         If true, delete the image from the host disk.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pBackendData, bool fDelete));
-
-    /**
-     * Read data from a cache image. The area read never crosses a block
-     * boundary.
-     *
-     * @returns VBox status code.
-     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         Offset to start reading from.
-     * @param   pvBuf           Where to store the read bits.
-     * @param   cbRead          Number of bytes to read.
-     * @param   pcbActuallyRead Pointer to returned number of bytes read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnRead, (void *pBackendData, uint64_t uOffset, void *pvBuf,
-                                        size_t cbRead, size_t *pcbActuallyRead));
-
-    /**
-     * Write data to a cache image. The area written never crosses a block
-     * boundary.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         Offset to start writing to.
-     * @param   pvBuf           Where to retrieve the written bits.
-     * @param   cbWrite         Number of bytes to write.
-     * @param   pcbWriteProcess Pointer to returned number of bytes that could
-     *                          be processed.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWrite, (void *pBackendData, uint64_t uOffset,
-                                         const void *pvBuf, size_t cbWrite,
-                                         size_t *pcbWriteProcess));
-
-    /**
-     * Flush data to disk.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlush, (void *pBackendData));
-
-    /**
-     * Get the version of a cache image.
-     *
-     * @returns version of cache image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetVersion, (void *pBackendData));
-
-    /**
-     * Get the capacity of a cache image.
-     *
-     * @returns size of cache image in bytes.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(uint64_t, pfnGetSize, (void *pBackendData));
-
-    /**
-     * Get the file size of a cache image.
-     *
-     * @returns size of cache image in bytes.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(uint64_t, pfnGetFileSize, (void *pBackendData));
-
-    /**
-     * Get the image flags of a cache image.
-     *
-     * @returns image flags of cache image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetImageFlags, (void *pBackendData));
-
-    /**
-     * Get the open flags of a cache image.
-     *
-     * @returns open flags of cache image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetOpenFlags, (void *pBackendData));
-
-    /**
-     * Set the open flags of a cache image. May cause the image to be locked
-     * in a different mode or be reopened (which can fail).
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOpenFlags      New open flags for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetOpenFlags, (void *pBackendData, unsigned uOpenFlags));
-
-    /**
-     * Get comment of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pszComment      Where to store the comment.
-     * @param   cbComment       Size of the comment buffer.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetComment, (void *pBackendData, char *pszComment, size_t cbComment));
-
-    /**
-     * Set comment of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pszComment      Where to get the comment from. NULL resets comment.
-     *                          The comment is silently truncated if the image format
-     *                          limit is exceeded.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetComment, (void *pBackendData, const char *pszComment));
-
-    /**
-     * Get UUID of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the image UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set UUID of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the image UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Get last modification UUID of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the image modification UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetModificationUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set last modification UUID of a cache image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the image modification UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetModificationUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Dump information about a cache image.
-     *
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(void, pfnDump, (void *pBackendData));
-
-    /**
-     * Start an asynchronous read request.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         The offset of the virtual disk to read from.
-     * @param   cbRead          How many bytes to read.
-     * @param   pIoCtx          I/O context associated with this request.
-     * @param   pcbActuallyRead Pointer to returned number of bytes read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncRead, (void *pBackendData, uint64_t uOffset, size_t cbRead,
-                                             PVDIOCTX pIoCtx, size_t *pcbActuallyRead));
-
-    /**
-     * Start an asynchronous write request.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         The offset of the virtual disk to write to.
-     * @param   cbWrite         How many bytes to write.
-     * @param   pIoCtx          I/O context associated with this request.
-     * @param   pcbWriteProcess Pointer to returned number of bytes that could
-     *                          be processed. In case the function returned
-     *                          VERR_VD_BLOCK_FREE this is the number of bytes
-     *                          that could be written in a full block write,
-     *                          when prefixed/postfixed by the appropriate
-     *                          amount of (previously read) padding data.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncWrite, (void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                                              PVDIOCTX pIoCtx, size_t *pcbWriteProcess));
-
-    /**
-     * Flush data to disk.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pIoCtx          I/O context associated with this request.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncFlush, (void *pBackendData, PVDIOCTX pIoCtx));
-
-    /** Returns a human readable hard disk location string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the full file path for image-based hard disks.
-     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
-
-    /** Returns a human readable hard disk name string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the file name part in the full file path for
-     *  image-based hard disks. Mandatory for backends with no
-     *  VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
-
-} VDCACHEBACKEND;
-
-/** Pointer to VD backend. */
-typedef VDCACHEBACKEND *PVDCACHEBACKEND;
-
-/** Constant pointer to VD backend. */
-typedef const VDCACHEBACKEND *PCVDCACHEBACKEND;
-
-/** Initialization entry point. */
-typedef DECLCALLBACK(int) FNVDCACHEFORMATLOAD(PVDCACHEBACKEND *ppBackendTable);
-typedef FNVDCACHEFORMATLOAD *PFNVDCACHEFORMATLOAD;
-#define VD_CACHEFORMAT_LOAD_NAME "VDCacheFormatLoad"
-
-/** The prefix to identify Storage Plugins. */
-#define VD_CACHEFORMAT_PLUGIN_PREFIX "VDCache"
-/** The size of the prefix excluding the '\\0' terminator. */
-#define VD_CACHEFORMAT_PLUGIN_PREFIX_LENGTH (sizeof(VD_CACHEFORMAT_PLUGIN_PREFIX)-1)
-
-#endif
Index: unk/include/VBox/VBoxHDD-Plugin.h
===================================================================
--- /trunk/include/VBox/VBoxHDD-Plugin.h	(revision 33566)
+++ 	(revision )
@@ -1,601 +1,0 @@
-/** @file
- * Internal hard disk format support API for VBoxHDD.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- * The contents of this file may alternatively be used under the terms
- * of the Common Development and Distribution License Version 1.0
- * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
- * VirtualBox OSE distribution, in which case the provisions of the
- * CDDL are applicable instead of those of the GPL.
- *
- * You may elect to license modified versions of this file under the
- * terms and conditions of either the GPL or the CDDL or both.
- */
-
-#ifndef __VBoxHDD_Plugin_h__
-#define __VBoxHDD_Plugin_h__
-
-#include <VBox/VBoxHDD.h>
-
-
-/** @name VBox HDD backend write flags
- * @{
- */
-/** Do not allocate a new block on this write. This is just an advisory
- * flag. The backend may still decide in some circumstances that it wants
- * to ignore this flag (which may cause extra dynamic image expansion). */
-#define VD_WRITE_NO_ALLOC   RT_BIT(1)
-/** @}*/
-
-
-/**
- * Image format backend interface used by VBox HDD Container implementation.
- */
-typedef struct VBOXHDDBACKEND
-{
-    /**
-     * The name of the backend (constant string).
-     */
-    const char *pszBackendName;
-
-    /**
-     * The size of the structure.
-     */
-    uint32_t cbSize;
-
-    /**
-     * The capabilities of the backend.
-     */
-    uint64_t uBackendCaps;
-
-    /**
-     * Pointer to a NULL-terminated array, containing the supported
-     * file extensions. Note that some backends do not work on files, so this
-     * pointer may just contain NULL.
-     */
-    PCVDFILEEXTENSION paFileExtensions;
-
-    /**
-     * Pointer to an array of structs describing each supported config key.
-     * Terminated by a NULL config key. Note that some backends do not support
-     * the configuration interface, so this pointer may just contain NULL.
-     * Mandatory if the backend sets VD_CAP_CONFIG.
-     */
-    PCVDCONFIGINFO paConfigInfo;
-
-    /**
-     * Handle of loaded plugin library, NIL_RTLDRMOD for static backends.
-     */
-    RTLDRMOD hPlugin;
-
-    /**
-     * Check if a file is valid for the backend.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   penmType        Returns the supported device type on success.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnCheckIfValid, (const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                                                PVDINTERFACE pVDIfsImage, VDTYPE *penmType));
-
-    /**
-     * Open a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file to open. Guaranteed to be available and
-     *                          unchanged during the lifetime of this image.
-     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   enmType         Requested type of the image.
-     * @param   ppBackendData   Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnOpen, (const char *pszFilename, unsigned uOpenFlags,
-                                        PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                                        VDTYPE enmType, void **ppBackendData));
-
-    /**
-     * Create a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pszFilename     Name of the image file to create. Guaranteed to be available and
-     *                          unchanged during the lifetime of this image.
-     * @param   cbSize          Image size in bytes.
-     * @param   uImageFlags     Flags specifying special image features.
-     * @param   pszComment      Pointer to image comment. NULL is ok.
-     * @param   pPCHSGeometry   Physical drive geometry CHS <= (16383,16,255).
-     * @param   pLCHSGeometry   Logical drive geometry CHS <= (1024,255,63).
-     * @param   pUuid           New UUID of the image. Not NULL.
-     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
-     * @param   uPercentStart   Starting value for progress percentage.
-     * @param   uPercentSpan    Span for varying progress percentage.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
-     * @param   ppBackendData   Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnCreate, (const char *pszFilename, uint64_t cbSize,
-                                          unsigned uImageFlags, const char *pszComment,
-                                          PCVDGEOMETRY pPCHSGeometry,
-                                          PCVDGEOMETRY pLCHSGeometry,
-                                          PCRTUUID pUuid, unsigned uOpenFlags,
-                                          unsigned uPercentStart, unsigned uPercentSpan,
-                                          PVDINTERFACE pVDIfsDisk,
-                                          PVDINTERFACE pVDIfsImage,
-                                          PVDINTERFACE pVDIfsOperation,
-                                          void **ppBackendData));
-
-    /**
-     * Rename a disk image. Only needs to work as long as the operating
-     * system's rename file functionality is usable. If an attempt is made to
-     * rename an image to a location on another disk/filesystem, this function
-     * may just fail with an appropriate error code (not changing the opened
-     * image data at all). Also works only on images which actually refer to
-     * regular files. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pszFilename     New name of the image file. Guaranteed to be available and
-     *                          unchanged during the lifetime of this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnRename, (void *pBackendData, const char *pszFilename));
-
-    /**
-     * Close a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   fDelete         If true, delete the image from the host disk.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pBackendData, bool fDelete));
-
-    /**
-     * Read data from a disk image. The area read never crosses a block
-     * boundary.
-     *
-     * @returns VBox status code.
-     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         Offset to start reading from.
-     * @param   pvBuf           Where to store the read bits.
-     * @param   cbRead          Number of bytes to read.
-     * @param   pcbActuallyRead Pointer to returned number of bytes read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnRead, (void *pBackendData, uint64_t uOffset, void *pvBuf,
-                                        size_t cbRead, size_t *pcbActuallyRead));
-
-    /**
-     * Write data to a disk image. The area written never crosses a block
-     * boundary.
-     *
-     * @returns VBox status code.
-     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block and
-     *          this is not a full-block write. The write must be repeated with
-     *          the correct amount of prefix/postfix data read from the images below
-     *          in the image stack. This might not be the most convenient interface,
-     *          but it works with arbitrary block sizes, especially when the image
-     *          stack uses different block sizes.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         Offset to start writing to.
-     * @param   pvBuf           Where to retrieve the written bits.
-     * @param   cbWrite         Number of bytes to write.
-     * @param   pcbWriteProcess Pointer to returned number of bytes that could
-     *                          be processed. In case the function returned
-     *                          VERR_VD_BLOCK_FREE this is the number of bytes
-     *                          that could be written in a full block write,
-     *                          when prefixed/postfixed by the appropriate
-     *                          amount of (previously read) padding data.
-     * @param   pcbPreRead      Pointer to the returned amount of data that must
-     *                          be prefixed to perform a full block write.
-     * @param   pcbPostRead     Pointer to the returned amount of data that must
-     *                          be postfixed to perform a full block write.
-     * @param   fWrite          Flags which affect write behavior. Combination
-     *                          of the VD_WRITE_* flags.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWrite, (void *pBackendData, uint64_t uOffset,
-                                         const void *pvBuf, size_t cbWrite,
-                                         size_t *pcbWriteProcess, size_t *pcbPreRead,
-                                         size_t *pcbPostRead, unsigned fWrite));
-
-    /**
-     * Flush data to disk.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlush, (void *pBackendData));
-
-    /**
-     * Get the version of a disk image.
-     *
-     * @returns version of disk image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetVersion, (void *pBackendData));
-
-    /**
-     * Get the capacity of a disk image.
-     *
-     * @returns size of disk image in bytes.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(uint64_t, pfnGetSize, (void *pBackendData));
-
-    /**
-     * Get the file size of a disk image.
-     *
-     * @returns size of disk image in bytes.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(uint64_t, pfnGetFileSize, (void *pBackendData));
-
-    /**
-     * Get virtual disk PCHS geometry stored in a disk image.
-     *
-     * @returns VBox status code.
-     * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the image.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pPCHSGeometry   Where to store the geometry. Not NULL.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetPCHSGeometry, (void *pBackendData, PVDGEOMETRY pPCHSGeometry));
-
-    /**
-     * Set virtual disk PCHS geometry stored in a disk image.
-     * Only called if geometry is different than before.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pPCHSGeometry   Where to load the geometry from. Not NULL.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetPCHSGeometry, (void *pBackendData, PCVDGEOMETRY pPCHSGeometry));
-
-    /**
-     * Get virtual disk LCHS geometry stored in a disk image.
-     *
-     * @returns VBox status code.
-     * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the image.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pLCHSGeometry   Where to store the geometry. Not NULL.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetLCHSGeometry, (void *pBackendData,  PVDGEOMETRY pLCHSGeometry));
-
-    /**
-     * Set virtual disk LCHS geometry stored in a disk image.
-     * Only called if geometry is different than before.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pLCHSGeometry   Where to load the geometry from. Not NULL.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetLCHSGeometry, (void *pBackendData,  PCVDGEOMETRY pLCHSGeometry));
-
-    /**
-     * Get the image flags of a disk image.
-     *
-     * @returns image flags of disk image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetImageFlags, (void *pBackendData));
-
-    /**
-     * Get the open flags of a disk image.
-     *
-     * @returns open flags of disk image.
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(unsigned, pfnGetOpenFlags, (void *pBackendData));
-
-    /**
-     * Set the open flags of a disk image. May cause the image to be locked
-     * in a different mode or be reopened (which can fail).
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOpenFlags      New open flags for this image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetOpenFlags, (void *pBackendData, unsigned uOpenFlags));
-
-    /**
-     * Get comment of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pszComment      Where to store the comment.
-     * @param   cbComment       Size of the comment buffer.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetComment, (void *pBackendData, char *pszComment, size_t cbComment));
-
-    /**
-     * Set comment of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pszComment      Where to get the comment from. NULL resets comment.
-     *                          The comment is silently truncated if the image format
-     *                          limit is exceeded.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetComment, (void *pBackendData, const char *pszComment));
-
-    /**
-     * Get UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the image UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the image UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Get last modification UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the image modification UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetModificationUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set last modification UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the image modification UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetModificationUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Get parent UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the parent image UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetParentUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set parent UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the parent image UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetParentUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Get parent modification UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to store the parent image modification UUID.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetParentModificationUuid, (void *pBackendData, PRTUUID pUuid));
-
-    /**
-     * Set parent modification UUID of a disk image.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pUuid           Where to get the parent image modification UUID from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetParentModificationUuid, (void *pBackendData, PCRTUUID pUuid));
-
-    /**
-     * Dump information about a disk image.
-     *
-     * @param   pBackendData    Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(void, pfnDump, (void *pBackendData));
-
-    /**
-     * Get a time stamp of a disk image. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pTimeStamp      Where to store the time stamp.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetTimeStamp, (void *pBackendData, PRTTIMESPEC pTimeStamp));
-
-    /**
-     * Get the parent time stamp of a disk image. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pTimeStamp      Where to store the time stamp.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetParentTimeStamp, (void *pBackendData, PRTTIMESPEC pTimeStamp));
-
-    /**
-     * Set the parent time stamp of a disk image. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pTimeStamp      Where to get the time stamp from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetParentTimeStamp, (void *pBackendData, PCRTTIMESPEC pTimeStamp));
-
-    /**
-     * Get the relative path to parent image. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData      Opaque state data for this image.
-     * @param   pszParentFilename Where to store the path.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetParentFilename, (void *pBackendData, char **ppszParentFilename));
-
-    /**
-     * Set the relative path to parent image. May be NULL.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData      Opaque state data for this image.
-     * @param   pszParentFilename Where to get the path from.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetParentFilename, (void *pBackendData, const char *pszParentFilename));
-
-    /**
-     * Return whether asynchronous I/O operations are supported for this image.
-     *
-     * @returns true if asynchronous I/O is supported
-     *          false otherwise.
-     * @param   pBackendData     Opaque state data for this image.
-     */
-    DECLR3CALLBACKMEMBER(bool, pfnIsAsyncIOSupported, (void *pBackendData));
-
-    /**
-     * Start an asynchronous read request.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         The offset of the virtual disk to read from.
-     * @param   cbRead          How many bytes to read.
-     * @param   pIoCtx          I/O context associated with this request.
-     * @param   pcbActuallyRead Pointer to returned number of bytes read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncRead, (void *pBackendData, uint64_t uOffset, size_t cbRead,
-                                             PVDIOCTX pIoCtx, size_t *pcbActuallyRead));
-
-    /**
-     * Start an asynchronous write request.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uOffset         The offset of the virtual disk to write to.
-     * @param   cbWrite         How many bytes to write.
-     * @param   pIoCtx          I/O context associated with this request.
-     * @param   pcbWriteProcess Pointer to returned number of bytes that could
-     *                          be processed. In case the function returned
-     *                          VERR_VD_BLOCK_FREE this is the number of bytes
-     *                          that could be written in a full block write,
-     *                          when prefixed/postfixed by the appropriate
-     *                          amount of (previously read) padding data.
-     * @param   pcbPreRead      Pointer to the returned amount of data that must
-     *                          be prefixed to perform a full block write.
-     * @param   pcbPostRead     Pointer to the returned amount of data that must
-     *                          be postfixed to perform a full block write.
-     * @param   fWrite          Flags which affect write behavior. Combination
-     *                          of the VD_WRITE_* flags.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncWrite, (void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                                              PVDIOCTX pIoCtx,
-                                              size_t *pcbWriteProcess, size_t *pcbPreRead,
-                                              size_t *pcbPostRead, unsigned fWrite));
-
-    /**
-     * Flush data to disk.
-     *
-     * @returns VBox status code.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   pIoCtx          I/O context associated with this request.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnAsyncFlush, (void *pBackendData, PVDIOCTX pIoCtx));
-
-    /** Returns a human readable hard disk location string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the full file path for image-based hard disks.
-     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
-
-    /** Returns a human readable hard disk name string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the file name part in the full file path for
-     *  image-based hard disks. Mandatory for backends with no
-     *  VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
-
-    /**
-     * Compact the image. The pointer may be NULL, indicating that this
-     * isn't supported yet (for file-based images) or not necessary.
-     *
-     * @returns VBox status code.
-     * @returns VERR_NOT_SUPPORTED if this image cannot be compacted yet.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   uPercentStart   Starting value for progress percentage.
-     * @param   uPercentSpan    Span for varying progress percentage.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnCompact, (void *pBackendData,
-                                           unsigned uPercentStart, unsigned uPercentSpan,
-                                           PVDINTERFACE pVDIfsDisk,
-                                           PVDINTERFACE pVDIfsImage,
-                                           PVDINTERFACE pVDIfsOperation));
-
-    /**
-     * Resize the image. The pointer may be NULL, indicating that this
-     * isn't supported yet (for file-based images) or not necessary.
-     *
-     * @returns VBox status code.
-     * @returns VERR_NOT_SUPPORTED if this image cannot be compacted yet.
-     * @param   pBackendData    Opaque state data for this image.
-     * @param   cbSize          New size of the image.
-     * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
-     * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
-     * @param   uPercentStart   Starting value for progress percentage.
-     * @param   uPercentSpan    Span for varying progress percentage.
-     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
-     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
-     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnResize, (void *pBackendData,
-                                          uint64_t cbSize,
-                                          PCVDGEOMETRY pPCHSGeometry,
-                                          PCVDGEOMETRY pLCHSGeometry,
-                                          unsigned uPercentStart, unsigned uPercentSpan,
-                                          PVDINTERFACE pVDIfsDisk,
-                                          PVDINTERFACE pVDIfsImage,
-                                          PVDINTERFACE pVDIfsOperation));
-
-} VBOXHDDBACKEND;
-
-/** Pointer to VD backend. */
-typedef VBOXHDDBACKEND *PVBOXHDDBACKEND;
-
-/** Constant pointer to VD backend. */
-typedef const VBOXHDDBACKEND *PCVBOXHDDBACKEND;
-
-/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
-DECLINLINE(int) genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
-{
-    *pszLocation = NULL;
-    return VINF_SUCCESS;
-}
-/** @copydoc VBOXHDDBACKEND::pfnComposeName */
-DECLINLINE(int) genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
-{
-    *pszName = NULL;
-    return VINF_SUCCESS;
-}
-
-/** Initialization entry point. */
-typedef DECLCALLBACK(int) VBOXHDDFORMATLOAD(PVBOXHDDBACKEND *ppBackendTable);
-typedef VBOXHDDFORMATLOAD *PFNVBOXHDDFORMATLOAD;
-#define VBOX_HDDFORMAT_LOAD_NAME "VBoxHDDFormatLoad"
-
-/** The prefix to identify Storage Plugins. */
-#define VBOX_HDDFORMAT_PLUGIN_PREFIX "VBoxHDD"
-/** The size of the prefix excluding the '\\0' terminator. */
-#define VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH (sizeof(VBOX_HDDFORMAT_PLUGIN_PREFIX)-1)
-
-#endif
Index: unk/include/VBox/VBoxHDD.h
===================================================================
--- /trunk/include/VBox/VBoxHDD.h	(revision 33566)
+++ 	(revision )
@@ -1,2674 +1,0 @@
-/** @file
- * VBox HDD Container API.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- * The contents of this file may alternatively be used under the terms
- * of the Common Development and Distribution License Version 1.0
- * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
- * VirtualBox OSE distribution, in which case the provisions of the
- * CDDL are applicable instead of those of the GPL.
- *
- * You may elect to license modified versions of this file under the
- * terms and conditions of either the GPL or the CDDL or both.
- */
-
-#ifndef ___VBox_VD_h
-#define ___VBox_VD_h
-
-#include <iprt/assert.h>
-#include <iprt/string.h>
-#include <iprt/mem.h>
-#include <iprt/file.h>
-#include <iprt/net.h>
-#include <iprt/sg.h>
-#include <VBox/cdefs.h>
-#include <VBox/types.h>
-#include <VBox/err.h>
-
-RT_C_DECLS_BEGIN
-
-#ifdef IN_RING0
-# error "There are no VBox HDD Container APIs available in Ring-0 Host Context!"
-#endif
-
-/** @defgroup grp_vd            VBox HDD Container
- * @{
- */
-
-/** Current VMDK image version. */
-#define VMDK_IMAGE_VERSION          (0x0001)
-
-/** 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)
-
-/** Placeholder for specifying the last opened image. */
-#define VD_LAST_IMAGE               0xffffffffU
-
-/** @name VBox HDD container image flags
- * @{
- */
-/** No flags. */
-#define VD_IMAGE_FLAGS_NONE                     (0)
-/** Fixed image. */
-#define VD_IMAGE_FLAGS_FIXED                    (0x10000)
-/** Diff image. Mutually exclusive with fixed image. */
-#define VD_IMAGE_FLAGS_DIFF                     (0x20000)
-/** 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)
-/** VMDK: stream optimized image, read only. */
-#define VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED    (0x0004)
-/** VMDK: ESX variant, use in addition to other flags. */
-#define VD_VMDK_IMAGE_FLAGS_ESX                 (0x0008)
-/** 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_FIXED | VD_IMAGE_FLAGS_DIFF | VD_IMAGE_FLAGS_NONE \
-                                             |  VD_VMDK_IMAGE_FLAGS_SPLIT_2G | VD_VMDK_IMAGE_FLAGS_RAWDISK \
-                                             | VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_VMDK_IMAGE_FLAGS_ESX)
-
-/** Mask of valid image flags for VDI. */
-#define VD_VDI_IMAGE_FLAGS_MASK             (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF | VD_IMAGE_FLAGS_NONE | VD_VDI_IMAGE_FLAGS_ZERO_EXPAND)
-
-/** Mask of all valid image flags for all formats. */
-#define VD_IMAGE_FLAGS_MASK                 (VD_VMDK_IMAGE_FLAGS_MASK | VD_VDI_IMAGE_FLAGS_MASK)
-
-/** Default image flags. */
-#define VD_IMAGE_FLAGS_DEFAULT              (VD_IMAGE_FLAGS_NONE)
-/** @} */
-
-
-/**
- * Auxiliary type for describing partitions on raw disks. The entries must be
- * in ascending order (as far as uStart is concerned), and must not overlap.
- * Note that this does not correspond 1:1 to partitions, it is describing the
- * general meaning of contiguous areas on the disk.
- */
-typedef struct VBOXHDDRAWPARTDESC
-{
-    /** Device to use for this partition/data area. Can be the disk device if
-     * the offset field is set appropriately. If this is NULL, then this
-     * partition will not be accessible to the guest. The size of the data area
-     * must still be set correctly. */
-    const char      *pszRawDevice;
-    /** Pointer to the partitioning info. NULL means this is a regular data
-     * area on disk, non-NULL denotes data which should be copied to the
-     * partition data overlay. */
-    const void      *pvPartitionData;
-    /** Offset where the data starts in this device. */
-    uint64_t        uStartOffset;
-    /** Offset where the data starts in the disk. */
-    uint64_t        uStart;
-    /** Size of the data area. */
-    uint64_t        cbData;
-} VBOXHDDRAWPARTDESC, *PVBOXHDDRAWPARTDESC;
-
-/**
- * Auxiliary data structure for creating raw disks.
- */
-typedef struct VBOXHDDRAW
-{
-    /** Signature for structure. Must be 'R', 'A', 'W', '\\0'. Actually a trick
-     * to make logging of the comment string produce sensible results. */
-    char            szSignature[4];
-    /** Flag whether access to full disk should be given (ignoring the
-     * partition information below). */
-    bool            fRawDisk;
-    /** Filename for the raw disk. Ignored for partitioned raw disks.
-     * For Linux e.g. /dev/sda, and for Windows e.g. \\\\.\\PhysicalDisk0. */
-    const char      *pszRawDisk;
-    /** Number of entries in the partition descriptor array. */
-    unsigned        cPartDescs;
-    /** Pointer to the partition descriptor array. */
-    PVBOXHDDRAWPARTDESC pPartDescs;
-} VBOXHDDRAW, *PVBOXHDDRAW;
-
-/** @name VBox HDD container image open mode flags
- * @{
- */
-/** Try to open image in read/write exclusive access mode if possible, or in read-only elsewhere. */
-#define VD_OPEN_FLAGS_NORMAL        0
-/** Open image in read-only mode with sharing access with others. */
-#define VD_OPEN_FLAGS_READONLY      RT_BIT(0)
-/** Honor zero block writes instead of ignoring them whenever possible.
- * This is not supported by all formats. It is silently ignored in this case. */
-#define VD_OPEN_FLAGS_HONOR_ZEROES  RT_BIT(1)
-/** Honor writes of the same data instead of ignoring whenever possible.
- * This is handled generically, and is only meaningful for differential image
- * formats. It is silently ignored otherwise. */
-#define VD_OPEN_FLAGS_HONOR_SAME    RT_BIT(2)
-/** Do not perform the base/diff image check on open. This does NOT imply
- * opening the image as readonly (would break e.g. adding UUIDs to VMDK files
- * created by other products). Images opened with this flag should only be
- * used for querying information, and nothing else. */
-#define VD_OPEN_FLAGS_INFO          RT_BIT(3)
-/** Open image for asynchronous access. Only available if VD_CAP_ASYNC_IO is
- * set. Check with VDIsAsynchonousIoSupported whether asynchronous I/O is
- * really supported for this file.  */
-#define VD_OPEN_FLAGS_ASYNC_IO      RT_BIT(4)
-/** Allow sharing of the image for writable images. May be ignored if the
- * format backend doesn't support this type of concurrent access. */
-#define VD_OPEN_FLAGS_SHAREABLE     RT_BIT(5)
-/** Ask the backend to switch to sequential accesses if possible. Opening
- * will not fail if it cannot do this, the flag will be simply ignored. */
-#define VD_OPEN_FLAGS_SEQUENTIAL    RT_BIT(6)
-/** Mask of valid flags. */
-#define VD_OPEN_FLAGS_MASK          (VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_HONOR_ZEROES | VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)
-/** @}*/
-
-/**
- * Helper functions to handle open flags.
- */
-
-/**
- * Translate VD_OPEN_FLAGS_* to RTFile open flags.
- *
- * @return  RTFile open flags.
- * @param   uOpenFlags      VD_OPEN_FLAGS_* open flags.
- * @param   fCreate         Flag that the file should be created.
- */
-DECLINLINE(uint32_t) VDOpenFlagsToFileOpenFlags(unsigned uOpenFlags, bool fCreate)
-{
-    AssertMsg(!((uOpenFlags & VD_OPEN_FLAGS_READONLY) && fCreate), ("Image can't be opened readonly while being created\n"));
-
-    uint32_t fOpen = 0;
-
-    if (RT_UNLIKELY(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        fOpen |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
-    else
-    {
-        fOpen |= RTFILE_O_READWRITE;
-
-        if (RT_UNLIKELY(uOpenFlags & VD_OPEN_FLAGS_SHAREABLE))
-            fOpen |= RTFILE_O_DENY_NONE;
-        else
-            fOpen |= RTFILE_O_DENY_WRITE;
-    }
-
-    if (RT_UNLIKELY(fCreate))
-        fOpen |= RTFILE_O_CREATE | RTFILE_O_NOT_CONTENT_INDEXED;
-    else
-        fOpen |= RTFILE_O_OPEN;
-
-    return fOpen;
-}
-
-
-/** @name VBox HDD container backend capability flags
- * @{
- */
-/** Supports UUIDs as expected by VirtualBox code. */
-#define VD_CAP_UUID                 RT_BIT(0)
-/** Supports creating fixed size images, allocating all space instantly. */
-#define VD_CAP_CREATE_FIXED         RT_BIT(1)
-/** Supports creating dynamically growing images, allocating space on demand. */
-#define VD_CAP_CREATE_DYNAMIC       RT_BIT(2)
-/** Supports creating images split in chunks of a bit less than 2GBytes. */
-#define VD_CAP_CREATE_SPLIT_2G      RT_BIT(3)
-/** Supports being used as differencing image format backend. */
-#define VD_CAP_DIFF                 RT_BIT(4)
-/** Supports asynchronous I/O operations for at least some configurations. */
-#define VD_CAP_ASYNC                RT_BIT(5)
-/** The backend operates on files. The caller needs to know to handle the
- * location appropriately. */
-#define VD_CAP_FILE                 RT_BIT(6)
-/** The backend uses the config interface. The caller needs to know how to
- * provide the mandatory configuration parts this way. */
-#define VD_CAP_CONFIG               RT_BIT(7)
-/** The backend uses the network stack interface. The caller has to provide
- * the appropriate interface. */
-#define VD_CAP_TCPNET               RT_BIT(8)
-/** The backend supports VFS (virtual filesystem) functionality since it uses
- * VDINTERFACEIO exclusively for all file operations. */
-#define VD_CAP_VFS                  RT_BIT(9)
-/** @}*/
-
-/** @name VBox HDD container type.
- * @{
- */
-typedef enum VDTYPE
-{
-    /** Invalid. */
-    VDTYPE_INVALID = 0,
-    /** HardDisk */
-    VDTYPE_HDD,
-    /** CD/DVD */
-    VDTYPE_DVD,
-    /** Floppy. */
-    VDTYPE_FLOPPY
-} VDTYPE;
-/** @}*/
-
-/**
- * Supported interface types.
- */
-typedef enum VDINTERFACETYPE
-{
-    /** First valid interface. */
-    VDINTERFACETYPE_FIRST = 0,
-    /** Interface to pass error message to upper layers. Per-disk. */
-    VDINTERFACETYPE_ERROR = VDINTERFACETYPE_FIRST,
-    /** Interface for I/O operations. Per-image. */
-    VDINTERFACETYPE_IO,
-    /** Interface for progress notification. Per-operation. */
-    VDINTERFACETYPE_PROGRESS,
-    /** Interface for configuration information. Per-image. */
-    VDINTERFACETYPE_CONFIG,
-    /** Interface for TCP network stack. Per-image. */
-    VDINTERFACETYPE_TCPNET,
-    /** Interface for getting parent image state. Per-operation. */
-    VDINTERFACETYPE_PARENTSTATE,
-    /** Interface for synchronizing accesses from several threads. Per-disk. */
-    VDINTERFACETYPE_THREADSYNC,
-    /** Interface for I/O between the generic VBoxHDD code and the backend. Per-image (internal).
-     * This interface is completely internal and must not be used elsewhere. */
-    VDINTERFACETYPE_IOINT,
-    /** invalid interface. */
-    VDINTERFACETYPE_INVALID
-} VDINTERFACETYPE;
-
-/**
- * Common structure for all interfaces.
- */
-typedef struct VDINTERFACE
-{
-    /** Human readable interface name. */
-    const char         *pszInterfaceName;
-    /** The size of the struct. */
-    uint32_t            cbSize;
-    /** Pointer to the next common interface structure. */
-    struct VDINTERFACE *pNext;
-    /** Interface type. */
-    VDINTERFACETYPE     enmInterface;
-    /** Opaque user data which is passed on every call. */
-    void               *pvUser;
-    /** Pointer to the function call table of the interface.
-     *  As this is opaque this must be casted to the right interface
-     *  struct defined below based on the interface type in enmInterface. */
-    void               *pCallbacks;
-} VDINTERFACE;
-/** Pointer to a VDINTERFACE. */
-typedef VDINTERFACE *PVDINTERFACE;
-/** Pointer to a const VDINTERFACE. */
-typedef const VDINTERFACE *PCVDINTERFACE;
-
-/**
- * Helper functions to handle interface lists.
- *
- * @note These interface lists are used consistently to pass per-disk,
- * per-image and/or per-operation callbacks. Those three purposes are strictly
- * separate. See the individual interface declarations for what context they
- * apply to. The caller is responsible for ensuring that the lifetime of the
- * interface descriptors is appropriate for the category of interface.
- */
-
-/**
- * Get a specific interface from a list of interfaces specified by the type.
- *
- * @return  Pointer to the matching interface or NULL if none was found.
- * @param   pVDIfs          Pointer to the VD interface list.
- * @param   enmInterface    Interface to search for.
- */
-DECLINLINE(PVDINTERFACE) VDInterfaceGet(PVDINTERFACE pVDIfs, VDINTERFACETYPE enmInterface)
-{
-    AssertMsgReturn(   enmInterface >= VDINTERFACETYPE_FIRST
-                    && enmInterface < VDINTERFACETYPE_INVALID,
-                    ("enmInterface=%u", enmInterface), NULL);
-
-    while (pVDIfs)
-    {
-        /* Sanity checks. */
-        AssertMsgBreak(pVDIfs->cbSize == sizeof(VDINTERFACE),
-                       ("cbSize=%u\n", pVDIfs->cbSize));
-
-        if (pVDIfs->enmInterface == enmInterface)
-            return pVDIfs;
-        pVDIfs = pVDIfs->pNext;
-    }
-
-    /* No matching interface was found. */
-    return NULL;
-}
-
-/**
- * Add an interface to a list of interfaces.
- *
- * @return VBox status code.
- * @param  pInterface   Pointer to an unitialized common interface structure.
- * @param  pszName      Name of the interface.
- * @param  enmInterface Type of the interface.
- * @param  pCallbacks   The callback table of the interface.
- * @param  pvUser       Opaque user data passed on every function call.
- * @param  ppVDIfs      Pointer to the VD interface list.
- */
-DECLINLINE(int) VDInterfaceAdd(PVDINTERFACE pInterface, const char *pszName,
-                               VDINTERFACETYPE enmInterface, void *pCallbacks,
-                               void *pvUser, PVDINTERFACE *ppVDIfs)
-{
-    /* Argument checks. */
-    AssertMsgReturn(   enmInterface >= VDINTERFACETYPE_FIRST
-                    && enmInterface < VDINTERFACETYPE_INVALID,
-                    ("enmInterface=%u", enmInterface), VERR_INVALID_PARAMETER);
-
-    AssertMsgReturn(VALID_PTR(pCallbacks),
-                    ("pCallbacks=%#p", pCallbacks),
-                    VERR_INVALID_PARAMETER);
-
-    AssertMsgReturn(VALID_PTR(ppVDIfs),
-                    ("pInterfaceList=%#p", ppVDIfs),
-                    VERR_INVALID_PARAMETER);
-
-    /* Fill out interface descriptor. */
-    pInterface->cbSize           = sizeof(VDINTERFACE);
-    pInterface->pszInterfaceName = pszName;
-    pInterface->enmInterface     = enmInterface;
-    pInterface->pCallbacks       = pCallbacks;
-    pInterface->pvUser           = pvUser;
-    pInterface->pNext            = *ppVDIfs;
-
-    /* Remember the new start of the list. */
-    *ppVDIfs = pInterface;
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Removes an interface from a list of interfaces.
- *
- * @return VBox status code
- * @param  pInterface   Pointer to an initialized common interface structure to remove.
- * @param  ppVDIfs      Pointer to the VD interface list to remove from.
- */
-DECLINLINE(int) VDInterfaceRemove(PVDINTERFACE pInterface, PVDINTERFACE *ppVDIfs)
-{
-    int rc = VERR_NOT_FOUND;
-
-    /* Argument checks. */
-    AssertMsgReturn(VALID_PTR(pInterface),
-                    ("pInterface=%#p", pInterface),
-                    VERR_INVALID_PARAMETER);
-
-    AssertMsgReturn(VALID_PTR(ppVDIfs),
-                    ("pInterfaceList=%#p", ppVDIfs),
-                    VERR_INVALID_PARAMETER);
-
-    if (*ppVDIfs)
-    {
-        PVDINTERFACE pPrev = NULL;
-        PVDINTERFACE pCurr = *ppVDIfs;
-
-        while (   pCurr
-               && (pCurr != pInterface))
-        {
-            pPrev = pCurr;
-            pCurr = pCurr->pNext;
-        }
-
-        /* First interface */
-        if (!pPrev)
-        {
-            *ppVDIfs = pCurr->pNext;
-            rc = VINF_SUCCESS;
-        }
-        else if (pCurr)
-        {
-            pPrev = pCurr->pNext;
-            rc = VINF_SUCCESS;
-        }
-    }
-
-    return rc;
-}
-
-/**
- * Interface to deliver error messages (and also informational messages)
- * to upper layers.
- *
- * Per-disk interface. Optional, but think twice if you want to miss the
- * opportunity of reporting better human-readable error messages.
- */
-typedef struct VDINTERFACEERROR
-{
-    /**
-     * Size of the error interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Error message callback. Must be able to accept special IPRT format
-     * strings.
-     *
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   rc              The VBox error code.
-     * @param   RT_SRC_POS_DECL Use RT_SRC_POS.
-     * @param   pszFormat       Error message format string.
-     * @param   va              Error message arguments.
-     */
-    DECLR3CALLBACKMEMBER(void, pfnError, (void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va));
-
-    /**
-     * Informational message callback. May be NULL. Used e.g. in
-     * VDDumpImages(). Must be able to accept special IPRT format strings.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pszFormat       Message format string.
-     * @param   va              Message arguments.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnMessage, (void *pvUser, const char *pszFormat, va_list va));
-
-} VDINTERFACEERROR, *PVDINTERFACEERROR;
-
-/**
- * Get error interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACEERROR) VDGetInterfaceError(PVDINTERFACE pInterface)
-{
-    PVDINTERFACEERROR pInterfaceError;
-
-    /* Check that the interface descriptor is a error interface. */
-    AssertMsgReturn(   pInterface->enmInterface == VDINTERFACETYPE_ERROR
-                    && pInterface->cbSize == sizeof(VDINTERFACE),
-                    ("Not an error interface"), NULL);
-
-    pInterfaceError = (PVDINTERFACEERROR)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   pInterfaceError->cbSize == sizeof(VDINTERFACEERROR)
-                    && pInterfaceError->enmInterface == VDINTERFACETYPE_ERROR,
-                    ("A non error callback table attached to a error interface descriptor\n"), NULL);
-
-    return pInterfaceError;
-}
-
-/**
- * Completion callback which is called by the interface owner
- * to inform the backend that a task finished.
- *
- * @return  VBox status code.
- * @param   pvUser          Opaque user data which is passed on request submission.
- * @param   rcReq           Status code of the completed request.
- */
-typedef DECLCALLBACK(int) FNVDCOMPLETED(void *pvUser, int rcReq);
-/** Pointer to FNVDCOMPLETED() */
-typedef FNVDCOMPLETED *PFNVDCOMPLETED;
-
-/**
- * Support interface for I/O
- *
- * Per-image. Optional as input.
- */
-typedef struct VDINTERFACEIO
-{
-    /**
-     * Size of the I/O interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Open callback
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pszLocation     Name of the location to open.
-     * @param   fOpen           Flags for opening the backend.
-     *                          See RTFILE_O_* #defines, inventing another set
-     *                          of open flags is not worth the mapping effort.
-     * @param   pfnCompleted    The callback which is called whenever a task
-     *                          completed. The backend has to pass the user data
-     *                          of the request initiator (ie the one who calls
-     *                          VDAsyncRead or VDAsyncWrite) in pvCompletion
-     *                          if this is NULL.
-     * @param   ppStorage       Where to store the opaque storage handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnOpen, (void *pvUser, const char *pszLocation,
-                                        uint32_t fOpen,
-                                        PFNVDCOMPLETED pfnCompleted,
-                                        void **ppStorage));
-
-    /**
-     * Close callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The opaque storage handle to close.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pvUser, void *pStorage));
-
-    /**
-     * Delete callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of the file to delete.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnDelete, (void *pvUser, const char *pcszFilename));
-
-    /**
-     * Move callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszSrc         The path to the source file.
-     * @param   pcszDst         The path to the destination file.
-     *                          This file will be created.
-     * @param   fMove           A combination of the RTFILEMOVE_* flags.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnMove, (void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove));
-
-    /**
-     * Returns the free space on a disk.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of a file to identify the disk.
-     * @param   pcbFreeSpace    Where to store the free space of the disk.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetFreeSpace, (void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace));
-
-    /**
-     * Returns the last modification timestamp of a file.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of a file to identify the disk.
-     * @param   pModificationTime   Where to store the timestamp of the file.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetModificationTime, (void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime));
-
-    /**
-     * Returns the size of the opened storage backend.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The opaque storage handle to close.
-     * @param   pcbSize         Where to store the size of the storage backend.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetSize, (void *pvUser, void *pStorage, uint64_t *pcbSize));
-
-    /**
-     * Sets the size of the opened storage backend if possible.
-     *
-     * @return  VBox status code.
-     * @retval  VERR_NOT_SUPPORTED if the backend does not support this operation.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The opaque storage handle to close.
-     * @param   cbSize          The new size of the image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetSize, (void *pvUser, void *pStorage, uint64_t cbSize));
-
-    /**
-     * Synchronous write callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to use.
-     * @param   uOffset         The offset to start from.
-     * @param   pvBuffer        Pointer to the bits need to be written.
-     * @param   cbBuffer        How many bytes to write.
-     * @param   pcbWritten      Where to store how many bytes were actually written.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteSync, (void *pvUser, void *pStorage, uint64_t uOffset,
-                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
-
-    /**
-     * Synchronous read callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to use.
-     * @param   uOffset         The offset to start from.
-     * @param   pvBuffer        Where to store the read bits.
-     * @param   cbBuffer        How many bytes to read.
-     * @param   pcbRead         Where to store how many bytes were actually read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadSync, (void *pvUser, void *pStorage, uint64_t uOffset,
-                                            void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
-
-    /**
-     * Flush data to the storage backend.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to flush.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlushSync, (void *pvUser, void *pStorage));
-
-    /**
-     * Initiate a asynchronous read request.
-     *
-     * @return  VBox status code.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        The offset to start reading from.
-     * @param   paSegments     Scatter gather list to store the data in.
-     * @param   cSegments      Number of segments in the list.
-     * @param   cbRead         How many bytes to read.
-     * @param   pvCompletion   The opaque user data which is returned upon completion.
-     * @param   ppTask         Where to store the opaque task handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadAsync, (void *pvUser, void *pStorage, uint64_t uOffset,
-                                             PCRTSGSEG paSegments, size_t cSegments,
-                                             size_t cbRead, void *pvCompletion,
-                                             void **ppTask));
-
-    /**
-     * Initiate a asynchronous write request.
-     *
-     * @return  VBox status code.
-     * @param   pvUser         The opaque user data passed on conatiner creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        The offset to start writing to.
-     * @param   paSegments     Scatter gather list of the data to write
-     * @param   cSegments      Number of segments in the list.
-     * @param   cbWrite        How many bytes to write.
-     * @param   pvCompletion   The opaque user data which is returned upon completion.
-     * @param   ppTask         Where to store the opaque task handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteAsync, (void *pvUser, void *pStorage, uint64_t uOffset,
-                                              PCRTSGSEG paSegments, size_t cSegments,
-                                              size_t cbWrite, void *pvCompletion,
-                                              void **ppTask));
-
-    /**
-     * Initiates a async flush request.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to flush.
-     * @param   pvCompletion    The opaque user data which is returned upon completion.
-     * @param   ppTask          Where to store the opaque task handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlushAsync, (void *pvUser, void *pStorage,
-                                              void *pvCompletion, void **ppTask));
-
-} VDINTERFACEIO, *PVDINTERFACEIO;
-
-/**
- * Get I/O interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACEIO) VDGetInterfaceIO(PVDINTERFACE pInterface)
-{
-    PVDINTERFACEIO pInterfaceIO;
-
-    /* Check that the interface descriptor is an I/O interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_IO)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not an I/O interface"), NULL);
-
-    pInterfaceIO = (PVDINTERFACEIO)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceIO->cbSize == sizeof(VDINTERFACEIO))
-                    && (pInterfaceIO->enmInterface == VDINTERFACETYPE_IO),
-                    ("A non I/O callback table attached to an I/O interface descriptor\n"), NULL);
-
-    return pInterfaceIO;
-}
-
-/**
- * Callback which provides progress information about a currently running
- * lengthy operation.
- *
- * @return  VBox status code.
- * @param   pvUser          The opaque user data associated with this interface.
- * @param   uPercent        Completion percentage.
- */
-typedef DECLCALLBACK(int) FNVDPROGRESS(void *pvUser, unsigned uPercentage);
-/** Pointer to FNVDPROGRESS() */
-typedef FNVDPROGRESS *PFNVDPROGRESS;
-
-/**
- * Progress notification interface
- *
- * Per-operation. Optional.
- */
-typedef struct VDINTERFACEPROGRESS
-{
-    /**
-     * Size of the progress interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Progress notification callbacks.
-     */
-    PFNVDPROGRESS pfnProgress;
-
-} VDINTERFACEPROGRESS, *PVDINTERFACEPROGRESS;
-
-/**
- * Get progress interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACEPROGRESS) VDGetInterfaceProgress(PVDINTERFACE pInterface)
-{
-    PVDINTERFACEPROGRESS pInterfaceProgress;
-
-    /* Check that the interface descriptor is a progress interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_PROGRESS)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not a progress interface"), NULL);
-
-
-    pInterfaceProgress = (PVDINTERFACEPROGRESS)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceProgress->cbSize == sizeof(VDINTERFACEPROGRESS))
-                    && (pInterfaceProgress->enmInterface == VDINTERFACETYPE_PROGRESS),
-                    ("A non progress callback table attached to a progress interface descriptor\n"), NULL);
-
-    return pInterfaceProgress;
-}
-
-
-/**
- * Configuration information interface
- *
- * Per-image. Optional for most backends, but mandatory for images which do
- * not operate on files (including standard block or character devices).
- */
-typedef struct VDINTERFACECONFIG
-{
-    /**
-     * Size of the configuration interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Validates that the keys are within a set of valid names.
-     *
-     * @return  true if all key names are found in pszzAllowed.
-     * @return  false if not.
-     * @param   pvUser          The opaque user data associated with this interface.
-     * @param   pszzValid       List of valid key names separated by '\\0' and ending with
-     *                          a double '\\0'.
-     */
-    DECLR3CALLBACKMEMBER(bool, pfnAreKeysValid, (void *pvUser, const char *pszzValid));
-
-    /**
-     * Retrieves the length of the string value associated with a key (including
-     * the terminator, for compatibility with CFGMR3QuerySize).
-     *
-     * @return  VBox status code.
-     *          VERR_CFGM_VALUE_NOT_FOUND means that the key is not known.
-     * @param   pvUser          The opaque user data associated with this interface.
-     * @param   pszName         Name of the key to query.
-     * @param   pcbValue        Where to store the value length. Non-NULL.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnQuerySize, (void *pvUser, const char *pszName, size_t *pcbValue));
-
-    /**
-     * Query the string value associated with a key.
-     *
-     * @return  VBox status code.
-     *          VERR_CFGM_VALUE_NOT_FOUND means that the key is not known.
-     *          VERR_CFGM_NOT_ENOUGH_SPACE means that the buffer is not big enough.
-     * @param   pvUser          The opaque user data associated with this interface.
-     * @param   pszName         Name of the key to query.
-     * @param   pszValue        Pointer to buffer where to store value.
-     * @param   cchValue        Length of value buffer.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnQuery, (void *pvUser, const char *pszName, char *pszValue, size_t cchValue));
-
-} VDINTERFACECONFIG, *PVDINTERFACECONFIG;
-
-/**
- * Get configuration information interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACECONFIG) VDGetInterfaceConfig(PVDINTERFACE pInterface)
-{
-    PVDINTERFACECONFIG pInterfaceConfig;
-
-    /* Check that the interface descriptor is a config interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_CONFIG)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not a config interface"), NULL);
-
-    pInterfaceConfig = (PVDINTERFACECONFIG)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceConfig->cbSize == sizeof(VDINTERFACECONFIG))
-                    && (pInterfaceConfig->enmInterface == VDINTERFACETYPE_CONFIG),
-                    ("A non config callback table attached to a config interface descriptor\n"), NULL);
-
-    return pInterfaceConfig;
-}
-
-/**
- * Query configuration, validates that the keys are within a set of valid names.
- *
- * @return  true if all key names are found in pszzAllowed.
- * @return  false if not.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszzValid   List of valid names separated by '\\0' and ending with
- *                      a double '\\0'.
- */
-DECLINLINE(bool) VDCFGAreKeysValid(PVDINTERFACECONFIG pCfgIf, void *pvUser,
-                                   const char *pszzValid)
-{
-    return pCfgIf->pfnAreKeysValid(pvUser, pszzValid);
-}
-
-/**
- * Query configuration, unsigned 64-bit integer value with default.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an integer value
- * @param   pu64        Where to store the value. Set to default on failure.
- * @param   u64Def      The default value.
- */
-DECLINLINE(int) VDCFGQueryU64Def(PVDINTERFACECONFIG pCfgIf, void *pvUser,
-                                 const char *pszName, uint64_t *pu64,
-                                 uint64_t u64Def)
-{
-    char aszBuf[32];
-    int rc = pCfgIf->pfnQuery(pvUser, pszName, aszBuf, sizeof(aszBuf));
-    if (RT_SUCCESS(rc))
-    {
-        rc = RTStrToUInt64Full(aszBuf, 0, pu64);
-    }
-    else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
-    {
-        rc = VINF_SUCCESS;
-        *pu64 = u64Def;
-    }
-    return rc;
-}
-
-/**
- * Query configuration, unsigned 32-bit integer value with default.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an integer value
- * @param   pu32        Where to store the value. Set to default on failure.
- * @param   u32Def      The default value.
- */
-DECLINLINE(int) VDCFGQueryU32Def(PVDINTERFACECONFIG pCfgIf, void *pvUser,
-                                 const char *pszName, uint32_t *pu32,
-                                 uint32_t u32Def)
-{
-    uint64_t u64;
-    int rc = VDCFGQueryU64Def(pCfgIf, pvUser, pszName, &u64, u32Def);
-    if (RT_SUCCESS(rc))
-    {
-        if (!(u64 & UINT64_C(0xffffffff00000000)))
-            *pu32 = (uint32_t)u64;
-        else
-            rc = VERR_CFGM_INTEGER_TOO_BIG;
-    }
-    return rc;
-}
-
-/**
- * Query configuration, bool value with default.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an integer value
- * @param   pf          Where to store the value. Set to default on failure.
- * @param   fDef        The default value.
- */
-DECLINLINE(int) VDCFGQueryBoolDef(PVDINTERFACECONFIG pCfgIf, void *pvUser,
-                                  const char *pszName, bool *pf,
-                                  bool fDef)
-{
-    uint64_t u64;
-    int rc = VDCFGQueryU64Def(pCfgIf, pvUser, pszName, &u64, fDef);
-    if (RT_SUCCESS(rc))
-        *pf = u64 ? true : false;
-    return rc;
-}
-
-/**
- * Query configuration, dynamically allocated (RTMemAlloc) zero terminated
- * character value.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an zero terminated character value
- * @param   ppszString  Where to store the string pointer. Not set on failure.
- *                      Free this using RTMemFree().
- */
-DECLINLINE(int) VDCFGQueryStringAlloc(PVDINTERFACECONFIG pCfgIf,
-                                      void *pvUser, const char *pszName,
-                                      char **ppszString)
-{
-    size_t cb;
-    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
-    if (RT_SUCCESS(rc))
-    {
-        char *pszString = (char *)RTMemAlloc(cb);
-        if (pszString)
-        {
-            rc = pCfgIf->pfnQuery(pvUser, pszName, pszString, cb);
-            if (RT_SUCCESS(rc))
-                *ppszString = pszString;
-            else
-                RTMemFree(pszString);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-    return rc;
-}
-
-/**
- * Query configuration, dynamically allocated (RTMemAlloc) zero terminated
- * character value with default.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an zero terminated character value
- * @param   ppszString  Where to store the string pointer. Not set on failure.
- *                      Free this using RTMemFree().
- * @param   pszDef      The default value.
- */
-DECLINLINE(int) VDCFGQueryStringAllocDef(PVDINTERFACECONFIG pCfgIf,
-                                         void *pvUser, const char *pszName,
-                                         char **ppszString,
-                                         const char *pszDef)
-{
-    size_t cb;
-    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-    {
-        cb = strlen(pszDef) + 1;
-        rc = VINF_SUCCESS;
-    }
-    if (RT_SUCCESS(rc))
-    {
-        char *pszString = (char *)RTMemAlloc(cb);
-        if (pszString)
-        {
-            rc = pCfgIf->pfnQuery(pvUser, pszName, pszString, cb);
-            if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-            {
-                memcpy(pszString, pszDef, cb);
-                rc = VINF_SUCCESS;
-            }
-            if (RT_SUCCESS(rc))
-                *ppszString = pszString;
-            else
-                RTMemFree(pszString);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-    return rc;
-}
-
-/**
- * Query configuration, dynamically allocated (RTMemAlloc) byte string value.
- *
- * @return  VBox status code.
- * @param   pCfgIf      Pointer to configuration callback table.
- * @param   pvUser      The opaque user data associated with this interface.
- * @param   pszName     Name of an zero terminated character value
- * @param   ppvData     Where to store the byte string pointer. Not set on failure.
- *                      Free this using RTMemFree().
- * @param   pcbData     Where to store the byte string length.
- */
-DECLINLINE(int) VDCFGQueryBytesAlloc(PVDINTERFACECONFIG pCfgIf,
-                                     void *pvUser, const char *pszName,
-                                     void **ppvData, size_t *pcbData)
-{
-    size_t cb;
-    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
-    if (RT_SUCCESS(rc))
-    {
-        char *pbData;
-        Assert(cb);
-
-        pbData = (char *)RTMemAlloc(cb);
-        if (pbData)
-        {
-            rc = pCfgIf->pfnQuery(pvUser, pszName, pbData, cb);
-            if (RT_SUCCESS(rc))
-            {
-                *ppvData = pbData;
-                *pcbData = cb - 1; /* Exclude terminator of the queried string. */
-            }
-            else
-                RTMemFree(pbData);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-    }
-    return rc;
-}
-
-/** Forward declaration of a VD socket. */
-typedef struct VDSOCKETINT *VDSOCKET;
-/** Pointer to a VD socket. */
-typedef VDSOCKET *PVDSOCKET;
-/** Nil socket handle. */
-#define NIL_VDSOCKET ((VDSOCKET)0)
-
-/** Connect flag to indicate that the backend wants to use the extended
- * socket I/O multiplexing call. This might not be supported on all configurations
- * (internal networking and iSCSI)
- * and the backend needs to take appropriate action.
- */
-#define VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT RT_BIT_32(0)
-
-/** @name Select events
- * @{ */
-/** Readable without blocking. */
-#define VD_INTERFACETCPNET_EVT_READ         RT_BIT_32(0)
-/** Writable without blocking. */
-#define VD_INTERFACETCPNET_EVT_WRITE        RT_BIT_32(1)
-/** Error condition, hangup, exception or similar. */
-#define VD_INTERFACETCPNET_EVT_ERROR        RT_BIT_32(2)
-/** Hint for the select that getting interrupted while waiting is more likely.
- * The interface implementation can optimize the waiting strategy based on this.
- * It is assumed that it is more likely to get one of the above socket events
- * instead of being interrupted if the flag is not set. */
-#define VD_INTERFACETCPNET_HINT_INTERRUPT   RT_BIT_32(3)
-/** Mask of the valid bits. */
-#define VD_INTERFACETCPNET_EVT_VALID_MASK   UINT32_C(0x0000000f)
-/** @} */
-
-/**
- * TCP network stack interface
- *
- * Per-image. Mandatory for backends which have the VD_CAP_TCPNET bit set.
- */
-typedef struct VDINTERFACETCPNET
-{
-    /**
-     * Size of the configuration interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Creates a socket. The socket is not connected if this succeeds.
-     *
-     * @return  iprt status code.
-     * @retval  VERR_NOT_SUPPORTED if the combination of flags is not supported.
-     * @param   fFlags    Combination of the VD_INTERFACETCPNET_CONNECT_* #defines.
-     * @param   pSock     Where to store the handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSocketCreate, (uint32_t fFlags, PVDSOCKET pSock));
-
-    /**
-     * Destroys the socket.
-     *
-     * @return iprt status code.
-     * @param  Sock       Socket descriptor.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSocketDestroy, (VDSOCKET Sock));
-
-    /**
-     * Connect as a client to a TCP port.
-     *
-     * @return  iprt status code.
-     * @param   Sock            Socket descriptor.
-     * @param   pszAddress      The address to connect to.
-     * @param   uPort           The port to connect to.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClientConnect, (VDSOCKET Sock, const char *pszAddress, uint32_t uPort));
-
-    /**
-     * Close a TCP connection.
-     *
-     * @return  iprt status code.
-     * @param   Sock            Socket descriptor.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClientClose, (VDSOCKET Sock));
-
-    /**
-     * Returns whether the socket is currently connected to the client.
-     *
-     * @returns true if the socket is connected.
-     *          false otherwise.
-     * @param   Sock        Socket descriptor.
-     */
-    DECLR3CALLBACKMEMBER(bool, pfnIsClientConnected, (VDSOCKET Sock));
-
-    /**
-     * Socket I/O multiplexing.
-     * Checks if the socket is ready for reading.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   cMillies    Number of milliseconds to wait for the socket.
-     *                      Use RT_INDEFINITE_WAIT to wait for ever.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSelectOne, (VDSOCKET Sock, RTMSINTERVAL cMillies));
-
-    /**
-     * Receive data from a socket.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pvBuffer    Where to put the data we read.
-     * @param   cbBuffer    Read buffer size.
-     * @param   pcbRead     Number of bytes read.
-     *                      If NULL the entire buffer will be filled upon successful return.
-     *                      If not NULL a partial read can be done successfully.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnRead, (VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
-
-    /**
-     * Send data to a socket.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pvBuffer    Buffer to write data to socket.
-     * @param   cbBuffer    How much to write.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWrite, (VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer));
-
-    /**
-     * Send data from scatter/gather buffer to a socket.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pSgBuffer   Scatter/gather buffer to write data to socket.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSgWrite, (VDSOCKET Sock, PCRTSGBUF pSgBuffer));
-
-    /**
-     * Receive data from a socket - not blocking.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pvBuffer    Where to put the data we read.
-     * @param   cbBuffer    Read buffer size.
-     * @param   pcbRead     Number of bytes read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadNB, (VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
-
-    /**
-     * Send data to a socket - not blocking.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pvBuffer    Buffer to write data to socket.
-     * @param   cbBuffer    How much to write.
-     * @param   pcbWritten  Number of bytes written.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteNB, (VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
-
-    /**
-     * Send data from scatter/gather buffer to a socket - not blocking.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pSgBuffer   Scatter/gather buffer to write data to socket.
-     * @param   pcbWritten  Number of bytes written.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSgWriteNB, (VDSOCKET Sock, PRTSGBUF pSgBuffer, size_t *pcbWritten));
-
-    /**
-     * Flush socket write buffers.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlush, (VDSOCKET Sock));
-
-    /**
-     * Enables or disables delaying sends to coalesce packets.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   fEnable     When set to true enables coalescing.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetSendCoalescing, (VDSOCKET Sock, bool fEnable));
-
-    /**
-     * Gets the address of the local side.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pAddr       Where to store the local address on success.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetLocalAddress, (VDSOCKET Sock, PRTNETADDR pAddr));
-
-    /**
-     * Gets the address of the other party.
-     *
-     * @return  iprt status code.
-     * @param   Sock        Socket descriptor.
-     * @param   pAddr       Where to store the peer address on success.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetPeerAddress, (VDSOCKET Sock, PRTNETADDR pAddr));
-
-    /**
-     * Socket I/O multiplexing - extended version which can be woken up.
-     * Checks if the socket is ready for reading or writing.
-     *
-     * @return  iprt status code.
-     * @retval  VERR_INTERRUPTED if the thread was woken up by a pfnPoke call.
-     * @param   Sock        Socket descriptor.
-     * @param   fEvents     Mask of events to wait for.
-     * @param   pfEvents    Where to store the received events.
-     * @param   cMillies    Number of milliseconds to wait for the socket.
-     *                      Use RT_INDEFINITE_WAIT to wait for ever.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSelectOneEx, (VDSOCKET Sock, uint32_t fEvents,
-                                               uint32_t *pfEvents, RTMSINTERVAL cMillies));
-
-    /**
-     * Wakes up the thread waiting in pfnSelectOneEx.
-     *
-     * @return iprt status code.
-     * @param  Sock        Socket descriptor.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnPoke, (VDSOCKET Sock));
-
-} VDINTERFACETCPNET, *PVDINTERFACETCPNET;
-
-/**
- * Get TCP network stack interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACETCPNET) VDGetInterfaceTcpNet(PVDINTERFACE pInterface)
-{
-    PVDINTERFACETCPNET pInterfaceTcpNet;
-
-    /* Check that the interface descriptor is a TCP network stack interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_TCPNET)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not a TCP network stack interface"), NULL);
-
-    pInterfaceTcpNet = (PVDINTERFACETCPNET)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceTcpNet->cbSize == sizeof(VDINTERFACETCPNET))
-                    && (pInterfaceTcpNet->enmInterface == VDINTERFACETYPE_TCPNET),
-                    ("A non TCP network stack callback table attached to a TCP network stack interface descriptor\n"), NULL);
-
-    return pInterfaceTcpNet;
-}
-
-/**
- * Interface to get the parent state.
- *
- * Per-operation interface. Optional, present only if there is a parent, and
- * used only internally for compacting.
- */
-typedef struct VDINTERFACEPARENTSTATE
-{
-    /**
-     * Size of the parent state interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Read data callback.
-     *
-     * @return  VBox status code.
-     * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
-     * @param   pvUser          The opaque data passed for the operation.
-     * @param   uOffset         Offset of first reading byte from start of disk.
-     *                          Must be aligned to a sector boundary.
-     * @param   pvBuffer        Pointer to buffer for reading data.
-     * @param   cbBuffer        Number of bytes to read.
-     *                          Must be aligned to a sector boundary.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnParentRead, (void *pvUser, uint64_t uOffset, void *pvBuffer, size_t cbBuffer));
-
-} VDINTERFACEPARENTSTATE, *PVDINTERFACEPARENTSTATE;
-
-
-/**
- * Get parent state interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACEPARENTSTATE) VDGetInterfaceParentState(PVDINTERFACE pInterface)
-{
-    PVDINTERFACEPARENTSTATE pInterfaceParentState;
-
-    /* Check that the interface descriptor is a parent state interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_PARENTSTATE)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not a parent state interface"), NULL);
-
-    pInterfaceParentState = (PVDINTERFACEPARENTSTATE)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceParentState->cbSize == sizeof(VDINTERFACEPARENTSTATE))
-                    && (pInterfaceParentState->enmInterface == VDINTERFACETYPE_PARENTSTATE),
-                    ("A non parent state callback table attached to a parent state interface descriptor\n"), NULL);
-
-    return pInterfaceParentState;
-}
-
-/**
- * Interface to synchronize concurrent accesses by several threads.
- *
- * @note The scope of this interface is to manage concurrent accesses after
- * the HDD container has been created, and they must stop before destroying the
- * container. Opening or closing images is covered by the synchronization, but
- * that does not mean it is safe to close images while a thread executes
- * <link to="VDMerge"/> or <link to="VDCopy"/> operating on these images.
- * Making them safe would require the lock to be held during the entire
- * operation, which prevents other concurrent acitivities.
- *
- * @note Right now this is kept as simple as possible, and does not even
- * attempt to provide enough information to allow e.g. concurrent write
- * accesses to different areas of the disk. The reason is that it is very
- * difficult to predict which area of a disk is affected by a write,
- * especially when different image formats are mixed. Maybe later a more
- * sophisticated interface will be provided which has the necessary information
- * about worst case affected areas.
- *
- * Per-disk interface. Optional, needed if the disk is accessed concurrently
- * by several threads, e.g. when merging diff images while a VM is running.
- */
-typedef struct VDINTERFACETHREADSYNC
-{
-    /**
-     * Size of the thread synchronization interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Start a read operation.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnStartRead, (void *pvUser));
-
-    /**
-     * Finish a read operation.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFinishRead, (void *pvUser));
-
-    /**
-     * Start a write operation.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnStartWrite, (void *pvUser));
-
-    /**
-     * Finish a write operation.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFinishWrite, (void *pvUser));
-
-} VDINTERFACETHREADSYNC, *PVDINTERFACETHREADSYNC;
-
-/**
- * Get thread synchronization interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACETHREADSYNC) VDGetInterfaceThreadSync(PVDINTERFACE pInterface)
-{
-    PVDINTERFACETHREADSYNC pInterfaceThreadSync;
-
-    /* Check that the interface descriptor is a thread synchronization interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_THREADSYNC)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not a thread synchronization interface"), NULL);
-
-    pInterfaceThreadSync = (PVDINTERFACETHREADSYNC)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceThreadSync->cbSize == sizeof(VDINTERFACETHREADSYNC))
-                    && (pInterfaceThreadSync->enmInterface == VDINTERFACETYPE_THREADSYNC),
-                    ("A non thread synchronization callback table attached to a thread synchronization interface descriptor\n"), NULL);
-
-    return pInterfaceThreadSync;
-}
-
-/** @name Configuration interface key handling flags.
- * @{
- */
-/** Mandatory config key. Not providing a value for this key will cause
- * the backend to fail. */
-#define VD_CFGKEY_MANDATORY         RT_BIT(0)
-/** Expert config key. Not showing it by default in the GUI is is probably
- * a good idea, as the average user won't understand it easily. */
-#define VD_CFGKEY_EXPERT            RT_BIT(1)
-/** @}*/
-
-
-/**
- * Configuration value type for configuration information interface.
- */
-typedef enum VDCFGVALUETYPE
-{
-    /** Integer value. */
-    VDCFGVALUETYPE_INTEGER = 1,
-    /** String value. */
-    VDCFGVALUETYPE_STRING,
-    /** Bytestring value. */
-    VDCFGVALUETYPE_BYTES
-} VDCFGVALUETYPE;
-
-
-/**
- * Structure describing configuration keys required/supported by a backend
- * through the config interface.
- */
-typedef struct VDCONFIGINFO
-{
-    /** Key name of the configuration. */
-    const char *pszKey;
-    /** Pointer to default value (descriptor). NULL if no useful default value
-     * can be specified. */
-    const char *pszDefaultValue;
-    /** Value type for this key. */
-    VDCFGVALUETYPE enmValueType;
-    /** Key handling flags (a combination of VD_CFGKEY_* flags). */
-    uint64_t uKeyFlags;
-} VDCONFIGINFO;
-
-/** Pointer to structure describing configuration keys. */
-typedef VDCONFIGINFO *PVDCONFIGINFO;
-
-/** Pointer to const structure describing configuration keys. */
-typedef const VDCONFIGINFO *PCVDCONFIGINFO;
-
-/**
- * Structure describing a file extension.
- */
-typedef struct VDFILEEXTENSION
-{
-    /** Pointer to the NULL-terminated string containing the extension. */
-    const char *pszExtension;
-    /** The device type the extension supports. */
-    VDTYPE      enmType;
-} VDFILEEXTENSION;
-
-/** Pointer to a structure describing a file extension. */
-typedef VDFILEEXTENSION *PVDFILEEXTENSION;
-
-/** Pointer to a const structure describing a file extension. */
-typedef const VDFILEEXTENSION *PCVDFILEEXTENSION;
-
-/**
- * Data structure for returning a list of backend capabilities.
- */
-typedef struct VDBACKENDINFO
-{
-    /** Name of the backend. Must be unique even with case insensitive comparison. */
-    const char *pszBackend;
-    /** Capabilities of the backend (a combination of the VD_CAP_* flags). */
-    uint64_t uBackendCaps;
-    /** Pointer to a NULL-terminated array of strings, containing the supported
-     * file extensions. Note that some backends do not work on files, so this
-     * pointer may just contain NULL. */
-    PCVDFILEEXTENSION paFileExtensions;
-    /** Pointer to an array of structs describing each supported config key.
-     * Terminated by a NULL config key. Note that some backends do not support
-     * the configuration interface, so this pointer may just contain NULL.
-     * Mandatory if the backend sets VD_CAP_CONFIG. */
-    PCVDCONFIGINFO paConfigInfo;
-    /** Returns a human readable hard disk location string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the full file path for image-based hard disks.
-     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
-    /** Returns a human readable hard disk name string given a
-     *  set of hard disk configuration keys. The returned string is an
-     *  equivalent of the file name part in the full file path for
-     *  image-based hard disks. Mandatory for backends with no
-     *  VD_CAP_FILE and NULL otherwise. */
-    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
-} VDBACKENDINFO, *PVDBACKENDINFO;
-
-
-/** Forward declaration. Only visible in the VBoxHDD module. */
-/** I/O context */
-typedef struct VDIOCTX *PVDIOCTX;
-/** Storage backend handle. */
-typedef struct VDIOSTORAGE *PVDIOSTORAGE;
-/** Pointer to a storage backend handle. */
-typedef PVDIOSTORAGE *PPVDIOSTORAGE;
-
-/**
- * Completion callback for meta/userdata reads or writes.
- *
- * @return  VBox status code.
- *          VINF_SUCCESS if everything was successful and the transfer can continue.
- *          VERR_VD_ASYNC_IO_IN_PROGRESS if there is another data transfer pending.
- * @param   pBackendData    The opaque backend data.
- * @param   pIoCtx          I/O context associated with this request.
- * @param   pvUser          Opaque user data passed during a read/write request.
- * @param   rcReq           Status code for the completed request.
- */
-typedef DECLCALLBACK(int) FNVDXFERCOMPLETED(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq);
-/** Pointer to FNVDXFERCOMPLETED() */
-typedef FNVDXFERCOMPLETED *PFNVDXFERCOMPLETED;
-
-/** Metadata transfer handle. */
-typedef struct VDMETAXFER *PVDMETAXFER;
-/** Pointer to a metadata transfer handle. */
-typedef PVDMETAXFER *PPVDMETAXFER;
-
-
-/**
- * Internal I/O interface between the generic VD layer and the backends.
- *
- * Per-image. Always passed to backends.
- */
-typedef struct VDINTERFACEIOINT
-{
-    /**
-     * Size of the internal I/O interface.
-     */
-    uint32_t    cbSize;
-
-    /**
-     * Interface type.
-     */
-    VDINTERFACETYPE enmInterface;
-
-    /**
-     * Open callback
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pszLocation     Name of the location to open.
-     * @param   fOpen           Flags for opening the backend.
-     *                          See RTFILE_O_* #defines, inventing another set
-     *                          of open flags is not worth the mapping effort.
-     * @param   ppStorage       Where to store the storage handle.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnOpen, (void *pvUser, const char *pszLocation,
-                                        uint32_t fOpen, PPVDIOSTORAGE ppStorage));
-
-    /**
-     * Close callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to close.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pvUser, PVDIOSTORAGE pStorage));
-
-    /**
-     * Delete callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of the file to delete.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnDelete, (void *pvUser, const char *pcszFilename));
-
-    /**
-     * Move callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszSrc         The path to the source file.
-     * @param   pcszDst         The path to the destination file.
-     *                          This file will be created.
-     * @param   fMove           A combination of the RTFILEMOVE_* flags.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnMove, (void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove));
-
-    /**
-     * Returns the free space on a disk.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of a file to identify the disk.
-     * @param   pcbFreeSpace    Where to store the free space of the disk.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetFreeSpace, (void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace));
-
-    /**
-     * Returns the last modification timestamp of a file.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pcszFilename    Name of a file to identify the disk.
-     * @param   pModificationTime   Where to store the timestamp of the file.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetModificationTime, (void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime));
-
-    /**
-     * Returns the size of the opened storage backend.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to get the size from.
-     * @param   pcbSize         Where to store the size of the storage backend.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnGetSize, (void *pvUser, PVDIOSTORAGE pStorage,
-                                           uint64_t *pcbSize));
-
-    /**
-     * Sets the size of the opened storage backend if possible.
-     *
-     * @return  VBox status code.
-     * @retval  VERR_NOT_SUPPORTED if the backend does not support this operation.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle.
-     * @param   cbSize          The new size of the image.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnSetSize, (void *pvUser, PVDIOSTORAGE pStorage,
-                                           uint64_t cbSize));
-
-    /**
-     * Synchronous write callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to use.
-     * @param   uOffset         The offset to start from.
-     * @param   pvBuffer        Pointer to the bits need to be written.
-     * @param   cbBuffer        How many bytes to write.
-     * @param   pcbWritten      Where to store how many bytes were actually written.
-     *
-     * @notes Do not use in code called from the async read/write entry points in the backends.
-     *        This should be only used during open/close of images and for the support functions
-     *        which are not called while a VM is running (pfnCompact).
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteSync, (void *pvUser, PVDIOSTORAGE pStorage, uint64_t uOffset,
-                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
-
-    /**
-     * Synchronous read callback.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to use.
-     * @param   uOffset         The offset to start from.
-     * @param   pvBuffer        Where to store the read bits.
-     * @param   cbBuffer        How many bytes to read.
-     * @param   pcbRead         Where to store how many bytes were actually read.
-     *
-     * @notes See pfnWriteSync()
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadSync, (void *pvUser, PVDIOSTORAGE pStorage, uint64_t uOffset,
-                                            void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
-
-    /**
-     * Flush data to the storage backend.
-     *
-     * @return  VBox status code.
-     * @param   pvUser          The opaque data passed on container creation.
-     * @param   pStorage        The storage handle to flush.
-     *
-     * @notes See pfnWriteSync()
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlushSync, (void *pvUser, PVDIOSTORAGE pStorage));
-
-    /**
-     * Initiate a asynchronous read request for user data.
-     *
-     * @return  VBox status code.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        The offset to start reading from.
-     * @param   pIoCtx         I/O context passed in VDAsyncRead/Write.
-     * @param   cbRead         How many bytes to read.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadUserAsync, (void *pvUser, PVDIOSTORAGE pStorage,
-                                                 uint64_t uOffset, PVDIOCTX pIoCtx,
-                                                 size_t cbRead));
-
-    /**
-     * Initiate a asynchronous write request for user data.
-     *
-     * @return  VBox status code.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        The offset to start writing to.
-     * @param   pIoCtx         I/O context passed in VDAsyncRead/Write
-     * @param   cbWrite        How many bytes to write.
-     * @param   pfnCompleted   Completion callback.
-     * @param   pvCompleteUser Opaque user data passed in the completion callback.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteUserAsync, (void *pvUser, PVDIOSTORAGE pStorage,
-                                                  uint64_t uOffset, PVDIOCTX pIoCtx,
-                                                  size_t cbWrite,
-                                                  PFNVDXFERCOMPLETED pfnComplete,
-                                                  void *pvCompleteUser));
-
-    /**
-     * Reads metadata asynchronously from storage.
-     * The current I/O context will be halted.
-     *
-     * @returns VBox status code.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        Offset to start reading from.
-     * @param   pvBuffer       Where to store the data.
-     * @param   cbBuffer       How many bytes to read.
-     * @param   pIoCtx         The I/O context which triggered the read.
-     * @param   ppMetaXfer     Where to store the metadata transfer handle on success.
-     * @param   pfnCompleted   Completion callback.
-     * @param   pvCompleteUser Opaque user data passed in the completion callback.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnReadMetaAsync, (void *pvUser, PVDIOSTORAGE pStorage,
-                                                 uint64_t uOffset, void *pvBuffer,
-                                                 size_t cbBuffer, PVDIOCTX pIoCtx,
-                                                 PPVDMETAXFER ppMetaXfer,
-                                                 PFNVDXFERCOMPLETED pfnComplete,
-                                                 void *pvCompleteUser));
-
-    /**
-     * Writes metadata asynchronously to storage.
-     *
-     * @returns VBox status code.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pStorage       The storage handle.
-     * @param   uOffset        Offset to start writing to.
-     * @param   pvBuffer       Written data.
-     * @param   cbBuffer       How many bytes to write.
-     * @param   pIoCtx         The I/O context which triggered the write.
-     * @param   pfnCompleted   Completion callback.
-     * @param   pvCompleteUser Opaque user data passed in the completion callback.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnWriteMetaAsync, (void *pvUser, PVDIOSTORAGE pStorage,
-                                                  uint64_t uOffset, void *pvBuffer,
-                                                  size_t cbBuffer, PVDIOCTX pIoCtx,
-                                                  PFNVDXFERCOMPLETED pfnComplete,
-                                                  void *pvCompleteUser));
-
-    /**
-     * Releases a metadata transfer handle.
-     * The free space can be used for another transfer.
-     *
-     * @returns nothing.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pMetaXfer      The metadata transfer handle to release.
-     */
-    DECLR3CALLBACKMEMBER(void, pfnMetaXferRelease, (void *pvUser, PVDMETAXFER pMetaXfer));
-
-    /**
-     * Initiates a async flush request.
-     *
-     * @return  VBox status code.
-     * @param   pvUser         The opaque data passed on container creation.
-     * @param   pStorage       The storage handle to flush.
-     * @param   pIoCtx         I/O context which triggered the flush.
-     * @param   pfnCompleted   Completion callback.
-     * @param   pvCompleteUser Opaque user data passed in the completion callback.
-     */
-    DECLR3CALLBACKMEMBER(int, pfnFlushAsync, (void *pvUser, PVDIOSTORAGE pStorage,
-                                              PVDIOCTX pIoCtx,
-                                              PFNVDXFERCOMPLETED pfnComplete,
-                                              void *pvCompleteUser));
-
-    /**
-     * Copies a buffer into the I/O context.
-     *
-     * @return Number of bytes copied.
-     * @param  pvUser          The opaque user data passed on container creation.
-     * @param  pIoCtx          I/O context to copy the data to.
-     * @param  pvBuffer        Buffer to copy.
-     * @param  cbBuffer        Number of bytes to copy.
-     */
-    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxCopyTo, (void *pvUser, PVDIOCTX pIoCtx,
-                                                  void *pvBuffer, size_t cbBuffer));
-
-    /**
-     * Copies data from the I/O context into a buffer.
-     *
-     * @return Number of bytes copied.
-     * @param  pvUser          The opaque user data passed on container creation.
-     * @param  pIoCtx          I/O context to copy the data from.
-     * @param  pvBuffer        Destination buffer.
-     * @param  cbBuffer        Number of bytes to copy.
-     */
-    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxCopyFrom, (void *pvUser, PVDIOCTX pIoCtx,
-                                                    void *pvBuffer, size_t cbBuffer));
-
-    /**
-     * Sets the buffer of the given context to a specific byte.
-     *
-     * @return Number of bytes set.
-     * @param  pvUser          The opaque user data passed on container creation.
-     * @param  pIoCtx          I/O context to copy the data from.
-     * @param  ch              The byte to set.
-     * @param  cbSet           Number of bytes to set.
-     */
-    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxSet, (void *pvUser, PVDIOCTX pIoCtx,
-                                               int ch, size_t cbSet));
-
-    /**
-     * Creates a segment array from the I/O context data buffer.
-     *
-     * @returns Number of bytes the array describes.
-     * @param  pvUser          The opaque user data passed on container creation.
-     * @param  pIoCtx          I/O context to copy the data from.
-     * @param  paSeg           The uninitialized segment array.
-     *                         If NULL pcSeg will contain the number of segments needed
-     *                         to describe the requested amount of data.
-     * @param  pcSeg           The number of segments the given array has.
-     *                         This will hold the actual number of entries needed upon return.
-     * @param  cbData          Number of bytes the new array should describe.
-     */
-    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxSegArrayCreate, (void *pvUser, PVDIOCTX pIoCtx,
-                                                          PRTSGSEG paSeg, unsigned *pcSeg,
-                                                          size_t cbData));
-    /**
-     * Marks the given number of bytes as completed and continues the I/O context.
-     *
-     * @returns nothing.
-     * @param   pvUser         The opaque user data passed on container creation.
-     * @param   pIoCtx         The I/O context.
-     * @param   rcReq          Status code the request completed with.
-     * @param   cbCompleted    Number of bytes completed.
-     */
-    DECLR3CALLBACKMEMBER(void, pfnIoCtxCompleted, (void *pvUser, PVDIOCTX pIoCtx,
-                                                   int rcReq, size_t cbCompleted));
-} VDINTERFACEIOINT, *PVDINTERFACEIOINT;
-
-/**
- * Get internal I/O interface from opaque callback table.
- *
- * @return Pointer to the callback table.
- * @param  pInterface Pointer to the interface descriptor.
- */
-DECLINLINE(PVDINTERFACEIOINT) VDGetInterfaceIOInt(PVDINTERFACE pInterface)
-{
-    PVDINTERFACEIOINT pInterfaceIOInt;
-
-    /* Check that the interface descriptor is an internal I/O interface. */
-    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_IOINT)
-                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
-                    ("Not an internal I/O interface"), NULL);
-
-    pInterfaceIOInt = (PVDINTERFACEIOINT)pInterface->pCallbacks;
-
-    /* Do basic checks. */
-    AssertMsgReturn(   (pInterfaceIOInt->cbSize == sizeof(VDINTERFACEIOINT))
-                    && (pInterfaceIOInt->enmInterface == VDINTERFACETYPE_IOINT),
-                    ("A non internal I/O callback table attached to an internal I/O interface descriptor\n"), NULL);
-
-    return pInterfaceIOInt;
-}
-
-/**
- * Request completion callback for the async read/write API.
- */
-typedef void (FNVDASYNCTRANSFERCOMPLETE) (void *pvUser1, void *pvUser2, int rcReq);
-/** Pointer to a transfer compelte callback. */
-typedef FNVDASYNCTRANSFERCOMPLETE *PFNVDASYNCTRANSFERCOMPLETE;
-
-/**
- * Disk geometry.
- */
-typedef struct VDGEOMETRY
-{
-    /** Number of cylinders. */
-    uint32_t    cCylinders;
-    /** Number of heads. */
-    uint32_t    cHeads;
-    /** Number of sectors. */
-    uint32_t    cSectors;
-} VDGEOMETRY;
-
-/** Pointer to disk geometry. */
-typedef VDGEOMETRY *PVDGEOMETRY;
-/** Pointer to constant disk geometry. */
-typedef const VDGEOMETRY *PCVDGEOMETRY;
-
-/**
- * VBox HDD Container main structure.
- */
-/* Forward declaration, VBOXHDD structure is visible only inside VBox HDD module. */
-struct VBOXHDD;
-typedef struct VBOXHDD VBOXHDD;
-typedef VBOXHDD *PVBOXHDD;
-
-/**
- * Initializes HDD backends.
- *
- * @returns VBox status code.
- */
-VBOXDDU_DECL(int) VDInit(void);
-
-/**
- * Destroys loaded HDD backends.
- *
- * @returns VBox status code.
- */
-VBOXDDU_DECL(int) VDShutdown(void);
-
-/**
- * Lists all HDD backends and their capabilities in a caller-provided buffer.
- *
- * @return  VBox status code.
- *          VERR_BUFFER_OVERFLOW if not enough space is passed.
- * @param   cEntriesAlloc   Number of list entries available.
- * @param   pEntries        Pointer to array for the entries.
- * @param   pcEntriesUsed   Number of entries returned.
- */
-VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
-                                unsigned *pcEntriesUsed);
-
-/**
- * Lists the capabilities of a backend identified by its name.
- *
- * @return  VBox status code.
- * @param   pszBackend      The backend name (case insensitive).
- * @param   pEntries        Pointer to an entry.
- */
-VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry);
-
-/**
- * Allocates and initializes an empty HDD container.
- * No image files are opened.
- *
- * @return  VBox status code.
- * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
- * @param   enmType         Type of the image container.
- * @param   ppDisk          Where to store the reference to HDD container.
- */
-VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk);
-
-/**
- * Destroys HDD container.
- * If container has opened image files they will be closed.
- *
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk);
-
-/**
- * Try to get the backend name which can use this image.
- *
- * @return  VBox status code.
- *          VINF_SUCCESS if a plugin was found.
- *                       ppszFormat contains the string which can be used as backend name.
- *          VERR_NOT_SUPPORTED if no backend was found.
- * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pszFilename     Name of the image file for which the backend is queried.
- * @param   ppszFormat      Receives pointer of the UTF-8 string which contains the format name.
- *                          The returned pointer must be freed using RTStrFree().
- * @param   penmType        Where to store the type of the image.
- */
-VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                              const char *pszFilename, char **ppszFormat, VDTYPE *penmType);
-
-/**
- * Opens an image file.
- *
- * The first opened image file in HDD container must have a base image type,
- * others (next opened images) must be differencing or undo images.
- * Linkage is checked for differencing image to be consistent with the previously opened image.
- * When another 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 is opened in read-only mode if a read/write open is not possible.
- * Use VDIsReadOnly to check open mode.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use (case insensitive).
- * @param   pszFilename     Name of the image file to open.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- */
-VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
-                         const char *pszFilename, unsigned uOpenFlags,
-                         PVDINTERFACE pVDIfsImage);
-
-/**
- * Opens a cache image.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to the HDD container which should use the cache image.
- * @param   pszBackend      Name of the cache file backend to use (case insensitive).
- * @param   pszFilename     Name of the cache image to open.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
- */
-VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
-                              const char *pszFilename, unsigned uOpenFlags,
-                              PVDINTERFACE pVDIfsCache);
-
-/**
- * Creates and opens a new base image file.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use (case insensitive).
- * @param   pszFilename     Name of the image file to create.
- * @param   cbSize          Image size in bytes.
- * @param   uImageFlags     Flags specifying special image features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pPCHSGeometry   Pointer to physical disk geometry <= (16383,16,63). Not NULL.
- * @param   pLCHSGeometry   Pointer to logical disk geometry <= (x,255,63). Not NULL.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
-                               const char *pszFilename, uint64_t cbSize,
-                               unsigned uImageFlags, const char *pszComment,
-                               PCVDGEOMETRY pPCHSGeometry,
-                               PCVDGEOMETRY pLCHSGeometry,
-                               PCRTUUID pUuid, unsigned uOpenFlags,
-                               PVDINTERFACE pVDIfsImage,
-                               PVDINTERFACE pVDIfsOperation);
-
-/**
- * Creates and opens a new differencing image file in HDD container.
- * See comments for VDOpen function about differencing images.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use (case insensitive).
- * @param   pszFilename     Name of the differencing image file to create.
- * @param   uImageFlags     Flags specifying special image features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   pParentUuid     New parent UUID of the image. If NULL, the UUID is queried automatically.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
-                               const char *pszFilename, unsigned uImageFlags,
-                               const char *pszComment, PCRTUUID pUuid,
-                               PCRTUUID pParentUuid, unsigned uOpenFlags,
-                               PVDINTERFACE pVDIfsImage,
-                               PVDINTERFACE pVDIfsOperation);
-
-/**
- * Creates and opens new cache image file in HDD container.
- *
- * @return  VBox status code.
- * @param   pDisk           Name of the cache file backend to use (case insensitive).
- * @param   pszFilename     Name of the differencing cache file to create.
- * @param   cbSize          Maximum size of the cache.
- * @param   uImageFlags     Flags specifying special cache features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
-                                const char *pszFilename, uint64_t cbSize,
-                                unsigned uImageFlags, const char *pszComment,
-                                PCRTUUID pUuid, unsigned uOpenFlags,
-                                PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation);
-
-/**
- * Merges two images (not necessarily with direct parent/child relationship).
- * As a side effect the source image and potentially the other images which
- * are also merged to the destination are deleted from both the disk and the
- * images in the HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImageFrom      Image number to merge from, counts from 0. 0 is always base image of container.
- * @param   nImageTo        Image number to merge to, counts from 0. 0 is always base image of container.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
-                          unsigned nImageTo, PVDINTERFACE pVDIfsOperation);
-
-/**
- * Copies an image from one HDD container to another.
- * The copy is opened in the target HDD container.
- * It is possible to convert between different image formats, because the
- * backend for the destination may be different from the source.
- * If both the source and destination reference the same HDD container,
- * then the image is moved (by copying/deleting or renaming) to the new location.
- * The source container is unchanged if the move operation fails, otherwise
- * the image at the new location is opened in the same way as the old one was.
- *
- * @note The read/write accesses across disks are not synchronized, just the
- * accesses to each disk. Once there is a use case which requires a defined
- * read/write behavior in this situation this needs to be extended.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDiskFrom       Pointer to source HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pDiskTo         Pointer to destination HDD container.
- * @param   pszBackend      Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
- * @param   pszFilename     New name of the image (may be NULL to specify that the
- *                          copy destination is the destination container, or
- *                          if pDiskFrom == pDiskTo, i.e. when moving).
- * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
- * @param   cbSize          New image size (0 means leave unchanged).
- * @param   uImageFlags     Flags specifying special destination image features.
- * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
- *                          This parameter is used if and only if a true copy is created.
- *                          In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- *                          Only used if the destination image is created.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
- *                          destination image.
- * @param   pDstVDIfsOperation Pointer to the per-operation VD interface list,
- *                          for the destination operation.
- */
-VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
-                         const char *pszBackend, const char *pszFilename,
-                         bool fMoveByRename, uint64_t cbSize,
-                         unsigned uImageFlags, PCRTUUID pDstUuid,
-                         unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
-                         PVDINTERFACE pDstVDIfsImage,
-                         PVDINTERFACE pDstVDIfsOperation);
-
-/**
- * Optimizes the storage consumption of an image. Typically the unused blocks
- * have to be wiped with zeroes to achieve a substantial reduced storage use.
- * Another optimization done is reordering the image blocks, which can provide
- * a significant performance boost, as reads and writes tend to use less random
- * file offsets.
- *
- * @note Compaction is treated as a single operation with regard to thread
- * synchronization, which means that it potentially blocks other activities for
- * a long time. The complexity of compaction would grow even more if concurrent
- * accesses have to be handled.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
- * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
- *                             this isn't supported yet.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
-                            PVDINTERFACE pVDIfsOperation);
-
-/**
- * Resizes the given disk image to the given size.
- *
- * @return  VBox status
- * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
- * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
- *
- * @param   pDisk           Pointer to the HDD container.
- * @param   cbSize          New size of the image.
- * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
- * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
-                           PCVDGEOMETRY pPCHSGeometry,
-                           PCVDGEOMETRY pLCHSGeometry,
-                           PVDINTERFACE pVDIfsOperation);
-
-/**
- * Closes the last opened image file in HDD container.
- * If previous image file was opened in read-only mode (the normal case) and
- * the last opened image is in read-write mode then the previous image will be
- * reopened in read/write mode.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   fDelete         If true, delete the image from the host disk.
- */
-VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete);
-
-/**
- * Closes the currently opened cache image file in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no cache is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   fDelete         If true, delete the image from the host disk.
- */
-VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete);
-
-/**
- * Closes all opened image files in HDD container.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk);
-
-/**
- * Read data from virtual HDD.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   uOffset         Offset of first reading byte from start of disk.
- *                          Must be aligned to a sector boundary.
- * @param   pvBuffer        Pointer to buffer for reading data.
- * @param   cbBuffer        Number of bytes to read.
- *                          Must be aligned to a sector boundary.
- */
-VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuffer, size_t cbBuffer);
-
-/**
- * Write data to virtual HDD.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   uOffset         Offset of first writing byte from start of disk.
- *                          Must be aligned to a sector boundary.
- * @param   pvBuffer        Pointer to buffer for writing data.
- * @param   cbBuffer        Number of bytes to write.
- *                          Must be aligned to a sector boundary.
- */
-VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer);
-
-/**
- * Make sure the on disk representation of a virtual HDD is up to date.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk);
-
-/**
- * Get number of opened images in HDD container.
- *
- * @return  Number of opened images for HDD container. 0 if no images have been opened.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk);
-
-/**
- * Get read/write mode of HDD container.
- *
- * @return  Virtual disk ReadOnly status.
- * @return  true if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk);
-
-/**
- * Get total capacity of an image in HDD container.
- *
- * @return  Virtual disk size in bytes.
- * @return  0 if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- */
-VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage);
-
-/**
- * Get total file size of an image in HDD container.
- *
- * @return  Virtual disk size in bytes.
- * @return  0 if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- */
-VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage);
-
-/**
- * Get virtual disk PCHS geometry of an image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pPCHSGeometry   Where to store PCHS geometry. Not NULL.
- */
-VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PVDGEOMETRY pPCHSGeometry);
-
-/**
- * Store virtual disk PCHS geometry of an image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pPCHSGeometry   Where to load PCHS geometry from. Not NULL.
- */
-VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PCVDGEOMETRY pPCHSGeometry);
-
-/**
- * Get virtual disk LCHS geometry of an image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pLCHSGeometry   Where to store LCHS geometry. Not NULL.
- */
-VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PVDGEOMETRY pLCHSGeometry);
-
-/**
- * Store virtual disk LCHS geometry of an image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pLCHSGeometry   Where to load LCHS geometry from. Not NULL.
- */
-VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PCVDGEOMETRY pLCHSGeometry);
-
-/**
- * Get version of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puVersion       Where to store the image version.
- */
-VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
-                               unsigned *puVersion);
-
-/**
- * List the capabilities of image backend in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to the HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pbackendInfo    Where to store the backend information.
- */
-VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
-                                      PVDBACKENDINFO pBackendInfo);
-
-/**
- * Get flags of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puImageFlags    Where to store the image flags.
- */
-VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage, unsigned *puImageFlags);
-
-/**
- * Get open flags of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puOpenFlags     Where to store the image open flags.
- */
-VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
-                                 unsigned *puOpenFlags);
-
-/**
- * Set open flags of image in HDD container.
- * This operation may cause file locking changes and/or files being reopened.
- * Note that in case of unrecoverable error all images in HDD container will be closed.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- */
-VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
-                                 unsigned uOpenFlags);
-
-/**
- * Get base filename of image in HDD container. Some image formats use
- * other filenames as well, so don't use this for anything but informational
- * purposes.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszFilename     Where to store the image file name.
- * @param   cbFilename      Size of buffer pszFilename points to.
- */
-VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
-                                char *pszFilename, unsigned cbFilename);
-
-/**
- * Get the comment line of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszComment      Where to store the comment string of image. NULL is ok.
- * @param   cbComment       The size of pszComment buffer. 0 is ok.
- */
-VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
-                               char *pszComment, unsigned cbComment);
-
-/**
- * Changes the comment line of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
- */
-VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
-                               const char *pszComment);
-
-/**
- * Get UUID of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           Where to store the image UUID.
- */
-VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid);
-
-/**
- * Set the image's UUID. Should not be used by normal applications.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid);
-
-/**
- * Get last modification UUID of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           Where to store the image modification UUID.
- */
-VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage,
-                                        PRTUUID pUuid);
-
-/**
- * Set the image's last modification UUID. Should not be used by normal applications.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New modification UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage,
-                                        PCRTUUID pUuid);
-
-/**
- * Get parent UUID of image in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of the container.
- * @param   pUuid           Where to store the parent image UUID.
- */
-VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
-                                  PRTUUID pUuid);
-
-/**
- * Set the image's parent UUID. Should not be used by normal applications.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New parent UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
-                                  PCRTUUID pUuid);
-
-
-/**
- * Debug helper - dumps all opened images in HDD container into the log file.
- *
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk);
-
-
-/**
- * Query if asynchronous operations are supported for this disk.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to the HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pfAIOSupported  Where to store if async IO is supported.
- */
-VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported);
-
-
-/**
- * Start a asynchronous read request.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to the HDD container.
- * @param   uOffset         The offset of the virtual disk to read from.
- * @param   cbRead          How many bytes to read.
- * @param   paSeg           Pointer to an array of segments.
- * @param   cSeg            Number of segments in the array.
- * @param   pfnComplete     Completion callback.
- * @param   pvUser          User data which is passed on completion
- */
-VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
-                              PCRTSGSEG paSeg, unsigned cSeg,
-                              PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                              void *pvUser1, void *pvUser2);
-
-
-/**
- * Start a asynchronous write request.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to the HDD container.
- * @param   uOffset         The offset of the virtual disk to write to.
- * @param   cbWrtie         How many bytes to write.
- * @param   paSeg           Pointer to an array of segments.
- * @param   cSeg            Number of segments in the array.
- * @param   pfnComplete     Completion callback.
- * @param   pvUser          User data which is passed on completion.
- */
-VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
-                               PCRTSGSEG paSeg, unsigned cSeg,
-                               PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                               void *pvUser1, void *pvUser2);
-
-
-/**
- * Start a asynchronous flush request.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to the HDD container.
- * @param   pfnComplete     Completion callback.
- * @param   pvUser          User data which is passed on completion.
- */
-VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk,
-                               PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                               void *pvUser1, void *pvUser2);
-RT_C_DECLS_END
-
-/** @} */
-
-#endif
Index: /trunk/include/VBox/vd-cache-plugin.h
===================================================================
--- /trunk/include/VBox/vd-cache-plugin.h	(revision 33567)
+++ /trunk/include/VBox/vd-cache-plugin.h	(revision 33567)
@@ -0,0 +1,356 @@
+/** @file
+ * Internal hard disk format support API for VBoxHDD cache images.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef __VBoxHDD_CachePlugin_h__
+#define __VBoxHDD_CachePlugin_h__
+
+#include <VBox/vd.h>
+
+/**
+ * Cache format backend interface used by VBox HDD Container implementation.
+ */
+typedef struct VDCACHEBACKEND
+{
+    /**
+     * The name of the backend (constant string).
+     */
+    const char *pszBackendName;
+
+    /**
+     * The size of the structure.
+     */
+    uint32_t cbSize;
+
+    /**
+     * The capabilities of the backend.
+     */
+    uint64_t uBackendCaps;
+
+    /**
+     * Pointer to a NULL-terminated array of strings, containing the supported
+     * file extensions. Note that some backends do not work on files, so this
+     * pointer may just contain NULL.
+     */
+    const char * const *papszFileExtensions;
+
+    /**
+     * Pointer to an array of structs describing each supported config key.
+     * Terminated by a NULL config key. Note that some backends do not support
+     * the configuration interface, so this pointer may just contain NULL.
+     * Mandatory if the backend sets VD_CAP_CONFIG.
+     */
+    PCVDCONFIGINFO paConfigInfo;
+
+    /**
+     * Handle of loaded plugin library, NIL_RTLDRMOD for static backends.
+     */
+    RTLDRMOD hPlugin;
+
+    /**
+     * Probes the given image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnProbe, (const char *pszFilename, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage));
+
+    /**
+     * Open a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file to open. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   ppBackendData   Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnOpen, (const char *pszFilename, unsigned uOpenFlags,
+                                        PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                                        void **ppBackendData));
+
+    /**
+     * Create a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file to create. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     * @param   cbSize          Image size in bytes.
+     * @param   uImageFlags     Flags specifying special image features.
+     * @param   pszComment      Pointer to image comment. NULL is ok.
+     * @param   pUuid           New UUID of the image. Not NULL.
+     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+     * @param   uPercentStart   Starting value for progress percentage.
+     * @param   uPercentSpan    Span for varying progress percentage.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+     * @param   ppBackendData   Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCreate, (const char *pszFilename, uint64_t cbSize,
+                                          unsigned uImageFlags, const char *pszComment,
+                                          PCRTUUID pUuid, unsigned uOpenFlags,
+                                          unsigned uPercentStart, unsigned uPercentSpan,
+                                          PVDINTERFACE pVDIfsDisk,
+                                          PVDINTERFACE pVDIfsImage,
+                                          PVDINTERFACE pVDIfsOperation,
+                                          void **ppBackendData));
+
+    /**
+     * Close a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   fDelete         If true, delete the image from the host disk.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pBackendData, bool fDelete));
+
+    /**
+     * Read data from a cache image. The area read never crosses a block
+     * boundary.
+     *
+     * @returns VBox status code.
+     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         Offset to start reading from.
+     * @param   pvBuf           Where to store the read bits.
+     * @param   cbRead          Number of bytes to read.
+     * @param   pcbActuallyRead Pointer to returned number of bytes read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnRead, (void *pBackendData, uint64_t uOffset, void *pvBuf,
+                                        size_t cbRead, size_t *pcbActuallyRead));
+
+    /**
+     * Write data to a cache image. The area written never crosses a block
+     * boundary.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         Offset to start writing to.
+     * @param   pvBuf           Where to retrieve the written bits.
+     * @param   cbWrite         Number of bytes to write.
+     * @param   pcbWriteProcess Pointer to returned number of bytes that could
+     *                          be processed.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWrite, (void *pBackendData, uint64_t uOffset,
+                                         const void *pvBuf, size_t cbWrite,
+                                         size_t *pcbWriteProcess));
+
+    /**
+     * Flush data to disk.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlush, (void *pBackendData));
+
+    /**
+     * Get the version of a cache image.
+     *
+     * @returns version of cache image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetVersion, (void *pBackendData));
+
+    /**
+     * Get the capacity of a cache image.
+     *
+     * @returns size of cache image in bytes.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(uint64_t, pfnGetSize, (void *pBackendData));
+
+    /**
+     * Get the file size of a cache image.
+     *
+     * @returns size of cache image in bytes.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(uint64_t, pfnGetFileSize, (void *pBackendData));
+
+    /**
+     * Get the image flags of a cache image.
+     *
+     * @returns image flags of cache image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetImageFlags, (void *pBackendData));
+
+    /**
+     * Get the open flags of a cache image.
+     *
+     * @returns open flags of cache image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetOpenFlags, (void *pBackendData));
+
+    /**
+     * Set the open flags of a cache image. May cause the image to be locked
+     * in a different mode or be reopened (which can fail).
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOpenFlags      New open flags for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetOpenFlags, (void *pBackendData, unsigned uOpenFlags));
+
+    /**
+     * Get comment of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pszComment      Where to store the comment.
+     * @param   cbComment       Size of the comment buffer.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetComment, (void *pBackendData, char *pszComment, size_t cbComment));
+
+    /**
+     * Set comment of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pszComment      Where to get the comment from. NULL resets comment.
+     *                          The comment is silently truncated if the image format
+     *                          limit is exceeded.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetComment, (void *pBackendData, const char *pszComment));
+
+    /**
+     * Get UUID of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the image UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set UUID of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the image UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Get last modification UUID of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the image modification UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetModificationUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set last modification UUID of a cache image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the image modification UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetModificationUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Dump information about a cache image.
+     *
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnDump, (void *pBackendData));
+
+    /**
+     * Start an asynchronous read request.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         The offset of the virtual disk to read from.
+     * @param   cbRead          How many bytes to read.
+     * @param   pIoCtx          I/O context associated with this request.
+     * @param   pcbActuallyRead Pointer to returned number of bytes read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncRead, (void *pBackendData, uint64_t uOffset, size_t cbRead,
+                                             PVDIOCTX pIoCtx, size_t *pcbActuallyRead));
+
+    /**
+     * Start an asynchronous write request.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         The offset of the virtual disk to write to.
+     * @param   cbWrite         How many bytes to write.
+     * @param   pIoCtx          I/O context associated with this request.
+     * @param   pcbWriteProcess Pointer to returned number of bytes that could
+     *                          be processed. In case the function returned
+     *                          VERR_VD_BLOCK_FREE this is the number of bytes
+     *                          that could be written in a full block write,
+     *                          when prefixed/postfixed by the appropriate
+     *                          amount of (previously read) padding data.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncWrite, (void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                                              PVDIOCTX pIoCtx, size_t *pcbWriteProcess));
+
+    /**
+     * Flush data to disk.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pIoCtx          I/O context associated with this request.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncFlush, (void *pBackendData, PVDIOCTX pIoCtx));
+
+    /** Returns a human readable hard disk location string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the full file path for image-based hard disks.
+     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
+
+    /** Returns a human readable hard disk name string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the file name part in the full file path for
+     *  image-based hard disks. Mandatory for backends with no
+     *  VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
+
+} VDCACHEBACKEND;
+
+/** Pointer to VD backend. */
+typedef VDCACHEBACKEND *PVDCACHEBACKEND;
+
+/** Constant pointer to VD backend. */
+typedef const VDCACHEBACKEND *PCVDCACHEBACKEND;
+
+/** Initialization entry point. */
+typedef DECLCALLBACK(int) FNVDCACHEFORMATLOAD(PVDCACHEBACKEND *ppBackendTable);
+typedef FNVDCACHEFORMATLOAD *PFNVDCACHEFORMATLOAD;
+#define VD_CACHEFORMAT_LOAD_NAME "VDCacheFormatLoad"
+
+/** The prefix to identify Storage Plugins. */
+#define VD_CACHEFORMAT_PLUGIN_PREFIX "VDCache"
+/** The size of the prefix excluding the '\\0' terminator. */
+#define VD_CACHEFORMAT_PLUGIN_PREFIX_LENGTH (sizeof(VD_CACHEFORMAT_PLUGIN_PREFIX)-1)
+
+#endif
Index: /trunk/include/VBox/vd-plugin.h
===================================================================
--- /trunk/include/VBox/vd-plugin.h	(revision 33567)
+++ /trunk/include/VBox/vd-plugin.h	(revision 33567)
@@ -0,0 +1,601 @@
+/** @file
+ * Internal hard disk format support API for VBoxHDD.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef __VBoxHDD_Plugin_h__
+#define __VBoxHDD_Plugin_h__
+
+#include <VBox/vd.h>
+
+
+/** @name VBox HDD backend write flags
+ * @{
+ */
+/** Do not allocate a new block on this write. This is just an advisory
+ * flag. The backend may still decide in some circumstances that it wants
+ * to ignore this flag (which may cause extra dynamic image expansion). */
+#define VD_WRITE_NO_ALLOC   RT_BIT(1)
+/** @}*/
+
+
+/**
+ * Image format backend interface used by VBox HDD Container implementation.
+ */
+typedef struct VBOXHDDBACKEND
+{
+    /**
+     * The name of the backend (constant string).
+     */
+    const char *pszBackendName;
+
+    /**
+     * The size of the structure.
+     */
+    uint32_t cbSize;
+
+    /**
+     * The capabilities of the backend.
+     */
+    uint64_t uBackendCaps;
+
+    /**
+     * Pointer to a NULL-terminated array, containing the supported
+     * file extensions. Note that some backends do not work on files, so this
+     * pointer may just contain NULL.
+     */
+    PCVDFILEEXTENSION paFileExtensions;
+
+    /**
+     * Pointer to an array of structs describing each supported config key.
+     * Terminated by a NULL config key. Note that some backends do not support
+     * the configuration interface, so this pointer may just contain NULL.
+     * Mandatory if the backend sets VD_CAP_CONFIG.
+     */
+    PCVDCONFIGINFO paConfigInfo;
+
+    /**
+     * Handle of loaded plugin library, NIL_RTLDRMOD for static backends.
+     */
+    RTLDRMOD hPlugin;
+
+    /**
+     * Check if a file is valid for the backend.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   penmType        Returns the supported device type on success.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCheckIfValid, (const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                                                PVDINTERFACE pVDIfsImage, VDTYPE *penmType));
+
+    /**
+     * Open a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file to open. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   enmType         Requested type of the image.
+     * @param   ppBackendData   Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnOpen, (const char *pszFilename, unsigned uOpenFlags,
+                                        PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                                        VDTYPE enmType, void **ppBackendData));
+
+    /**
+     * Create a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pszFilename     Name of the image file to create. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     * @param   cbSize          Image size in bytes.
+     * @param   uImageFlags     Flags specifying special image features.
+     * @param   pszComment      Pointer to image comment. NULL is ok.
+     * @param   pPCHSGeometry   Physical drive geometry CHS <= (16383,16,255).
+     * @param   pLCHSGeometry   Logical drive geometry CHS <= (1024,255,63).
+     * @param   pUuid           New UUID of the image. Not NULL.
+     * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+     * @param   uPercentStart   Starting value for progress percentage.
+     * @param   uPercentSpan    Span for varying progress percentage.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+     * @param   ppBackendData   Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCreate, (const char *pszFilename, uint64_t cbSize,
+                                          unsigned uImageFlags, const char *pszComment,
+                                          PCVDGEOMETRY pPCHSGeometry,
+                                          PCVDGEOMETRY pLCHSGeometry,
+                                          PCRTUUID pUuid, unsigned uOpenFlags,
+                                          unsigned uPercentStart, unsigned uPercentSpan,
+                                          PVDINTERFACE pVDIfsDisk,
+                                          PVDINTERFACE pVDIfsImage,
+                                          PVDINTERFACE pVDIfsOperation,
+                                          void **ppBackendData));
+
+    /**
+     * Rename a disk image. Only needs to work as long as the operating
+     * system's rename file functionality is usable. If an attempt is made to
+     * rename an image to a location on another disk/filesystem, this function
+     * may just fail with an appropriate error code (not changing the opened
+     * image data at all). Also works only on images which actually refer to
+     * regular files. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pszFilename     New name of the image file. Guaranteed to be available and
+     *                          unchanged during the lifetime of this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnRename, (void *pBackendData, const char *pszFilename));
+
+    /**
+     * Close a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   fDelete         If true, delete the image from the host disk.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pBackendData, bool fDelete));
+
+    /**
+     * Read data from a disk image. The area read never crosses a block
+     * boundary.
+     *
+     * @returns VBox status code.
+     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         Offset to start reading from.
+     * @param   pvBuf           Where to store the read bits.
+     * @param   cbRead          Number of bytes to read.
+     * @param   pcbActuallyRead Pointer to returned number of bytes read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnRead, (void *pBackendData, uint64_t uOffset, void *pvBuf,
+                                        size_t cbRead, size_t *pcbActuallyRead));
+
+    /**
+     * Write data to a disk image. The area written never crosses a block
+     * boundary.
+     *
+     * @returns VBox status code.
+     * @returns VERR_VD_BLOCK_FREE if this image contains no data for this block and
+     *          this is not a full-block write. The write must be repeated with
+     *          the correct amount of prefix/postfix data read from the images below
+     *          in the image stack. This might not be the most convenient interface,
+     *          but it works with arbitrary block sizes, especially when the image
+     *          stack uses different block sizes.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         Offset to start writing to.
+     * @param   pvBuf           Where to retrieve the written bits.
+     * @param   cbWrite         Number of bytes to write.
+     * @param   pcbWriteProcess Pointer to returned number of bytes that could
+     *                          be processed. In case the function returned
+     *                          VERR_VD_BLOCK_FREE this is the number of bytes
+     *                          that could be written in a full block write,
+     *                          when prefixed/postfixed by the appropriate
+     *                          amount of (previously read) padding data.
+     * @param   pcbPreRead      Pointer to the returned amount of data that must
+     *                          be prefixed to perform a full block write.
+     * @param   pcbPostRead     Pointer to the returned amount of data that must
+     *                          be postfixed to perform a full block write.
+     * @param   fWrite          Flags which affect write behavior. Combination
+     *                          of the VD_WRITE_* flags.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWrite, (void *pBackendData, uint64_t uOffset,
+                                         const void *pvBuf, size_t cbWrite,
+                                         size_t *pcbWriteProcess, size_t *pcbPreRead,
+                                         size_t *pcbPostRead, unsigned fWrite));
+
+    /**
+     * Flush data to disk.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlush, (void *pBackendData));
+
+    /**
+     * Get the version of a disk image.
+     *
+     * @returns version of disk image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetVersion, (void *pBackendData));
+
+    /**
+     * Get the capacity of a disk image.
+     *
+     * @returns size of disk image in bytes.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(uint64_t, pfnGetSize, (void *pBackendData));
+
+    /**
+     * Get the file size of a disk image.
+     *
+     * @returns size of disk image in bytes.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(uint64_t, pfnGetFileSize, (void *pBackendData));
+
+    /**
+     * Get virtual disk PCHS geometry stored in a disk image.
+     *
+     * @returns VBox status code.
+     * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the image.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pPCHSGeometry   Where to store the geometry. Not NULL.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetPCHSGeometry, (void *pBackendData, PVDGEOMETRY pPCHSGeometry));
+
+    /**
+     * Set virtual disk PCHS geometry stored in a disk image.
+     * Only called if geometry is different than before.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pPCHSGeometry   Where to load the geometry from. Not NULL.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetPCHSGeometry, (void *pBackendData, PCVDGEOMETRY pPCHSGeometry));
+
+    /**
+     * Get virtual disk LCHS geometry stored in a disk image.
+     *
+     * @returns VBox status code.
+     * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the image.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pLCHSGeometry   Where to store the geometry. Not NULL.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetLCHSGeometry, (void *pBackendData,  PVDGEOMETRY pLCHSGeometry));
+
+    /**
+     * Set virtual disk LCHS geometry stored in a disk image.
+     * Only called if geometry is different than before.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pLCHSGeometry   Where to load the geometry from. Not NULL.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetLCHSGeometry, (void *pBackendData,  PCVDGEOMETRY pLCHSGeometry));
+
+    /**
+     * Get the image flags of a disk image.
+     *
+     * @returns image flags of disk image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetImageFlags, (void *pBackendData));
+
+    /**
+     * Get the open flags of a disk image.
+     *
+     * @returns open flags of disk image.
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(unsigned, pfnGetOpenFlags, (void *pBackendData));
+
+    /**
+     * Set the open flags of a disk image. May cause the image to be locked
+     * in a different mode or be reopened (which can fail).
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOpenFlags      New open flags for this image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetOpenFlags, (void *pBackendData, unsigned uOpenFlags));
+
+    /**
+     * Get comment of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pszComment      Where to store the comment.
+     * @param   cbComment       Size of the comment buffer.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetComment, (void *pBackendData, char *pszComment, size_t cbComment));
+
+    /**
+     * Set comment of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pszComment      Where to get the comment from. NULL resets comment.
+     *                          The comment is silently truncated if the image format
+     *                          limit is exceeded.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetComment, (void *pBackendData, const char *pszComment));
+
+    /**
+     * Get UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the image UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the image UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Get last modification UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the image modification UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetModificationUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set last modification UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the image modification UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetModificationUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Get parent UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the parent image UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetParentUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set parent UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the parent image UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetParentUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Get parent modification UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to store the parent image modification UUID.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetParentModificationUuid, (void *pBackendData, PRTUUID pUuid));
+
+    /**
+     * Set parent modification UUID of a disk image.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pUuid           Where to get the parent image modification UUID from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetParentModificationUuid, (void *pBackendData, PCRTUUID pUuid));
+
+    /**
+     * Dump information about a disk image.
+     *
+     * @param   pBackendData    Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnDump, (void *pBackendData));
+
+    /**
+     * Get a time stamp of a disk image. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pTimeStamp      Where to store the time stamp.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetTimeStamp, (void *pBackendData, PRTTIMESPEC pTimeStamp));
+
+    /**
+     * Get the parent time stamp of a disk image. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pTimeStamp      Where to store the time stamp.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetParentTimeStamp, (void *pBackendData, PRTTIMESPEC pTimeStamp));
+
+    /**
+     * Set the parent time stamp of a disk image. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pTimeStamp      Where to get the time stamp from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetParentTimeStamp, (void *pBackendData, PCRTTIMESPEC pTimeStamp));
+
+    /**
+     * Get the relative path to parent image. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData      Opaque state data for this image.
+     * @param   pszParentFilename Where to store the path.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetParentFilename, (void *pBackendData, char **ppszParentFilename));
+
+    /**
+     * Set the relative path to parent image. May be NULL.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData      Opaque state data for this image.
+     * @param   pszParentFilename Where to get the path from.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetParentFilename, (void *pBackendData, const char *pszParentFilename));
+
+    /**
+     * Return whether asynchronous I/O operations are supported for this image.
+     *
+     * @returns true if asynchronous I/O is supported
+     *          false otherwise.
+     * @param   pBackendData     Opaque state data for this image.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsAsyncIOSupported, (void *pBackendData));
+
+    /**
+     * Start an asynchronous read request.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         The offset of the virtual disk to read from.
+     * @param   cbRead          How many bytes to read.
+     * @param   pIoCtx          I/O context associated with this request.
+     * @param   pcbActuallyRead Pointer to returned number of bytes read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncRead, (void *pBackendData, uint64_t uOffset, size_t cbRead,
+                                             PVDIOCTX pIoCtx, size_t *pcbActuallyRead));
+
+    /**
+     * Start an asynchronous write request.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uOffset         The offset of the virtual disk to write to.
+     * @param   cbWrite         How many bytes to write.
+     * @param   pIoCtx          I/O context associated with this request.
+     * @param   pcbWriteProcess Pointer to returned number of bytes that could
+     *                          be processed. In case the function returned
+     *                          VERR_VD_BLOCK_FREE this is the number of bytes
+     *                          that could be written in a full block write,
+     *                          when prefixed/postfixed by the appropriate
+     *                          amount of (previously read) padding data.
+     * @param   pcbPreRead      Pointer to the returned amount of data that must
+     *                          be prefixed to perform a full block write.
+     * @param   pcbPostRead     Pointer to the returned amount of data that must
+     *                          be postfixed to perform a full block write.
+     * @param   fWrite          Flags which affect write behavior. Combination
+     *                          of the VD_WRITE_* flags.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncWrite, (void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                                              PVDIOCTX pIoCtx,
+                                              size_t *pcbWriteProcess, size_t *pcbPreRead,
+                                              size_t *pcbPostRead, unsigned fWrite));
+
+    /**
+     * Flush data to disk.
+     *
+     * @returns VBox status code.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   pIoCtx          I/O context associated with this request.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnAsyncFlush, (void *pBackendData, PVDIOCTX pIoCtx));
+
+    /** Returns a human readable hard disk location string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the full file path for image-based hard disks.
+     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
+
+    /** Returns a human readable hard disk name string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the file name part in the full file path for
+     *  image-based hard disks. Mandatory for backends with no
+     *  VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
+
+    /**
+     * Compact the image. The pointer may be NULL, indicating that this
+     * isn't supported yet (for file-based images) or not necessary.
+     *
+     * @returns VBox status code.
+     * @returns VERR_NOT_SUPPORTED if this image cannot be compacted yet.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   uPercentStart   Starting value for progress percentage.
+     * @param   uPercentSpan    Span for varying progress percentage.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnCompact, (void *pBackendData,
+                                           unsigned uPercentStart, unsigned uPercentSpan,
+                                           PVDINTERFACE pVDIfsDisk,
+                                           PVDINTERFACE pVDIfsImage,
+                                           PVDINTERFACE pVDIfsOperation));
+
+    /**
+     * Resize the image. The pointer may be NULL, indicating that this
+     * isn't supported yet (for file-based images) or not necessary.
+     *
+     * @returns VBox status code.
+     * @returns VERR_NOT_SUPPORTED if this image cannot be compacted yet.
+     * @param   pBackendData    Opaque state data for this image.
+     * @param   cbSize          New size of the image.
+     * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
+     * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
+     * @param   uPercentStart   Starting value for progress percentage.
+     * @param   uPercentSpan    Span for varying progress percentage.
+     * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+     * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+     * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnResize, (void *pBackendData,
+                                          uint64_t cbSize,
+                                          PCVDGEOMETRY pPCHSGeometry,
+                                          PCVDGEOMETRY pLCHSGeometry,
+                                          unsigned uPercentStart, unsigned uPercentSpan,
+                                          PVDINTERFACE pVDIfsDisk,
+                                          PVDINTERFACE pVDIfsImage,
+                                          PVDINTERFACE pVDIfsOperation));
+
+} VBOXHDDBACKEND;
+
+/** Pointer to VD backend. */
+typedef VBOXHDDBACKEND *PVBOXHDDBACKEND;
+
+/** Constant pointer to VD backend. */
+typedef const VBOXHDDBACKEND *PCVBOXHDDBACKEND;
+
+/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
+DECLINLINE(int) genericFileComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
+{
+    *pszLocation = NULL;
+    return VINF_SUCCESS;
+}
+/** @copydoc VBOXHDDBACKEND::pfnComposeName */
+DECLINLINE(int) genericFileComposeName(PVDINTERFACE pConfig, char **pszName)
+{
+    *pszName = NULL;
+    return VINF_SUCCESS;
+}
+
+/** Initialization entry point. */
+typedef DECLCALLBACK(int) VBOXHDDFORMATLOAD(PVBOXHDDBACKEND *ppBackendTable);
+typedef VBOXHDDFORMATLOAD *PFNVBOXHDDFORMATLOAD;
+#define VBOX_HDDFORMAT_LOAD_NAME "VBoxHDDFormatLoad"
+
+/** The prefix to identify Storage Plugins. */
+#define VBOX_HDDFORMAT_PLUGIN_PREFIX "VBoxHDD"
+/** The size of the prefix excluding the '\\0' terminator. */
+#define VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH (sizeof(VBOX_HDDFORMAT_PLUGIN_PREFIX)-1)
+
+#endif
Index: /trunk/include/VBox/vd.h
===================================================================
--- /trunk/include/VBox/vd.h	(revision 33567)
+++ /trunk/include/VBox/vd.h	(revision 33567)
@@ -0,0 +1,2674 @@
+/** @file
+ * VBox HDD Container API.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef ___VBox_VD_h
+#define ___VBox_VD_h
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/file.h>
+#include <iprt/net.h>
+#include <iprt/sg.h>
+#include <VBox/cdefs.h>
+#include <VBox/types.h>
+#include <VBox/err.h>
+
+RT_C_DECLS_BEGIN
+
+#ifdef IN_RING0
+# error "There are no VBox HDD Container APIs available in Ring-0 Host Context!"
+#endif
+
+/** @defgroup grp_vd            VBox HDD Container
+ * @{
+ */
+
+/** Current VMDK image version. */
+#define VMDK_IMAGE_VERSION          (0x0001)
+
+/** 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)
+
+/** Placeholder for specifying the last opened image. */
+#define VD_LAST_IMAGE               0xffffffffU
+
+/** @name VBox HDD container image flags
+ * @{
+ */
+/** No flags. */
+#define VD_IMAGE_FLAGS_NONE                     (0)
+/** Fixed image. */
+#define VD_IMAGE_FLAGS_FIXED                    (0x10000)
+/** Diff image. Mutually exclusive with fixed image. */
+#define VD_IMAGE_FLAGS_DIFF                     (0x20000)
+/** 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)
+/** VMDK: stream optimized image, read only. */
+#define VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED    (0x0004)
+/** VMDK: ESX variant, use in addition to other flags. */
+#define VD_VMDK_IMAGE_FLAGS_ESX                 (0x0008)
+/** 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_FIXED | VD_IMAGE_FLAGS_DIFF | VD_IMAGE_FLAGS_NONE \
+                                             |  VD_VMDK_IMAGE_FLAGS_SPLIT_2G | VD_VMDK_IMAGE_FLAGS_RAWDISK \
+                                             | VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_VMDK_IMAGE_FLAGS_ESX)
+
+/** Mask of valid image flags for VDI. */
+#define VD_VDI_IMAGE_FLAGS_MASK             (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF | VD_IMAGE_FLAGS_NONE | VD_VDI_IMAGE_FLAGS_ZERO_EXPAND)
+
+/** Mask of all valid image flags for all formats. */
+#define VD_IMAGE_FLAGS_MASK                 (VD_VMDK_IMAGE_FLAGS_MASK | VD_VDI_IMAGE_FLAGS_MASK)
+
+/** Default image flags. */
+#define VD_IMAGE_FLAGS_DEFAULT              (VD_IMAGE_FLAGS_NONE)
+/** @} */
+
+
+/**
+ * Auxiliary type for describing partitions on raw disks. The entries must be
+ * in ascending order (as far as uStart is concerned), and must not overlap.
+ * Note that this does not correspond 1:1 to partitions, it is describing the
+ * general meaning of contiguous areas on the disk.
+ */
+typedef struct VBOXHDDRAWPARTDESC
+{
+    /** Device to use for this partition/data area. Can be the disk device if
+     * the offset field is set appropriately. If this is NULL, then this
+     * partition will not be accessible to the guest. The size of the data area
+     * must still be set correctly. */
+    const char      *pszRawDevice;
+    /** Pointer to the partitioning info. NULL means this is a regular data
+     * area on disk, non-NULL denotes data which should be copied to the
+     * partition data overlay. */
+    const void      *pvPartitionData;
+    /** Offset where the data starts in this device. */
+    uint64_t        uStartOffset;
+    /** Offset where the data starts in the disk. */
+    uint64_t        uStart;
+    /** Size of the data area. */
+    uint64_t        cbData;
+} VBOXHDDRAWPARTDESC, *PVBOXHDDRAWPARTDESC;
+
+/**
+ * Auxiliary data structure for creating raw disks.
+ */
+typedef struct VBOXHDDRAW
+{
+    /** Signature for structure. Must be 'R', 'A', 'W', '\\0'. Actually a trick
+     * to make logging of the comment string produce sensible results. */
+    char            szSignature[4];
+    /** Flag whether access to full disk should be given (ignoring the
+     * partition information below). */
+    bool            fRawDisk;
+    /** Filename for the raw disk. Ignored for partitioned raw disks.
+     * For Linux e.g. /dev/sda, and for Windows e.g. \\\\.\\PhysicalDisk0. */
+    const char      *pszRawDisk;
+    /** Number of entries in the partition descriptor array. */
+    unsigned        cPartDescs;
+    /** Pointer to the partition descriptor array. */
+    PVBOXHDDRAWPARTDESC pPartDescs;
+} VBOXHDDRAW, *PVBOXHDDRAW;
+
+/** @name VBox HDD container image open mode flags
+ * @{
+ */
+/** Try to open image in read/write exclusive access mode if possible, or in read-only elsewhere. */
+#define VD_OPEN_FLAGS_NORMAL        0
+/** Open image in read-only mode with sharing access with others. */
+#define VD_OPEN_FLAGS_READONLY      RT_BIT(0)
+/** Honor zero block writes instead of ignoring them whenever possible.
+ * This is not supported by all formats. It is silently ignored in this case. */
+#define VD_OPEN_FLAGS_HONOR_ZEROES  RT_BIT(1)
+/** Honor writes of the same data instead of ignoring whenever possible.
+ * This is handled generically, and is only meaningful for differential image
+ * formats. It is silently ignored otherwise. */
+#define VD_OPEN_FLAGS_HONOR_SAME    RT_BIT(2)
+/** Do not perform the base/diff image check on open. This does NOT imply
+ * opening the image as readonly (would break e.g. adding UUIDs to VMDK files
+ * created by other products). Images opened with this flag should only be
+ * used for querying information, and nothing else. */
+#define VD_OPEN_FLAGS_INFO          RT_BIT(3)
+/** Open image for asynchronous access. Only available if VD_CAP_ASYNC_IO is
+ * set. Check with VDIsAsynchonousIoSupported whether asynchronous I/O is
+ * really supported for this file.  */
+#define VD_OPEN_FLAGS_ASYNC_IO      RT_BIT(4)
+/** Allow sharing of the image for writable images. May be ignored if the
+ * format backend doesn't support this type of concurrent access. */
+#define VD_OPEN_FLAGS_SHAREABLE     RT_BIT(5)
+/** Ask the backend to switch to sequential accesses if possible. Opening
+ * will not fail if it cannot do this, the flag will be simply ignored. */
+#define VD_OPEN_FLAGS_SEQUENTIAL    RT_BIT(6)
+/** Mask of valid flags. */
+#define VD_OPEN_FLAGS_MASK          (VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_HONOR_ZEROES | VD_OPEN_FLAGS_HONOR_SAME | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)
+/** @}*/
+
+/**
+ * Helper functions to handle open flags.
+ */
+
+/**
+ * Translate VD_OPEN_FLAGS_* to RTFile open flags.
+ *
+ * @return  RTFile open flags.
+ * @param   uOpenFlags      VD_OPEN_FLAGS_* open flags.
+ * @param   fCreate         Flag that the file should be created.
+ */
+DECLINLINE(uint32_t) VDOpenFlagsToFileOpenFlags(unsigned uOpenFlags, bool fCreate)
+{
+    AssertMsg(!((uOpenFlags & VD_OPEN_FLAGS_READONLY) && fCreate), ("Image can't be opened readonly while being created\n"));
+
+    uint32_t fOpen = 0;
+
+    if (RT_UNLIKELY(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        fOpen |= RTFILE_O_READ | RTFILE_O_DENY_NONE;
+    else
+    {
+        fOpen |= RTFILE_O_READWRITE;
+
+        if (RT_UNLIKELY(uOpenFlags & VD_OPEN_FLAGS_SHAREABLE))
+            fOpen |= RTFILE_O_DENY_NONE;
+        else
+            fOpen |= RTFILE_O_DENY_WRITE;
+    }
+
+    if (RT_UNLIKELY(fCreate))
+        fOpen |= RTFILE_O_CREATE | RTFILE_O_NOT_CONTENT_INDEXED;
+    else
+        fOpen |= RTFILE_O_OPEN;
+
+    return fOpen;
+}
+
+
+/** @name VBox HDD container backend capability flags
+ * @{
+ */
+/** Supports UUIDs as expected by VirtualBox code. */
+#define VD_CAP_UUID                 RT_BIT(0)
+/** Supports creating fixed size images, allocating all space instantly. */
+#define VD_CAP_CREATE_FIXED         RT_BIT(1)
+/** Supports creating dynamically growing images, allocating space on demand. */
+#define VD_CAP_CREATE_DYNAMIC       RT_BIT(2)
+/** Supports creating images split in chunks of a bit less than 2GBytes. */
+#define VD_CAP_CREATE_SPLIT_2G      RT_BIT(3)
+/** Supports being used as differencing image format backend. */
+#define VD_CAP_DIFF                 RT_BIT(4)
+/** Supports asynchronous I/O operations for at least some configurations. */
+#define VD_CAP_ASYNC                RT_BIT(5)
+/** The backend operates on files. The caller needs to know to handle the
+ * location appropriately. */
+#define VD_CAP_FILE                 RT_BIT(6)
+/** The backend uses the config interface. The caller needs to know how to
+ * provide the mandatory configuration parts this way. */
+#define VD_CAP_CONFIG               RT_BIT(7)
+/** The backend uses the network stack interface. The caller has to provide
+ * the appropriate interface. */
+#define VD_CAP_TCPNET               RT_BIT(8)
+/** The backend supports VFS (virtual filesystem) functionality since it uses
+ * VDINTERFACEIO exclusively for all file operations. */
+#define VD_CAP_VFS                  RT_BIT(9)
+/** @}*/
+
+/** @name VBox HDD container type.
+ * @{
+ */
+typedef enum VDTYPE
+{
+    /** Invalid. */
+    VDTYPE_INVALID = 0,
+    /** HardDisk */
+    VDTYPE_HDD,
+    /** CD/DVD */
+    VDTYPE_DVD,
+    /** Floppy. */
+    VDTYPE_FLOPPY
+} VDTYPE;
+/** @}*/
+
+/**
+ * Supported interface types.
+ */
+typedef enum VDINTERFACETYPE
+{
+    /** First valid interface. */
+    VDINTERFACETYPE_FIRST = 0,
+    /** Interface to pass error message to upper layers. Per-disk. */
+    VDINTERFACETYPE_ERROR = VDINTERFACETYPE_FIRST,
+    /** Interface for I/O operations. Per-image. */
+    VDINTERFACETYPE_IO,
+    /** Interface for progress notification. Per-operation. */
+    VDINTERFACETYPE_PROGRESS,
+    /** Interface for configuration information. Per-image. */
+    VDINTERFACETYPE_CONFIG,
+    /** Interface for TCP network stack. Per-image. */
+    VDINTERFACETYPE_TCPNET,
+    /** Interface for getting parent image state. Per-operation. */
+    VDINTERFACETYPE_PARENTSTATE,
+    /** Interface for synchronizing accesses from several threads. Per-disk. */
+    VDINTERFACETYPE_THREADSYNC,
+    /** Interface for I/O between the generic VBoxHDD code and the backend. Per-image (internal).
+     * This interface is completely internal and must not be used elsewhere. */
+    VDINTERFACETYPE_IOINT,
+    /** invalid interface. */
+    VDINTERFACETYPE_INVALID
+} VDINTERFACETYPE;
+
+/**
+ * Common structure for all interfaces.
+ */
+typedef struct VDINTERFACE
+{
+    /** Human readable interface name. */
+    const char         *pszInterfaceName;
+    /** The size of the struct. */
+    uint32_t            cbSize;
+    /** Pointer to the next common interface structure. */
+    struct VDINTERFACE *pNext;
+    /** Interface type. */
+    VDINTERFACETYPE     enmInterface;
+    /** Opaque user data which is passed on every call. */
+    void               *pvUser;
+    /** Pointer to the function call table of the interface.
+     *  As this is opaque this must be casted to the right interface
+     *  struct defined below based on the interface type in enmInterface. */
+    void               *pCallbacks;
+} VDINTERFACE;
+/** Pointer to a VDINTERFACE. */
+typedef VDINTERFACE *PVDINTERFACE;
+/** Pointer to a const VDINTERFACE. */
+typedef const VDINTERFACE *PCVDINTERFACE;
+
+/**
+ * Helper functions to handle interface lists.
+ *
+ * @note These interface lists are used consistently to pass per-disk,
+ * per-image and/or per-operation callbacks. Those three purposes are strictly
+ * separate. See the individual interface declarations for what context they
+ * apply to. The caller is responsible for ensuring that the lifetime of the
+ * interface descriptors is appropriate for the category of interface.
+ */
+
+/**
+ * Get a specific interface from a list of interfaces specified by the type.
+ *
+ * @return  Pointer to the matching interface or NULL if none was found.
+ * @param   pVDIfs          Pointer to the VD interface list.
+ * @param   enmInterface    Interface to search for.
+ */
+DECLINLINE(PVDINTERFACE) VDInterfaceGet(PVDINTERFACE pVDIfs, VDINTERFACETYPE enmInterface)
+{
+    AssertMsgReturn(   enmInterface >= VDINTERFACETYPE_FIRST
+                    && enmInterface < VDINTERFACETYPE_INVALID,
+                    ("enmInterface=%u", enmInterface), NULL);
+
+    while (pVDIfs)
+    {
+        /* Sanity checks. */
+        AssertMsgBreak(pVDIfs->cbSize == sizeof(VDINTERFACE),
+                       ("cbSize=%u\n", pVDIfs->cbSize));
+
+        if (pVDIfs->enmInterface == enmInterface)
+            return pVDIfs;
+        pVDIfs = pVDIfs->pNext;
+    }
+
+    /* No matching interface was found. */
+    return NULL;
+}
+
+/**
+ * Add an interface to a list of interfaces.
+ *
+ * @return VBox status code.
+ * @param  pInterface   Pointer to an unitialized common interface structure.
+ * @param  pszName      Name of the interface.
+ * @param  enmInterface Type of the interface.
+ * @param  pCallbacks   The callback table of the interface.
+ * @param  pvUser       Opaque user data passed on every function call.
+ * @param  ppVDIfs      Pointer to the VD interface list.
+ */
+DECLINLINE(int) VDInterfaceAdd(PVDINTERFACE pInterface, const char *pszName,
+                               VDINTERFACETYPE enmInterface, void *pCallbacks,
+                               void *pvUser, PVDINTERFACE *ppVDIfs)
+{
+    /* Argument checks. */
+    AssertMsgReturn(   enmInterface >= VDINTERFACETYPE_FIRST
+                    && enmInterface < VDINTERFACETYPE_INVALID,
+                    ("enmInterface=%u", enmInterface), VERR_INVALID_PARAMETER);
+
+    AssertMsgReturn(VALID_PTR(pCallbacks),
+                    ("pCallbacks=%#p", pCallbacks),
+                    VERR_INVALID_PARAMETER);
+
+    AssertMsgReturn(VALID_PTR(ppVDIfs),
+                    ("pInterfaceList=%#p", ppVDIfs),
+                    VERR_INVALID_PARAMETER);
+
+    /* Fill out interface descriptor. */
+    pInterface->cbSize           = sizeof(VDINTERFACE);
+    pInterface->pszInterfaceName = pszName;
+    pInterface->enmInterface     = enmInterface;
+    pInterface->pCallbacks       = pCallbacks;
+    pInterface->pvUser           = pvUser;
+    pInterface->pNext            = *ppVDIfs;
+
+    /* Remember the new start of the list. */
+    *ppVDIfs = pInterface;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Removes an interface from a list of interfaces.
+ *
+ * @return VBox status code
+ * @param  pInterface   Pointer to an initialized common interface structure to remove.
+ * @param  ppVDIfs      Pointer to the VD interface list to remove from.
+ */
+DECLINLINE(int) VDInterfaceRemove(PVDINTERFACE pInterface, PVDINTERFACE *ppVDIfs)
+{
+    int rc = VERR_NOT_FOUND;
+
+    /* Argument checks. */
+    AssertMsgReturn(VALID_PTR(pInterface),
+                    ("pInterface=%#p", pInterface),
+                    VERR_INVALID_PARAMETER);
+
+    AssertMsgReturn(VALID_PTR(ppVDIfs),
+                    ("pInterfaceList=%#p", ppVDIfs),
+                    VERR_INVALID_PARAMETER);
+
+    if (*ppVDIfs)
+    {
+        PVDINTERFACE pPrev = NULL;
+        PVDINTERFACE pCurr = *ppVDIfs;
+
+        while (   pCurr
+               && (pCurr != pInterface))
+        {
+            pPrev = pCurr;
+            pCurr = pCurr->pNext;
+        }
+
+        /* First interface */
+        if (!pPrev)
+        {
+            *ppVDIfs = pCurr->pNext;
+            rc = VINF_SUCCESS;
+        }
+        else if (pCurr)
+        {
+            pPrev = pCurr->pNext;
+            rc = VINF_SUCCESS;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Interface to deliver error messages (and also informational messages)
+ * to upper layers.
+ *
+ * Per-disk interface. Optional, but think twice if you want to miss the
+ * opportunity of reporting better human-readable error messages.
+ */
+typedef struct VDINTERFACEERROR
+{
+    /**
+     * Size of the error interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Error message callback. Must be able to accept special IPRT format
+     * strings.
+     *
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   rc              The VBox error code.
+     * @param   RT_SRC_POS_DECL Use RT_SRC_POS.
+     * @param   pszFormat       Error message format string.
+     * @param   va              Error message arguments.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnError, (void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va));
+
+    /**
+     * Informational message callback. May be NULL. Used e.g. in
+     * VDDumpImages(). Must be able to accept special IPRT format strings.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pszFormat       Message format string.
+     * @param   va              Message arguments.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnMessage, (void *pvUser, const char *pszFormat, va_list va));
+
+} VDINTERFACEERROR, *PVDINTERFACEERROR;
+
+/**
+ * Get error interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACEERROR) VDGetInterfaceError(PVDINTERFACE pInterface)
+{
+    PVDINTERFACEERROR pInterfaceError;
+
+    /* Check that the interface descriptor is a error interface. */
+    AssertMsgReturn(   pInterface->enmInterface == VDINTERFACETYPE_ERROR
+                    && pInterface->cbSize == sizeof(VDINTERFACE),
+                    ("Not an error interface"), NULL);
+
+    pInterfaceError = (PVDINTERFACEERROR)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   pInterfaceError->cbSize == sizeof(VDINTERFACEERROR)
+                    && pInterfaceError->enmInterface == VDINTERFACETYPE_ERROR,
+                    ("A non error callback table attached to a error interface descriptor\n"), NULL);
+
+    return pInterfaceError;
+}
+
+/**
+ * Completion callback which is called by the interface owner
+ * to inform the backend that a task finished.
+ *
+ * @return  VBox status code.
+ * @param   pvUser          Opaque user data which is passed on request submission.
+ * @param   rcReq           Status code of the completed request.
+ */
+typedef DECLCALLBACK(int) FNVDCOMPLETED(void *pvUser, int rcReq);
+/** Pointer to FNVDCOMPLETED() */
+typedef FNVDCOMPLETED *PFNVDCOMPLETED;
+
+/**
+ * Support interface for I/O
+ *
+ * Per-image. Optional as input.
+ */
+typedef struct VDINTERFACEIO
+{
+    /**
+     * Size of the I/O interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Open callback
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pszLocation     Name of the location to open.
+     * @param   fOpen           Flags for opening the backend.
+     *                          See RTFILE_O_* #defines, inventing another set
+     *                          of open flags is not worth the mapping effort.
+     * @param   pfnCompleted    The callback which is called whenever a task
+     *                          completed. The backend has to pass the user data
+     *                          of the request initiator (ie the one who calls
+     *                          VDAsyncRead or VDAsyncWrite) in pvCompletion
+     *                          if this is NULL.
+     * @param   ppStorage       Where to store the opaque storage handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnOpen, (void *pvUser, const char *pszLocation,
+                                        uint32_t fOpen,
+                                        PFNVDCOMPLETED pfnCompleted,
+                                        void **ppStorage));
+
+    /**
+     * Close callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The opaque storage handle to close.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pvUser, void *pStorage));
+
+    /**
+     * Delete callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of the file to delete.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnDelete, (void *pvUser, const char *pcszFilename));
+
+    /**
+     * Move callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszSrc         The path to the source file.
+     * @param   pcszDst         The path to the destination file.
+     *                          This file will be created.
+     * @param   fMove           A combination of the RTFILEMOVE_* flags.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnMove, (void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove));
+
+    /**
+     * Returns the free space on a disk.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of a file to identify the disk.
+     * @param   pcbFreeSpace    Where to store the free space of the disk.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetFreeSpace, (void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace));
+
+    /**
+     * Returns the last modification timestamp of a file.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of a file to identify the disk.
+     * @param   pModificationTime   Where to store the timestamp of the file.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetModificationTime, (void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime));
+
+    /**
+     * Returns the size of the opened storage backend.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The opaque storage handle to close.
+     * @param   pcbSize         Where to store the size of the storage backend.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetSize, (void *pvUser, void *pStorage, uint64_t *pcbSize));
+
+    /**
+     * Sets the size of the opened storage backend if possible.
+     *
+     * @return  VBox status code.
+     * @retval  VERR_NOT_SUPPORTED if the backend does not support this operation.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The opaque storage handle to close.
+     * @param   cbSize          The new size of the image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetSize, (void *pvUser, void *pStorage, uint64_t cbSize));
+
+    /**
+     * Synchronous write callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to use.
+     * @param   uOffset         The offset to start from.
+     * @param   pvBuffer        Pointer to the bits need to be written.
+     * @param   cbBuffer        How many bytes to write.
+     * @param   pcbWritten      Where to store how many bytes were actually written.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteSync, (void *pvUser, void *pStorage, uint64_t uOffset,
+                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
+
+    /**
+     * Synchronous read callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to use.
+     * @param   uOffset         The offset to start from.
+     * @param   pvBuffer        Where to store the read bits.
+     * @param   cbBuffer        How many bytes to read.
+     * @param   pcbRead         Where to store how many bytes were actually read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadSync, (void *pvUser, void *pStorage, uint64_t uOffset,
+                                            void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
+
+    /**
+     * Flush data to the storage backend.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to flush.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlushSync, (void *pvUser, void *pStorage));
+
+    /**
+     * Initiate a asynchronous read request.
+     *
+     * @return  VBox status code.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        The offset to start reading from.
+     * @param   paSegments     Scatter gather list to store the data in.
+     * @param   cSegments      Number of segments in the list.
+     * @param   cbRead         How many bytes to read.
+     * @param   pvCompletion   The opaque user data which is returned upon completion.
+     * @param   ppTask         Where to store the opaque task handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadAsync, (void *pvUser, void *pStorage, uint64_t uOffset,
+                                             PCRTSGSEG paSegments, size_t cSegments,
+                                             size_t cbRead, void *pvCompletion,
+                                             void **ppTask));
+
+    /**
+     * Initiate a asynchronous write request.
+     *
+     * @return  VBox status code.
+     * @param   pvUser         The opaque user data passed on conatiner creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        The offset to start writing to.
+     * @param   paSegments     Scatter gather list of the data to write
+     * @param   cSegments      Number of segments in the list.
+     * @param   cbWrite        How many bytes to write.
+     * @param   pvCompletion   The opaque user data which is returned upon completion.
+     * @param   ppTask         Where to store the opaque task handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteAsync, (void *pvUser, void *pStorage, uint64_t uOffset,
+                                              PCRTSGSEG paSegments, size_t cSegments,
+                                              size_t cbWrite, void *pvCompletion,
+                                              void **ppTask));
+
+    /**
+     * Initiates a async flush request.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to flush.
+     * @param   pvCompletion    The opaque user data which is returned upon completion.
+     * @param   ppTask          Where to store the opaque task handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlushAsync, (void *pvUser, void *pStorage,
+                                              void *pvCompletion, void **ppTask));
+
+} VDINTERFACEIO, *PVDINTERFACEIO;
+
+/**
+ * Get I/O interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACEIO) VDGetInterfaceIO(PVDINTERFACE pInterface)
+{
+    PVDINTERFACEIO pInterfaceIO;
+
+    /* Check that the interface descriptor is an I/O interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_IO)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not an I/O interface"), NULL);
+
+    pInterfaceIO = (PVDINTERFACEIO)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceIO->cbSize == sizeof(VDINTERFACEIO))
+                    && (pInterfaceIO->enmInterface == VDINTERFACETYPE_IO),
+                    ("A non I/O callback table attached to an I/O interface descriptor\n"), NULL);
+
+    return pInterfaceIO;
+}
+
+/**
+ * Callback which provides progress information about a currently running
+ * lengthy operation.
+ *
+ * @return  VBox status code.
+ * @param   pvUser          The opaque user data associated with this interface.
+ * @param   uPercent        Completion percentage.
+ */
+typedef DECLCALLBACK(int) FNVDPROGRESS(void *pvUser, unsigned uPercentage);
+/** Pointer to FNVDPROGRESS() */
+typedef FNVDPROGRESS *PFNVDPROGRESS;
+
+/**
+ * Progress notification interface
+ *
+ * Per-operation. Optional.
+ */
+typedef struct VDINTERFACEPROGRESS
+{
+    /**
+     * Size of the progress interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Progress notification callbacks.
+     */
+    PFNVDPROGRESS pfnProgress;
+
+} VDINTERFACEPROGRESS, *PVDINTERFACEPROGRESS;
+
+/**
+ * Get progress interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACEPROGRESS) VDGetInterfaceProgress(PVDINTERFACE pInterface)
+{
+    PVDINTERFACEPROGRESS pInterfaceProgress;
+
+    /* Check that the interface descriptor is a progress interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_PROGRESS)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not a progress interface"), NULL);
+
+
+    pInterfaceProgress = (PVDINTERFACEPROGRESS)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceProgress->cbSize == sizeof(VDINTERFACEPROGRESS))
+                    && (pInterfaceProgress->enmInterface == VDINTERFACETYPE_PROGRESS),
+                    ("A non progress callback table attached to a progress interface descriptor\n"), NULL);
+
+    return pInterfaceProgress;
+}
+
+
+/**
+ * Configuration information interface
+ *
+ * Per-image. Optional for most backends, but mandatory for images which do
+ * not operate on files (including standard block or character devices).
+ */
+typedef struct VDINTERFACECONFIG
+{
+    /**
+     * Size of the configuration interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Validates that the keys are within a set of valid names.
+     *
+     * @return  true if all key names are found in pszzAllowed.
+     * @return  false if not.
+     * @param   pvUser          The opaque user data associated with this interface.
+     * @param   pszzValid       List of valid key names separated by '\\0' and ending with
+     *                          a double '\\0'.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnAreKeysValid, (void *pvUser, const char *pszzValid));
+
+    /**
+     * Retrieves the length of the string value associated with a key (including
+     * the terminator, for compatibility with CFGMR3QuerySize).
+     *
+     * @return  VBox status code.
+     *          VERR_CFGM_VALUE_NOT_FOUND means that the key is not known.
+     * @param   pvUser          The opaque user data associated with this interface.
+     * @param   pszName         Name of the key to query.
+     * @param   pcbValue        Where to store the value length. Non-NULL.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnQuerySize, (void *pvUser, const char *pszName, size_t *pcbValue));
+
+    /**
+     * Query the string value associated with a key.
+     *
+     * @return  VBox status code.
+     *          VERR_CFGM_VALUE_NOT_FOUND means that the key is not known.
+     *          VERR_CFGM_NOT_ENOUGH_SPACE means that the buffer is not big enough.
+     * @param   pvUser          The opaque user data associated with this interface.
+     * @param   pszName         Name of the key to query.
+     * @param   pszValue        Pointer to buffer where to store value.
+     * @param   cchValue        Length of value buffer.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnQuery, (void *pvUser, const char *pszName, char *pszValue, size_t cchValue));
+
+} VDINTERFACECONFIG, *PVDINTERFACECONFIG;
+
+/**
+ * Get configuration information interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACECONFIG) VDGetInterfaceConfig(PVDINTERFACE pInterface)
+{
+    PVDINTERFACECONFIG pInterfaceConfig;
+
+    /* Check that the interface descriptor is a config interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_CONFIG)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not a config interface"), NULL);
+
+    pInterfaceConfig = (PVDINTERFACECONFIG)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceConfig->cbSize == sizeof(VDINTERFACECONFIG))
+                    && (pInterfaceConfig->enmInterface == VDINTERFACETYPE_CONFIG),
+                    ("A non config callback table attached to a config interface descriptor\n"), NULL);
+
+    return pInterfaceConfig;
+}
+
+/**
+ * Query configuration, validates that the keys are within a set of valid names.
+ *
+ * @return  true if all key names are found in pszzAllowed.
+ * @return  false if not.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszzValid   List of valid names separated by '\\0' and ending with
+ *                      a double '\\0'.
+ */
+DECLINLINE(bool) VDCFGAreKeysValid(PVDINTERFACECONFIG pCfgIf, void *pvUser,
+                                   const char *pszzValid)
+{
+    return pCfgIf->pfnAreKeysValid(pvUser, pszzValid);
+}
+
+/**
+ * Query configuration, unsigned 64-bit integer value with default.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an integer value
+ * @param   pu64        Where to store the value. Set to default on failure.
+ * @param   u64Def      The default value.
+ */
+DECLINLINE(int) VDCFGQueryU64Def(PVDINTERFACECONFIG pCfgIf, void *pvUser,
+                                 const char *pszName, uint64_t *pu64,
+                                 uint64_t u64Def)
+{
+    char aszBuf[32];
+    int rc = pCfgIf->pfnQuery(pvUser, pszName, aszBuf, sizeof(aszBuf));
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTStrToUInt64Full(aszBuf, 0, pu64);
+    }
+    else if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+    {
+        rc = VINF_SUCCESS;
+        *pu64 = u64Def;
+    }
+    return rc;
+}
+
+/**
+ * Query configuration, unsigned 32-bit integer value with default.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an integer value
+ * @param   pu32        Where to store the value. Set to default on failure.
+ * @param   u32Def      The default value.
+ */
+DECLINLINE(int) VDCFGQueryU32Def(PVDINTERFACECONFIG pCfgIf, void *pvUser,
+                                 const char *pszName, uint32_t *pu32,
+                                 uint32_t u32Def)
+{
+    uint64_t u64;
+    int rc = VDCFGQueryU64Def(pCfgIf, pvUser, pszName, &u64, u32Def);
+    if (RT_SUCCESS(rc))
+    {
+        if (!(u64 & UINT64_C(0xffffffff00000000)))
+            *pu32 = (uint32_t)u64;
+        else
+            rc = VERR_CFGM_INTEGER_TOO_BIG;
+    }
+    return rc;
+}
+
+/**
+ * Query configuration, bool value with default.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an integer value
+ * @param   pf          Where to store the value. Set to default on failure.
+ * @param   fDef        The default value.
+ */
+DECLINLINE(int) VDCFGQueryBoolDef(PVDINTERFACECONFIG pCfgIf, void *pvUser,
+                                  const char *pszName, bool *pf,
+                                  bool fDef)
+{
+    uint64_t u64;
+    int rc = VDCFGQueryU64Def(pCfgIf, pvUser, pszName, &u64, fDef);
+    if (RT_SUCCESS(rc))
+        *pf = u64 ? true : false;
+    return rc;
+}
+
+/**
+ * Query configuration, dynamically allocated (RTMemAlloc) zero terminated
+ * character value.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an zero terminated character value
+ * @param   ppszString  Where to store the string pointer. Not set on failure.
+ *                      Free this using RTMemFree().
+ */
+DECLINLINE(int) VDCFGQueryStringAlloc(PVDINTERFACECONFIG pCfgIf,
+                                      void *pvUser, const char *pszName,
+                                      char **ppszString)
+{
+    size_t cb;
+    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
+    if (RT_SUCCESS(rc))
+    {
+        char *pszString = (char *)RTMemAlloc(cb);
+        if (pszString)
+        {
+            rc = pCfgIf->pfnQuery(pvUser, pszName, pszString, cb);
+            if (RT_SUCCESS(rc))
+                *ppszString = pszString;
+            else
+                RTMemFree(pszString);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    return rc;
+}
+
+/**
+ * Query configuration, dynamically allocated (RTMemAlloc) zero terminated
+ * character value with default.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an zero terminated character value
+ * @param   ppszString  Where to store the string pointer. Not set on failure.
+ *                      Free this using RTMemFree().
+ * @param   pszDef      The default value.
+ */
+DECLINLINE(int) VDCFGQueryStringAllocDef(PVDINTERFACECONFIG pCfgIf,
+                                         void *pvUser, const char *pszName,
+                                         char **ppszString,
+                                         const char *pszDef)
+{
+    size_t cb;
+    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+    {
+        cb = strlen(pszDef) + 1;
+        rc = VINF_SUCCESS;
+    }
+    if (RT_SUCCESS(rc))
+    {
+        char *pszString = (char *)RTMemAlloc(cb);
+        if (pszString)
+        {
+            rc = pCfgIf->pfnQuery(pvUser, pszName, pszString, cb);
+            if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+            {
+                memcpy(pszString, pszDef, cb);
+                rc = VINF_SUCCESS;
+            }
+            if (RT_SUCCESS(rc))
+                *ppszString = pszString;
+            else
+                RTMemFree(pszString);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    return rc;
+}
+
+/**
+ * Query configuration, dynamically allocated (RTMemAlloc) byte string value.
+ *
+ * @return  VBox status code.
+ * @param   pCfgIf      Pointer to configuration callback table.
+ * @param   pvUser      The opaque user data associated with this interface.
+ * @param   pszName     Name of an zero terminated character value
+ * @param   ppvData     Where to store the byte string pointer. Not set on failure.
+ *                      Free this using RTMemFree().
+ * @param   pcbData     Where to store the byte string length.
+ */
+DECLINLINE(int) VDCFGQueryBytesAlloc(PVDINTERFACECONFIG pCfgIf,
+                                     void *pvUser, const char *pszName,
+                                     void **ppvData, size_t *pcbData)
+{
+    size_t cb;
+    int rc = pCfgIf->pfnQuerySize(pvUser, pszName, &cb);
+    if (RT_SUCCESS(rc))
+    {
+        char *pbData;
+        Assert(cb);
+
+        pbData = (char *)RTMemAlloc(cb);
+        if (pbData)
+        {
+            rc = pCfgIf->pfnQuery(pvUser, pszName, pbData, cb);
+            if (RT_SUCCESS(rc))
+            {
+                *ppvData = pbData;
+                *pcbData = cb - 1; /* Exclude terminator of the queried string. */
+            }
+            else
+                RTMemFree(pbData);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+    }
+    return rc;
+}
+
+/** Forward declaration of a VD socket. */
+typedef struct VDSOCKETINT *VDSOCKET;
+/** Pointer to a VD socket. */
+typedef VDSOCKET *PVDSOCKET;
+/** Nil socket handle. */
+#define NIL_VDSOCKET ((VDSOCKET)0)
+
+/** Connect flag to indicate that the backend wants to use the extended
+ * socket I/O multiplexing call. This might not be supported on all configurations
+ * (internal networking and iSCSI)
+ * and the backend needs to take appropriate action.
+ */
+#define VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT RT_BIT_32(0)
+
+/** @name Select events
+ * @{ */
+/** Readable without blocking. */
+#define VD_INTERFACETCPNET_EVT_READ         RT_BIT_32(0)
+/** Writable without blocking. */
+#define VD_INTERFACETCPNET_EVT_WRITE        RT_BIT_32(1)
+/** Error condition, hangup, exception or similar. */
+#define VD_INTERFACETCPNET_EVT_ERROR        RT_BIT_32(2)
+/** Hint for the select that getting interrupted while waiting is more likely.
+ * The interface implementation can optimize the waiting strategy based on this.
+ * It is assumed that it is more likely to get one of the above socket events
+ * instead of being interrupted if the flag is not set. */
+#define VD_INTERFACETCPNET_HINT_INTERRUPT   RT_BIT_32(3)
+/** Mask of the valid bits. */
+#define VD_INTERFACETCPNET_EVT_VALID_MASK   UINT32_C(0x0000000f)
+/** @} */
+
+/**
+ * TCP network stack interface
+ *
+ * Per-image. Mandatory for backends which have the VD_CAP_TCPNET bit set.
+ */
+typedef struct VDINTERFACETCPNET
+{
+    /**
+     * Size of the configuration interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Creates a socket. The socket is not connected if this succeeds.
+     *
+     * @return  iprt status code.
+     * @retval  VERR_NOT_SUPPORTED if the combination of flags is not supported.
+     * @param   fFlags    Combination of the VD_INTERFACETCPNET_CONNECT_* #defines.
+     * @param   pSock     Where to store the handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSocketCreate, (uint32_t fFlags, PVDSOCKET pSock));
+
+    /**
+     * Destroys the socket.
+     *
+     * @return iprt status code.
+     * @param  Sock       Socket descriptor.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSocketDestroy, (VDSOCKET Sock));
+
+    /**
+     * Connect as a client to a TCP port.
+     *
+     * @return  iprt status code.
+     * @param   Sock            Socket descriptor.
+     * @param   pszAddress      The address to connect to.
+     * @param   uPort           The port to connect to.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClientConnect, (VDSOCKET Sock, const char *pszAddress, uint32_t uPort));
+
+    /**
+     * Close a TCP connection.
+     *
+     * @return  iprt status code.
+     * @param   Sock            Socket descriptor.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClientClose, (VDSOCKET Sock));
+
+    /**
+     * Returns whether the socket is currently connected to the client.
+     *
+     * @returns true if the socket is connected.
+     *          false otherwise.
+     * @param   Sock        Socket descriptor.
+     */
+    DECLR3CALLBACKMEMBER(bool, pfnIsClientConnected, (VDSOCKET Sock));
+
+    /**
+     * Socket I/O multiplexing.
+     * Checks if the socket is ready for reading.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   cMillies    Number of milliseconds to wait for the socket.
+     *                      Use RT_INDEFINITE_WAIT to wait for ever.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSelectOne, (VDSOCKET Sock, RTMSINTERVAL cMillies));
+
+    /**
+     * Receive data from a socket.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pvBuffer    Where to put the data we read.
+     * @param   cbBuffer    Read buffer size.
+     * @param   pcbRead     Number of bytes read.
+     *                      If NULL the entire buffer will be filled upon successful return.
+     *                      If not NULL a partial read can be done successfully.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnRead, (VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
+
+    /**
+     * Send data to a socket.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pvBuffer    Buffer to write data to socket.
+     * @param   cbBuffer    How much to write.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWrite, (VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer));
+
+    /**
+     * Send data from scatter/gather buffer to a socket.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pSgBuffer   Scatter/gather buffer to write data to socket.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSgWrite, (VDSOCKET Sock, PCRTSGBUF pSgBuffer));
+
+    /**
+     * Receive data from a socket - not blocking.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pvBuffer    Where to put the data we read.
+     * @param   cbBuffer    Read buffer size.
+     * @param   pcbRead     Number of bytes read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadNB, (VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
+
+    /**
+     * Send data to a socket - not blocking.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pvBuffer    Buffer to write data to socket.
+     * @param   cbBuffer    How much to write.
+     * @param   pcbWritten  Number of bytes written.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteNB, (VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
+
+    /**
+     * Send data from scatter/gather buffer to a socket - not blocking.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pSgBuffer   Scatter/gather buffer to write data to socket.
+     * @param   pcbWritten  Number of bytes written.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSgWriteNB, (VDSOCKET Sock, PRTSGBUF pSgBuffer, size_t *pcbWritten));
+
+    /**
+     * Flush socket write buffers.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlush, (VDSOCKET Sock));
+
+    /**
+     * Enables or disables delaying sends to coalesce packets.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   fEnable     When set to true enables coalescing.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetSendCoalescing, (VDSOCKET Sock, bool fEnable));
+
+    /**
+     * Gets the address of the local side.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pAddr       Where to store the local address on success.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetLocalAddress, (VDSOCKET Sock, PRTNETADDR pAddr));
+
+    /**
+     * Gets the address of the other party.
+     *
+     * @return  iprt status code.
+     * @param   Sock        Socket descriptor.
+     * @param   pAddr       Where to store the peer address on success.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetPeerAddress, (VDSOCKET Sock, PRTNETADDR pAddr));
+
+    /**
+     * Socket I/O multiplexing - extended version which can be woken up.
+     * Checks if the socket is ready for reading or writing.
+     *
+     * @return  iprt status code.
+     * @retval  VERR_INTERRUPTED if the thread was woken up by a pfnPoke call.
+     * @param   Sock        Socket descriptor.
+     * @param   fEvents     Mask of events to wait for.
+     * @param   pfEvents    Where to store the received events.
+     * @param   cMillies    Number of milliseconds to wait for the socket.
+     *                      Use RT_INDEFINITE_WAIT to wait for ever.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSelectOneEx, (VDSOCKET Sock, uint32_t fEvents,
+                                               uint32_t *pfEvents, RTMSINTERVAL cMillies));
+
+    /**
+     * Wakes up the thread waiting in pfnSelectOneEx.
+     *
+     * @return iprt status code.
+     * @param  Sock        Socket descriptor.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnPoke, (VDSOCKET Sock));
+
+} VDINTERFACETCPNET, *PVDINTERFACETCPNET;
+
+/**
+ * Get TCP network stack interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACETCPNET) VDGetInterfaceTcpNet(PVDINTERFACE pInterface)
+{
+    PVDINTERFACETCPNET pInterfaceTcpNet;
+
+    /* Check that the interface descriptor is a TCP network stack interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_TCPNET)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not a TCP network stack interface"), NULL);
+
+    pInterfaceTcpNet = (PVDINTERFACETCPNET)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceTcpNet->cbSize == sizeof(VDINTERFACETCPNET))
+                    && (pInterfaceTcpNet->enmInterface == VDINTERFACETYPE_TCPNET),
+                    ("A non TCP network stack callback table attached to a TCP network stack interface descriptor\n"), NULL);
+
+    return pInterfaceTcpNet;
+}
+
+/**
+ * Interface to get the parent state.
+ *
+ * Per-operation interface. Optional, present only if there is a parent, and
+ * used only internally for compacting.
+ */
+typedef struct VDINTERFACEPARENTSTATE
+{
+    /**
+     * Size of the parent state interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Read data callback.
+     *
+     * @return  VBox status code.
+     * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
+     * @param   pvUser          The opaque data passed for the operation.
+     * @param   uOffset         Offset of first reading byte from start of disk.
+     *                          Must be aligned to a sector boundary.
+     * @param   pvBuffer        Pointer to buffer for reading data.
+     * @param   cbBuffer        Number of bytes to read.
+     *                          Must be aligned to a sector boundary.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnParentRead, (void *pvUser, uint64_t uOffset, void *pvBuffer, size_t cbBuffer));
+
+} VDINTERFACEPARENTSTATE, *PVDINTERFACEPARENTSTATE;
+
+
+/**
+ * Get parent state interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACEPARENTSTATE) VDGetInterfaceParentState(PVDINTERFACE pInterface)
+{
+    PVDINTERFACEPARENTSTATE pInterfaceParentState;
+
+    /* Check that the interface descriptor is a parent state interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_PARENTSTATE)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not a parent state interface"), NULL);
+
+    pInterfaceParentState = (PVDINTERFACEPARENTSTATE)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceParentState->cbSize == sizeof(VDINTERFACEPARENTSTATE))
+                    && (pInterfaceParentState->enmInterface == VDINTERFACETYPE_PARENTSTATE),
+                    ("A non parent state callback table attached to a parent state interface descriptor\n"), NULL);
+
+    return pInterfaceParentState;
+}
+
+/**
+ * Interface to synchronize concurrent accesses by several threads.
+ *
+ * @note The scope of this interface is to manage concurrent accesses after
+ * the HDD container has been created, and they must stop before destroying the
+ * container. Opening or closing images is covered by the synchronization, but
+ * that does not mean it is safe to close images while a thread executes
+ * <link to="VDMerge"/> or <link to="VDCopy"/> operating on these images.
+ * Making them safe would require the lock to be held during the entire
+ * operation, which prevents other concurrent acitivities.
+ *
+ * @note Right now this is kept as simple as possible, and does not even
+ * attempt to provide enough information to allow e.g. concurrent write
+ * accesses to different areas of the disk. The reason is that it is very
+ * difficult to predict which area of a disk is affected by a write,
+ * especially when different image formats are mixed. Maybe later a more
+ * sophisticated interface will be provided which has the necessary information
+ * about worst case affected areas.
+ *
+ * Per-disk interface. Optional, needed if the disk is accessed concurrently
+ * by several threads, e.g. when merging diff images while a VM is running.
+ */
+typedef struct VDINTERFACETHREADSYNC
+{
+    /**
+     * Size of the thread synchronization interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Start a read operation.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnStartRead, (void *pvUser));
+
+    /**
+     * Finish a read operation.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFinishRead, (void *pvUser));
+
+    /**
+     * Start a write operation.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnStartWrite, (void *pvUser));
+
+    /**
+     * Finish a write operation.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFinishWrite, (void *pvUser));
+
+} VDINTERFACETHREADSYNC, *PVDINTERFACETHREADSYNC;
+
+/**
+ * Get thread synchronization interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACETHREADSYNC) VDGetInterfaceThreadSync(PVDINTERFACE pInterface)
+{
+    PVDINTERFACETHREADSYNC pInterfaceThreadSync;
+
+    /* Check that the interface descriptor is a thread synchronization interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_THREADSYNC)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not a thread synchronization interface"), NULL);
+
+    pInterfaceThreadSync = (PVDINTERFACETHREADSYNC)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceThreadSync->cbSize == sizeof(VDINTERFACETHREADSYNC))
+                    && (pInterfaceThreadSync->enmInterface == VDINTERFACETYPE_THREADSYNC),
+                    ("A non thread synchronization callback table attached to a thread synchronization interface descriptor\n"), NULL);
+
+    return pInterfaceThreadSync;
+}
+
+/** @name Configuration interface key handling flags.
+ * @{
+ */
+/** Mandatory config key. Not providing a value for this key will cause
+ * the backend to fail. */
+#define VD_CFGKEY_MANDATORY         RT_BIT(0)
+/** Expert config key. Not showing it by default in the GUI is is probably
+ * a good idea, as the average user won't understand it easily. */
+#define VD_CFGKEY_EXPERT            RT_BIT(1)
+/** @}*/
+
+
+/**
+ * Configuration value type for configuration information interface.
+ */
+typedef enum VDCFGVALUETYPE
+{
+    /** Integer value. */
+    VDCFGVALUETYPE_INTEGER = 1,
+    /** String value. */
+    VDCFGVALUETYPE_STRING,
+    /** Bytestring value. */
+    VDCFGVALUETYPE_BYTES
+} VDCFGVALUETYPE;
+
+
+/**
+ * Structure describing configuration keys required/supported by a backend
+ * through the config interface.
+ */
+typedef struct VDCONFIGINFO
+{
+    /** Key name of the configuration. */
+    const char *pszKey;
+    /** Pointer to default value (descriptor). NULL if no useful default value
+     * can be specified. */
+    const char *pszDefaultValue;
+    /** Value type for this key. */
+    VDCFGVALUETYPE enmValueType;
+    /** Key handling flags (a combination of VD_CFGKEY_* flags). */
+    uint64_t uKeyFlags;
+} VDCONFIGINFO;
+
+/** Pointer to structure describing configuration keys. */
+typedef VDCONFIGINFO *PVDCONFIGINFO;
+
+/** Pointer to const structure describing configuration keys. */
+typedef const VDCONFIGINFO *PCVDCONFIGINFO;
+
+/**
+ * Structure describing a file extension.
+ */
+typedef struct VDFILEEXTENSION
+{
+    /** Pointer to the NULL-terminated string containing the extension. */
+    const char *pszExtension;
+    /** The device type the extension supports. */
+    VDTYPE      enmType;
+} VDFILEEXTENSION;
+
+/** Pointer to a structure describing a file extension. */
+typedef VDFILEEXTENSION *PVDFILEEXTENSION;
+
+/** Pointer to a const structure describing a file extension. */
+typedef const VDFILEEXTENSION *PCVDFILEEXTENSION;
+
+/**
+ * Data structure for returning a list of backend capabilities.
+ */
+typedef struct VDBACKENDINFO
+{
+    /** Name of the backend. Must be unique even with case insensitive comparison. */
+    const char *pszBackend;
+    /** Capabilities of the backend (a combination of the VD_CAP_* flags). */
+    uint64_t uBackendCaps;
+    /** Pointer to a NULL-terminated array of strings, containing the supported
+     * file extensions. Note that some backends do not work on files, so this
+     * pointer may just contain NULL. */
+    PCVDFILEEXTENSION paFileExtensions;
+    /** Pointer to an array of structs describing each supported config key.
+     * Terminated by a NULL config key. Note that some backends do not support
+     * the configuration interface, so this pointer may just contain NULL.
+     * Mandatory if the backend sets VD_CAP_CONFIG. */
+    PCVDCONFIGINFO paConfigInfo;
+    /** Returns a human readable hard disk location string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the full file path for image-based hard disks.
+     *  Mandatory for backends with no VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeLocation, (PVDINTERFACE pConfig, char **pszLocation));
+    /** Returns a human readable hard disk name string given a
+     *  set of hard disk configuration keys. The returned string is an
+     *  equivalent of the file name part in the full file path for
+     *  image-based hard disks. Mandatory for backends with no
+     *  VD_CAP_FILE and NULL otherwise. */
+    DECLR3CALLBACKMEMBER(int, pfnComposeName, (PVDINTERFACE pConfig, char **pszName));
+} VDBACKENDINFO, *PVDBACKENDINFO;
+
+
+/** Forward declaration. Only visible in the VBoxHDD module. */
+/** I/O context */
+typedef struct VDIOCTX *PVDIOCTX;
+/** Storage backend handle. */
+typedef struct VDIOSTORAGE *PVDIOSTORAGE;
+/** Pointer to a storage backend handle. */
+typedef PVDIOSTORAGE *PPVDIOSTORAGE;
+
+/**
+ * Completion callback for meta/userdata reads or writes.
+ *
+ * @return  VBox status code.
+ *          VINF_SUCCESS if everything was successful and the transfer can continue.
+ *          VERR_VD_ASYNC_IO_IN_PROGRESS if there is another data transfer pending.
+ * @param   pBackendData    The opaque backend data.
+ * @param   pIoCtx          I/O context associated with this request.
+ * @param   pvUser          Opaque user data passed during a read/write request.
+ * @param   rcReq           Status code for the completed request.
+ */
+typedef DECLCALLBACK(int) FNVDXFERCOMPLETED(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq);
+/** Pointer to FNVDXFERCOMPLETED() */
+typedef FNVDXFERCOMPLETED *PFNVDXFERCOMPLETED;
+
+/** Metadata transfer handle. */
+typedef struct VDMETAXFER *PVDMETAXFER;
+/** Pointer to a metadata transfer handle. */
+typedef PVDMETAXFER *PPVDMETAXFER;
+
+
+/**
+ * Internal I/O interface between the generic VD layer and the backends.
+ *
+ * Per-image. Always passed to backends.
+ */
+typedef struct VDINTERFACEIOINT
+{
+    /**
+     * Size of the internal I/O interface.
+     */
+    uint32_t    cbSize;
+
+    /**
+     * Interface type.
+     */
+    VDINTERFACETYPE enmInterface;
+
+    /**
+     * Open callback
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pszLocation     Name of the location to open.
+     * @param   fOpen           Flags for opening the backend.
+     *                          See RTFILE_O_* #defines, inventing another set
+     *                          of open flags is not worth the mapping effort.
+     * @param   ppStorage       Where to store the storage handle.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnOpen, (void *pvUser, const char *pszLocation,
+                                        uint32_t fOpen, PPVDIOSTORAGE ppStorage));
+
+    /**
+     * Close callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to close.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnClose, (void *pvUser, PVDIOSTORAGE pStorage));
+
+    /**
+     * Delete callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of the file to delete.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnDelete, (void *pvUser, const char *pcszFilename));
+
+    /**
+     * Move callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszSrc         The path to the source file.
+     * @param   pcszDst         The path to the destination file.
+     *                          This file will be created.
+     * @param   fMove           A combination of the RTFILEMOVE_* flags.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnMove, (void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove));
+
+    /**
+     * Returns the free space on a disk.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of a file to identify the disk.
+     * @param   pcbFreeSpace    Where to store the free space of the disk.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetFreeSpace, (void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace));
+
+    /**
+     * Returns the last modification timestamp of a file.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pcszFilename    Name of a file to identify the disk.
+     * @param   pModificationTime   Where to store the timestamp of the file.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetModificationTime, (void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime));
+
+    /**
+     * Returns the size of the opened storage backend.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to get the size from.
+     * @param   pcbSize         Where to store the size of the storage backend.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnGetSize, (void *pvUser, PVDIOSTORAGE pStorage,
+                                           uint64_t *pcbSize));
+
+    /**
+     * Sets the size of the opened storage backend if possible.
+     *
+     * @return  VBox status code.
+     * @retval  VERR_NOT_SUPPORTED if the backend does not support this operation.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle.
+     * @param   cbSize          The new size of the image.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnSetSize, (void *pvUser, PVDIOSTORAGE pStorage,
+                                           uint64_t cbSize));
+
+    /**
+     * Synchronous write callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to use.
+     * @param   uOffset         The offset to start from.
+     * @param   pvBuffer        Pointer to the bits need to be written.
+     * @param   cbBuffer        How many bytes to write.
+     * @param   pcbWritten      Where to store how many bytes were actually written.
+     *
+     * @notes Do not use in code called from the async read/write entry points in the backends.
+     *        This should be only used during open/close of images and for the support functions
+     *        which are not called while a VM is running (pfnCompact).
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteSync, (void *pvUser, PVDIOSTORAGE pStorage, uint64_t uOffset,
+                                             const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten));
+
+    /**
+     * Synchronous read callback.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to use.
+     * @param   uOffset         The offset to start from.
+     * @param   pvBuffer        Where to store the read bits.
+     * @param   cbBuffer        How many bytes to read.
+     * @param   pcbRead         Where to store how many bytes were actually read.
+     *
+     * @notes See pfnWriteSync()
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadSync, (void *pvUser, PVDIOSTORAGE pStorage, uint64_t uOffset,
+                                            void *pvBuffer, size_t cbBuffer, size_t *pcbRead));
+
+    /**
+     * Flush data to the storage backend.
+     *
+     * @return  VBox status code.
+     * @param   pvUser          The opaque data passed on container creation.
+     * @param   pStorage        The storage handle to flush.
+     *
+     * @notes See pfnWriteSync()
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlushSync, (void *pvUser, PVDIOSTORAGE pStorage));
+
+    /**
+     * Initiate a asynchronous read request for user data.
+     *
+     * @return  VBox status code.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        The offset to start reading from.
+     * @param   pIoCtx         I/O context passed in VDAsyncRead/Write.
+     * @param   cbRead         How many bytes to read.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadUserAsync, (void *pvUser, PVDIOSTORAGE pStorage,
+                                                 uint64_t uOffset, PVDIOCTX pIoCtx,
+                                                 size_t cbRead));
+
+    /**
+     * Initiate a asynchronous write request for user data.
+     *
+     * @return  VBox status code.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        The offset to start writing to.
+     * @param   pIoCtx         I/O context passed in VDAsyncRead/Write
+     * @param   cbWrite        How many bytes to write.
+     * @param   pfnCompleted   Completion callback.
+     * @param   pvCompleteUser Opaque user data passed in the completion callback.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteUserAsync, (void *pvUser, PVDIOSTORAGE pStorage,
+                                                  uint64_t uOffset, PVDIOCTX pIoCtx,
+                                                  size_t cbWrite,
+                                                  PFNVDXFERCOMPLETED pfnComplete,
+                                                  void *pvCompleteUser));
+
+    /**
+     * Reads metadata asynchronously from storage.
+     * The current I/O context will be halted.
+     *
+     * @returns VBox status code.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        Offset to start reading from.
+     * @param   pvBuffer       Where to store the data.
+     * @param   cbBuffer       How many bytes to read.
+     * @param   pIoCtx         The I/O context which triggered the read.
+     * @param   ppMetaXfer     Where to store the metadata transfer handle on success.
+     * @param   pfnCompleted   Completion callback.
+     * @param   pvCompleteUser Opaque user data passed in the completion callback.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnReadMetaAsync, (void *pvUser, PVDIOSTORAGE pStorage,
+                                                 uint64_t uOffset, void *pvBuffer,
+                                                 size_t cbBuffer, PVDIOCTX pIoCtx,
+                                                 PPVDMETAXFER ppMetaXfer,
+                                                 PFNVDXFERCOMPLETED pfnComplete,
+                                                 void *pvCompleteUser));
+
+    /**
+     * Writes metadata asynchronously to storage.
+     *
+     * @returns VBox status code.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pStorage       The storage handle.
+     * @param   uOffset        Offset to start writing to.
+     * @param   pvBuffer       Written data.
+     * @param   cbBuffer       How many bytes to write.
+     * @param   pIoCtx         The I/O context which triggered the write.
+     * @param   pfnCompleted   Completion callback.
+     * @param   pvCompleteUser Opaque user data passed in the completion callback.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnWriteMetaAsync, (void *pvUser, PVDIOSTORAGE pStorage,
+                                                  uint64_t uOffset, void *pvBuffer,
+                                                  size_t cbBuffer, PVDIOCTX pIoCtx,
+                                                  PFNVDXFERCOMPLETED pfnComplete,
+                                                  void *pvCompleteUser));
+
+    /**
+     * Releases a metadata transfer handle.
+     * The free space can be used for another transfer.
+     *
+     * @returns nothing.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pMetaXfer      The metadata transfer handle to release.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnMetaXferRelease, (void *pvUser, PVDMETAXFER pMetaXfer));
+
+    /**
+     * Initiates a async flush request.
+     *
+     * @return  VBox status code.
+     * @param   pvUser         The opaque data passed on container creation.
+     * @param   pStorage       The storage handle to flush.
+     * @param   pIoCtx         I/O context which triggered the flush.
+     * @param   pfnCompleted   Completion callback.
+     * @param   pvCompleteUser Opaque user data passed in the completion callback.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnFlushAsync, (void *pvUser, PVDIOSTORAGE pStorage,
+                                              PVDIOCTX pIoCtx,
+                                              PFNVDXFERCOMPLETED pfnComplete,
+                                              void *pvCompleteUser));
+
+    /**
+     * Copies a buffer into the I/O context.
+     *
+     * @return Number of bytes copied.
+     * @param  pvUser          The opaque user data passed on container creation.
+     * @param  pIoCtx          I/O context to copy the data to.
+     * @param  pvBuffer        Buffer to copy.
+     * @param  cbBuffer        Number of bytes to copy.
+     */
+    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxCopyTo, (void *pvUser, PVDIOCTX pIoCtx,
+                                                  void *pvBuffer, size_t cbBuffer));
+
+    /**
+     * Copies data from the I/O context into a buffer.
+     *
+     * @return Number of bytes copied.
+     * @param  pvUser          The opaque user data passed on container creation.
+     * @param  pIoCtx          I/O context to copy the data from.
+     * @param  pvBuffer        Destination buffer.
+     * @param  cbBuffer        Number of bytes to copy.
+     */
+    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxCopyFrom, (void *pvUser, PVDIOCTX pIoCtx,
+                                                    void *pvBuffer, size_t cbBuffer));
+
+    /**
+     * Sets the buffer of the given context to a specific byte.
+     *
+     * @return Number of bytes set.
+     * @param  pvUser          The opaque user data passed on container creation.
+     * @param  pIoCtx          I/O context to copy the data from.
+     * @param  ch              The byte to set.
+     * @param  cbSet           Number of bytes to set.
+     */
+    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxSet, (void *pvUser, PVDIOCTX pIoCtx,
+                                               int ch, size_t cbSet));
+
+    /**
+     * Creates a segment array from the I/O context data buffer.
+     *
+     * @returns Number of bytes the array describes.
+     * @param  pvUser          The opaque user data passed on container creation.
+     * @param  pIoCtx          I/O context to copy the data from.
+     * @param  paSeg           The uninitialized segment array.
+     *                         If NULL pcSeg will contain the number of segments needed
+     *                         to describe the requested amount of data.
+     * @param  pcSeg           The number of segments the given array has.
+     *                         This will hold the actual number of entries needed upon return.
+     * @param  cbData          Number of bytes the new array should describe.
+     */
+    DECLR3CALLBACKMEMBER(size_t, pfnIoCtxSegArrayCreate, (void *pvUser, PVDIOCTX pIoCtx,
+                                                          PRTSGSEG paSeg, unsigned *pcSeg,
+                                                          size_t cbData));
+    /**
+     * Marks the given number of bytes as completed and continues the I/O context.
+     *
+     * @returns nothing.
+     * @param   pvUser         The opaque user data passed on container creation.
+     * @param   pIoCtx         The I/O context.
+     * @param   rcReq          Status code the request completed with.
+     * @param   cbCompleted    Number of bytes completed.
+     */
+    DECLR3CALLBACKMEMBER(void, pfnIoCtxCompleted, (void *pvUser, PVDIOCTX pIoCtx,
+                                                   int rcReq, size_t cbCompleted));
+} VDINTERFACEIOINT, *PVDINTERFACEIOINT;
+
+/**
+ * Get internal I/O interface from opaque callback table.
+ *
+ * @return Pointer to the callback table.
+ * @param  pInterface Pointer to the interface descriptor.
+ */
+DECLINLINE(PVDINTERFACEIOINT) VDGetInterfaceIOInt(PVDINTERFACE pInterface)
+{
+    PVDINTERFACEIOINT pInterfaceIOInt;
+
+    /* Check that the interface descriptor is an internal I/O interface. */
+    AssertMsgReturn(   (pInterface->enmInterface == VDINTERFACETYPE_IOINT)
+                    && (pInterface->cbSize == sizeof(VDINTERFACE)),
+                    ("Not an internal I/O interface"), NULL);
+
+    pInterfaceIOInt = (PVDINTERFACEIOINT)pInterface->pCallbacks;
+
+    /* Do basic checks. */
+    AssertMsgReturn(   (pInterfaceIOInt->cbSize == sizeof(VDINTERFACEIOINT))
+                    && (pInterfaceIOInt->enmInterface == VDINTERFACETYPE_IOINT),
+                    ("A non internal I/O callback table attached to an internal I/O interface descriptor\n"), NULL);
+
+    return pInterfaceIOInt;
+}
+
+/**
+ * Request completion callback for the async read/write API.
+ */
+typedef void (FNVDASYNCTRANSFERCOMPLETE) (void *pvUser1, void *pvUser2, int rcReq);
+/** Pointer to a transfer compelte callback. */
+typedef FNVDASYNCTRANSFERCOMPLETE *PFNVDASYNCTRANSFERCOMPLETE;
+
+/**
+ * Disk geometry.
+ */
+typedef struct VDGEOMETRY
+{
+    /** Number of cylinders. */
+    uint32_t    cCylinders;
+    /** Number of heads. */
+    uint32_t    cHeads;
+    /** Number of sectors. */
+    uint32_t    cSectors;
+} VDGEOMETRY;
+
+/** Pointer to disk geometry. */
+typedef VDGEOMETRY *PVDGEOMETRY;
+/** Pointer to constant disk geometry. */
+typedef const VDGEOMETRY *PCVDGEOMETRY;
+
+/**
+ * VBox HDD Container main structure.
+ */
+/* Forward declaration, VBOXHDD structure is visible only inside VBox HDD module. */
+struct VBOXHDD;
+typedef struct VBOXHDD VBOXHDD;
+typedef VBOXHDD *PVBOXHDD;
+
+/**
+ * Initializes HDD backends.
+ *
+ * @returns VBox status code.
+ */
+VBOXDDU_DECL(int) VDInit(void);
+
+/**
+ * Destroys loaded HDD backends.
+ *
+ * @returns VBox status code.
+ */
+VBOXDDU_DECL(int) VDShutdown(void);
+
+/**
+ * Lists all HDD backends and their capabilities in a caller-provided buffer.
+ *
+ * @return  VBox status code.
+ *          VERR_BUFFER_OVERFLOW if not enough space is passed.
+ * @param   cEntriesAlloc   Number of list entries available.
+ * @param   pEntries        Pointer to array for the entries.
+ * @param   pcEntriesUsed   Number of entries returned.
+ */
+VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
+                                    unsigned *pcEntriesUsed);
+
+/**
+ * Lists the capabilities of a backend identified by its name.
+ *
+ * @return  VBox status code.
+ * @param   pszBackend      The backend name (case insensitive).
+ * @param   pEntries        Pointer to an entry.
+ */
+VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry);
+
+/**
+ * Allocates and initializes an empty HDD container.
+ * No image files are opened.
+ *
+ * @return  VBox status code.
+ * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+ * @param   enmType         Type of the image container.
+ * @param   ppDisk          Where to store the reference to HDD container.
+ */
+VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk);
+
+/**
+ * Destroys HDD container.
+ * If container has opened image files they will be closed.
+ *
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk);
+
+/**
+ * Try to get the backend name which can use this image.
+ *
+ * @return  VBox status code.
+ *          VINF_SUCCESS if a plugin was found.
+ *                       ppszFormat contains the string which can be used as backend name.
+ *          VERR_NOT_SUPPORTED if no backend was found.
+ * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pszFilename     Name of the image file for which the backend is queried.
+ * @param   ppszFormat      Receives pointer of the UTF-8 string which contains the format name.
+ *                          The returned pointer must be freed using RTStrFree().
+ * @param   penmType        Where to store the type of the image.
+ */
+VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                                  const char *pszFilename, char **ppszFormat, VDTYPE *penmType);
+
+/**
+ * Opens an image file.
+ *
+ * The first opened image file in HDD container must have a base image type,
+ * others (next opened images) must be differencing or undo images.
+ * Linkage is checked for differencing image to be consistent with the previously opened image.
+ * When another 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 is opened in read-only mode if a read/write open is not possible.
+ * Use VDIsReadOnly to check open mode.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use (case insensitive).
+ * @param   pszFilename     Name of the image file to open.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ */
+VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
+                             const char *pszFilename, unsigned uOpenFlags,
+                             PVDINTERFACE pVDIfsImage);
+
+/**
+ * Opens a cache image.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to the HDD container which should use the cache image.
+ * @param   pszBackend      Name of the cache file backend to use (case insensitive).
+ * @param   pszFilename     Name of the cache image to open.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
+ */
+VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
+                                  const char *pszFilename, unsigned uOpenFlags,
+                                  PVDINTERFACE pVDIfsCache);
+
+/**
+ * Creates and opens a new base image file.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use (case insensitive).
+ * @param   pszFilename     Name of the image file to create.
+ * @param   cbSize          Image size in bytes.
+ * @param   uImageFlags     Flags specifying special image features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pPCHSGeometry   Pointer to physical disk geometry <= (16383,16,63). Not NULL.
+ * @param   pLCHSGeometry   Pointer to logical disk geometry <= (x,255,63). Not NULL.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
+                                   const char *pszFilename, uint64_t cbSize,
+                                   unsigned uImageFlags, const char *pszComment,
+                                   PCVDGEOMETRY pPCHSGeometry,
+                                   PCVDGEOMETRY pLCHSGeometry,
+                                   PCRTUUID pUuid, unsigned uOpenFlags,
+                                   PVDINTERFACE pVDIfsImage,
+                                   PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Creates and opens a new differencing image file in HDD container.
+ * See comments for VDOpen function about differencing images.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use (case insensitive).
+ * @param   pszFilename     Name of the differencing image file to create.
+ * @param   uImageFlags     Flags specifying special image features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   pParentUuid     New parent UUID of the image. If NULL, the UUID is queried automatically.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
+                                   const char *pszFilename, unsigned uImageFlags,
+                                   const char *pszComment, PCRTUUID pUuid,
+                                   PCRTUUID pParentUuid, unsigned uOpenFlags,
+                                   PVDINTERFACE pVDIfsImage,
+                                   PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Creates and opens new cache image file in HDD container.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Name of the cache file backend to use (case insensitive).
+ * @param   pszFilename     Name of the differencing cache file to create.
+ * @param   cbSize          Maximum size of the cache.
+ * @param   uImageFlags     Flags specifying special cache features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
+                                    const char *pszFilename, uint64_t cbSize,
+                                    unsigned uImageFlags, const char *pszComment,
+                                    PCRTUUID pUuid, unsigned uOpenFlags,
+                                    PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Merges two images (not necessarily with direct parent/child relationship).
+ * As a side effect the source image and potentially the other images which
+ * are also merged to the destination are deleted from both the disk and the
+ * images in the HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImageFrom      Image number to merge from, counts from 0. 0 is always base image of container.
+ * @param   nImageTo        Image number to merge to, counts from 0. 0 is always base image of container.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
+                              unsigned nImageTo, PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Copies an image from one HDD container to another.
+ * The copy is opened in the target HDD container.
+ * It is possible to convert between different image formats, because the
+ * backend for the destination may be different from the source.
+ * If both the source and destination reference the same HDD container,
+ * then the image is moved (by copying/deleting or renaming) to the new location.
+ * The source container is unchanged if the move operation fails, otherwise
+ * the image at the new location is opened in the same way as the old one was.
+ *
+ * @note The read/write accesses across disks are not synchronized, just the
+ * accesses to each disk. Once there is a use case which requires a defined
+ * read/write behavior in this situation this needs to be extended.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDiskFrom       Pointer to source HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pDiskTo         Pointer to destination HDD container.
+ * @param   pszBackend      Name of the image file backend to use (may be NULL to use the same as the source, case insensitive).
+ * @param   pszFilename     New name of the image (may be NULL to specify that the
+ *                          copy destination is the destination container, or
+ *                          if pDiskFrom == pDiskTo, i.e. when moving).
+ * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
+ * @param   cbSize          New image size (0 means leave unchanged).
+ * @param   uImageFlags     Flags specifying special destination image features.
+ * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
+ *                          This parameter is used if and only if a true copy is created.
+ *                          In all rename/move cases or copy to existing image cases the modification UUIDs are copied over.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ *                          Only used if the destination image is created.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
+ *                          destination image.
+ * @param   pDstVDIfsOperation Pointer to the per-operation VD interface list,
+ *                          for the destination operation.
+ */
+VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
+                             const char *pszBackend, const char *pszFilename,
+                             bool fMoveByRename, uint64_t cbSize,
+                             unsigned uImageFlags, PCRTUUID pDstUuid,
+                             unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
+                             PVDINTERFACE pDstVDIfsImage,
+                             PVDINTERFACE pDstVDIfsOperation);
+
+/**
+ * Optimizes the storage consumption of an image. Typically the unused blocks
+ * have to be wiped with zeroes to achieve a substantial reduced storage use.
+ * Another optimization done is reordering the image blocks, which can provide
+ * a significant performance boost, as reads and writes tend to use less random
+ * file offsets.
+ *
+ * @note Compaction is treated as a single operation with regard to thread
+ * synchronization, which means that it potentially blocks other activities for
+ * a long time. The complexity of compaction would grow even more if concurrent
+ * accesses have to be handled.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
+ * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
+ *                             this isn't supported yet.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
+                                PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Resizes the given disk image to the given size.
+ *
+ * @return  VBox status
+ * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
+ * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
+ *
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   cbSize          New size of the image.
+ * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
+ * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
+                               PCVDGEOMETRY pPCHSGeometry,
+                               PCVDGEOMETRY pLCHSGeometry,
+                               PVDINTERFACE pVDIfsOperation);
+
+/**
+ * Closes the last opened image file in HDD container.
+ * If previous image file was opened in read-only mode (the normal case) and
+ * the last opened image is in read-write mode then the previous image will be
+ * reopened in read/write mode.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   fDelete         If true, delete the image from the host disk.
+ */
+VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete);
+
+/**
+ * Closes the currently opened cache image file in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no cache is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   fDelete         If true, delete the image from the host disk.
+ */
+VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete);
+
+/**
+ * Closes all opened image files in HDD container.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk);
+
+/**
+ * Read data from virtual HDD.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   uOffset         Offset of first reading byte from start of disk.
+ *                          Must be aligned to a sector boundary.
+ * @param   pvBuffer        Pointer to buffer for reading data.
+ * @param   cbBuffer        Number of bytes to read.
+ *                          Must be aligned to a sector boundary.
+ */
+VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuffer, size_t cbBuffer);
+
+/**
+ * Write data to virtual HDD.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   uOffset         Offset of first writing byte from start of disk.
+ *                          Must be aligned to a sector boundary.
+ * @param   pvBuffer        Pointer to buffer for writing data.
+ * @param   cbBuffer        Number of bytes to write.
+ *                          Must be aligned to a sector boundary.
+ */
+VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuffer, size_t cbBuffer);
+
+/**
+ * Make sure the on disk representation of a virtual HDD is up to date.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk);
+
+/**
+ * Get number of opened images in HDD container.
+ *
+ * @return  Number of opened images for HDD container. 0 if no images have been opened.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk);
+
+/**
+ * Get read/write mode of HDD container.
+ *
+ * @return  Virtual disk ReadOnly status.
+ * @return  true if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk);
+
+/**
+ * Get total capacity of an image in HDD container.
+ *
+ * @return  Virtual disk size in bytes.
+ * @return  0 if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ */
+VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage);
+
+/**
+ * Get total file size of an image in HDD container.
+ *
+ * @return  Virtual disk size in bytes.
+ * @return  0 if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ */
+VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage);
+
+/**
+ * Get virtual disk PCHS geometry of an image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pPCHSGeometry   Where to store PCHS geometry. Not NULL.
+ */
+VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                        PVDGEOMETRY pPCHSGeometry);
+
+/**
+ * Store virtual disk PCHS geometry of an image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pPCHSGeometry   Where to load PCHS geometry from. Not NULL.
+ */
+VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                        PCVDGEOMETRY pPCHSGeometry);
+
+/**
+ * Get virtual disk LCHS geometry of an image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pLCHSGeometry   Where to store LCHS geometry. Not NULL.
+ */
+VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                        PVDGEOMETRY pLCHSGeometry);
+
+/**
+ * Store virtual disk LCHS geometry of an image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pLCHSGeometry   Where to load LCHS geometry from. Not NULL.
+ */
+VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                        PCVDGEOMETRY pLCHSGeometry);
+
+/**
+ * Get version of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puVersion       Where to store the image version.
+ */
+VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
+                                   unsigned *puVersion);
+
+/**
+ * List the capabilities of image backend in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pbackendInfo    Where to store the backend information.
+ */
+VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
+                                          PVDBACKENDINFO pBackendInfo);
+
+/**
+ * Get flags of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puImageFlags    Where to store the image flags.
+ */
+VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage, unsigned *puImageFlags);
+
+/**
+ * Get open flags of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puOpenFlags     Where to store the image open flags.
+ */
+VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
+                                     unsigned *puOpenFlags);
+
+/**
+ * Set open flags of image in HDD container.
+ * This operation may cause file locking changes and/or files being reopened.
+ * Note that in case of unrecoverable error all images in HDD container will be closed.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ */
+VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
+                                     unsigned uOpenFlags);
+
+/**
+ * Get base filename of image in HDD container. Some image formats use
+ * other filenames as well, so don't use this for anything but informational
+ * purposes.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszFilename     Where to store the image file name.
+ * @param   cbFilename      Size of buffer pszFilename points to.
+ */
+VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
+                                    char *pszFilename, unsigned cbFilename);
+
+/**
+ * Get the comment line of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszComment      Where to store the comment string of image. NULL is ok.
+ * @param   cbComment       The size of pszComment buffer. 0 is ok.
+ */
+VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
+                                   char *pszComment, unsigned cbComment);
+
+/**
+ * Changes the comment line of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
+ */
+VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
+                                   const char *pszComment);
+
+/**
+ * Get UUID of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           Where to store the image UUID.
+ */
+VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid);
+
+/**
+ * Set the image's UUID. Should not be used by normal applications.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid);
+
+/**
+ * Get last modification UUID of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           Where to store the image modification UUID.
+ */
+VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage,
+                                            PRTUUID pUuid);
+
+/**
+ * Set the image's last modification UUID. Should not be used by normal applications.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New modification UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage,
+                                            PCRTUUID pUuid);
+
+/**
+ * Get parent UUID of image in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of the container.
+ * @param   pUuid           Where to store the parent image UUID.
+ */
+VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
+                                      PRTUUID pUuid);
+
+/**
+ * Set the image's parent UUID. Should not be used by normal applications.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New parent UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
+                                      PCRTUUID pUuid);
+
+
+/**
+ * Debug helper - dumps all opened images in HDD container into the log file.
+ *
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk);
+
+
+/**
+ * Query if asynchronous operations are supported for this disk.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pfAIOSupported  Where to store if async IO is supported.
+ */
+VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported);
+
+
+/**
+ * Start a asynchronous read request.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   uOffset         The offset of the virtual disk to read from.
+ * @param   cbRead          How many bytes to read.
+ * @param   paSeg           Pointer to an array of segments.
+ * @param   cSeg            Number of segments in the array.
+ * @param   pfnComplete     Completion callback.
+ * @param   pvUser          User data which is passed on completion
+ */
+VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
+                                  PCRTSGSEG paSeg, unsigned cSeg,
+                                  PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                  void *pvUser1, void *pvUser2);
+
+
+/**
+ * Start a asynchronous write request.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   uOffset         The offset of the virtual disk to write to.
+ * @param   cbWrtie         How many bytes to write.
+ * @param   paSeg           Pointer to an array of segments.
+ * @param   cSeg            Number of segments in the array.
+ * @param   pfnComplete     Completion callback.
+ * @param   pvUser          User data which is passed on completion.
+ */
+VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
+                                   PCRTSGSEG paSeg, unsigned cSeg,
+                                   PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                   void *pvUser1, void *pvUser2);
+
+
+/**
+ * Start a asynchronous flush request.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   pfnComplete     Completion callback.
+ * @param   pvUser          User data which is passed on completion.
+ */
+VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk,
+                                   PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                   void *pvUser1, void *pvUser2);
+RT_C_DECLS_END
+
+/** @} */
+
+#endif
Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 33566)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 33567)
@@ -26,8 +26,4 @@
 include $(PATH_SUB_CURRENT)/Graphics/BIOS/Makefile.kmk
 include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
-include $(PATH_SUB_CURRENT)/Storage/testcase/Makefile.kmk
-ifndef VBOX_OSE
- include $(PATH_SUB_CURRENT)/Storage/VBoxHDDFormats/Makefile.kmk
-endif
 ifdef VBOX_WITH_EFI
  include $(PATH_SUB_CURRENT)/EFI/Thunk/Makefile.kmk
@@ -77,12 +73,4 @@
 endif
 VBoxDDU_SOURCES          = \
-	Storage/VBoxHDD.cpp \
-	Storage/VDIHDDCore.cpp \
-	Storage/VHDHDDCore.cpp \
-	Storage/VmdkHDDCore.cpp \
-	Storage/RawHDDCore.cpp \
-	Storage/ParallelsHDDCore.cpp \
-	Storage/DMGHDDCore.cpp \
-	Storage/VCICacheCore.cpp \
 	Storage/VSCSI/VSCSIDevice.cpp \
 	Storage/VSCSI/VSCSILun.cpp \
@@ -97,5 +85,6 @@
 endif
 VBoxDDU_LIBS             = \
-	$(LIB_RUNTIME)
+	$(LIB_RUNTIME) \
+	$(PATH_LIB)/StorageLib$(VBOX_SUFF_LIB)
 ifdef VBOX_WITH_USB
  VBoxDDU_LIBS           += \
@@ -1057,7 +1046,4 @@
 
 ifdef VBOX_WITH_ISCSI
- VBoxDDU_SOURCES      += \
-    Storage/ISCSIHDDCore.cpp
- VBoxDDU_DEFS         += VBOX_WITH_ISCSI
  Storage/DrvVD.cpp_INCS = \
 	Network/lwip/src/include \
Index: unk/src/VBox/Devices/Storage/DMGHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DMGHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,2445 +1,0 @@
-/* $Id$ */
-/** @file
- * VBoxDMG - Interpreter for Apple Disk Images (DMG).
- */
-
-/*
- * Copyright (C) 2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/log.h>
-#include <VBox/err.h>
-#include <iprt/assert.h>
-#include <iprt/asm.h>
-#include <iprt/mem.h>
-#include <iprt/ctype.h>
-#include <iprt/string.h>
-#include <iprt/base64.h>
-#include <iprt/zip.h>
-
-/*******************************************************************************
-*   Structures and Typedefs                                                    *
-*******************************************************************************/
-
-/** Sector size, multiply with all sector counts to get number of bytes. */
-#define DMG_SECTOR_SIZE 512
-
-/** Convert block number/size to byte offset/size. */
-#define DMG_BLOCK2BYTE(u)          ((uint64_t)(u) << 9)
-
-/** Convert byte offset/size to block number/size. */
-#define DMG_BYTE2BLOCK(u)          ((u) >> 9)
-
-/**
- * UDIF checksum structure.
- */
-typedef struct DMGUDIFCKSUM
-{
-    uint32_t        u32Kind;                    /**< The kind of checksum.  */
-    uint32_t        cBits;                      /**< The size of the checksum. */
-    union
-    {
-        uint8_t     au8[128];                   /**< 8-bit view. */
-        uint32_t    au32[32];                   /**< 32-bit view. */
-    }               uSum;                       /**< The checksum. */
-} DMGUDIFCKSUM;
-AssertCompileSize(DMGUDIFCKSUM, 8 + 128);
-typedef DMGUDIFCKSUM *PDMGUDIFCKSUM;
-typedef const DMGUDIFCKSUM *PCDMGUDIFCKSUM;
-
-/** @name Checksum Kind (DMGUDIFCKSUM::u32Kind)
- * @{ */
-/** No checksum. */
-#define DMGUDIFCKSUM_NONE          UINT32_C(0)
-/** CRC-32. */
-#define DMGUDIFCKSUM_CRC32         UINT32_C(2)
-/** @} */
-
-/**
- * UDIF ID.
- * This is kind of like a UUID only it isn't, but we'll use the UUID
- * representation of it for simplicity.
- */
-typedef RTUUID DMGUDIFID;
-AssertCompileSize(DMGUDIFID, 16);
-typedef DMGUDIFID *PDMGUDIFID;
-typedef const DMGUDIFID *PCDMGUDIFID;
-
-/**
- * UDIF footer used by Apple Disk Images (DMG).
- *
- * This is a footer placed 512 bytes from the end of the file. Typically a DMG
- * file starts with the data, which is followed by the block table and then ends
- * with this structure.
- *
- * All fields are stored in big endian format.
- */
-#pragma pack(1)
-typedef struct DMGUDIF
-{
-    uint32_t            u32Magic;               /**< 0x000 - Magic, 'koly' (DMGUDIF_MAGIC).                       (fUDIFSignature) */
-    uint32_t            u32Version;             /**< 0x004 - The UDIF version (DMGUDIF_VER_CURRENT).              (fUDIFVersion) */
-    uint32_t            cbFooter;               /**< 0x008 - The size of the this structure (512).                 (fUDIFHeaderSize) */
-    uint32_t            fFlags;                 /**< 0x00c - Flags.                                                (fUDIFFlags) */
-    uint64_t            offRunData;             /**< 0x010 - Where the running data fork starts (usually 0).       (fUDIFRunningDataForkOffset) */
-    uint64_t            offData;                /**< 0x018 - Where the data fork starts (usually 0).               (fUDIFDataForkOffset) */
-    uint64_t            cbData;                 /**< 0x020 - Size of the data fork (in bytes).                     (fUDIFDataForkLength) */
-    uint64_t            offRsrc;                /**< 0x028 - Where the resource fork starts (usually cbData or 0). (fUDIFRsrcForkOffset) */
-    uint64_t            cbRsrc;                 /**< 0x030 - The size of the resource fork.                        (fUDIFRsrcForkLength)*/
-    uint32_t            iSegment;               /**< 0x038 - The segment number of this file.                      (fUDIFSegmentNumber) */
-    uint32_t            cSegments;              /**< 0x03c - The number of segments.                               (fUDIFSegmentCount) */
-    DMGUDIFID           SegmentId;              /**< 0x040 - The segment ID.                                       (fUDIFSegmentID) */
-    DMGUDIFCKSUM        DataCkSum;              /**< 0x050 - The data checksum.                                    (fUDIFDataForkChecksum) */
-    uint64_t            offXml;                 /**< 0x0d8 - The XML offset (.plist kind of data).                 (fUDIFXMLOffset) */
-    uint64_t            cbXml;                  /**< 0x0e0 - The size of the XML.                                  (fUDIFXMLSize) */
-    uint8_t             abUnknown[120];         /**< 0x0e8 - Unknown stuff, hdiutil doesn't dump it... */
-    DMGUDIFCKSUM        MasterCkSum;            /**< 0x160 - The master checksum.                                  (fUDIFMasterChecksum) */
-    uint32_t            u32Type;                /**< 0x1e8 - The image type.                                       (fUDIFImageVariant) */
-    uint64_t            cSectors;               /**< 0x1ec - The sector count. Warning! Unaligned!                 (fUDISectorCount) */
-    uint32_t            au32Unknown[3];         /**< 0x1f4 - Unknown stuff, hdiutil doesn't dump it... */
-} DMGUDIF;
-#pragma pack()
-AssertCompileSize(DMGUDIF, 512);
-AssertCompileMemberOffset(DMGUDIF, cbRsrc,   0x030);
-AssertCompileMemberOffset(DMGUDIF, cbXml,    0x0e0);
-AssertCompileMemberOffset(DMGUDIF, cSectors, 0x1ec);
-
-typedef DMGUDIF *PDMGUDIF;
-typedef const DMGUDIF *PCDMGUDIF;
-
-/** The UDIF magic 'koly' (DMGUDIF::u32Magic). */
-#define DMGUDIF_MAGIC              UINT32_C(0x6b6f6c79)
-
-/** The current UDIF version (DMGUDIF::u32Version).
- * This is currently the only we recognizes and will create. */
-#define DMGUDIF_VER_CURRENT        4
-
-/** @name UDIF flags (DMGUDIF::fFlags).
- * @{ */
-/** Flatten image whatever that means.
- * (hdiutil -debug calls it kUDIFFlagsFlattened.) */
-#define DMGUDIF_FLAGS_FLATTENED    RT_BIT_32(0)
-/** Internet enabled image.
- * (hdiutil -debug calls it kUDIFFlagsInternetEnabled) */
-#define DMGUDIF_FLAGS_INET_ENABLED RT_BIT_32(2)
-/** Mask of known bits. */
-#define DMGUDIF_FLAGS_KNOWN_MASK   (RT_BIT_32(0) | RT_BIT_32(2))
-/** @} */
-
-/** @name UDIF Image Types (DMGUDIF::u32Type).
- * @{ */
-/** Device image type. (kUDIFDeviceImageType) */
-#define DMGUDIF_TYPE_DEVICE        1
-/** Device image type. (kUDIFPartitionImageType) */
-#define DMGUDIF_TYPE_PARTITION     2
-/** @}  */
-
-/**
- * BLKX data.
- *
- * This contains the start offset and size of raw data stored in the image.
- *
- * All fields are stored in big endian format.
- */
-#pragma pack(1)
-typedef struct DMGBLKX
-{
-    uint32_t            u32Magic;               /**< 0x000 - Magic, 'mish' (DMGBLKX_MAGIC). */
-    uint32_t            u32Version;             /**< 0x004 - The BLKX version (DMGBLKX_VER_CURRENT). */
-    uint64_t            cSectornumberFirst;     /**< 0x008 - The first sector number the block represents in the virtual device. */
-    uint64_t            cSectors;               /**< 0x010 - Number of sectors this block represents. */
-    uint64_t            offDataStart;           /**< 0x018 - Start offset for raw data. */
-    uint32_t            cSectorsDecompress;     /**< 0x020 - Size of the buffer in sectors needed to decompress. */
-    uint32_t            u32BlocksDescriptor;    /**< 0x024 - Blocks descriptor. */
-    uint8_t             abReserved[24];
-    DMGUDIFCKSUM        BlkxCkSum;              /**< 0x03c - Checksum for the BLKX table. */
-    uint32_t            cBlocksRunCount;        /**< 0x    - Number of entries in the blkx run table afterwards. */
-} DMGBLKX;
-#pragma pack()
-AssertCompileSize(DMGBLKX, 204);
-
-typedef DMGBLKX *PDMGBLKX;
-typedef const DMGBLKX *PCDMGBLKX;
-
-/** The BLKX magic 'mish' (DMGBLKX::u32Magic). */
-#define DMGBLKX_MAGIC              UINT32_C(0x6d697368)
-/** BLKX version (DMGBLKX::u32Version). */
-#define DMGBLKX_VERSION            UINT32_C(0x00000001)
-
-/** Blocks descriptor type: entire device. */
-#define DMGBLKX_DESC_ENTIRE_DEVICE UINT32_C(0xfffffffe)
-
-/**
- * BLKX table descriptor.
- *
- * All fields are stored in big endian format.
- */
-#pragma pack(1)
-typedef struct DMGBLKXDESC
-{
-    uint32_t            u32Type;                /**< 0x000 - Type of the descriptor. */
-    uint32_t            u32Reserved;            /**< 0x004 - Reserved, but contains +beg or +end in case thisi is a comment descriptor. */
-    uint64_t            u64SectorStart;         /**< 0x008 - First sector number in the block this entry describes. */
-    uint64_t            u64SectorCount;         /**< 0x010 - Number of sectors this entry describes. */
-    uint64_t            offData;                /**< 0x018 - Offset in the image where the data starts. */
-    uint64_t            cbData;                 /**< 0x020 - Number of bytes in the image. */
-} DMGBLKXDESC;
-#pragma pack()
-AssertCompileSize(DMGBLKXDESC, 40);
-
-typedef DMGBLKXDESC *PDMGBLKXDESC;
-typedef const DMGBLKXDESC *PCDMGBLKXDESC;
-
-/** Raw image data type. */
-#define DMGBLKXDESC_TYPE_RAW        1
-/** Ignore type. */
-#define DMGBLKXDESC_TYPE_IGNORE     2
-/** Compressed with zlib type. */
-#define DMGBLKXDESC_TYPE_ZLIB       UINT32_C(0x80000005)
-/** Comment type. */
-#define DMGBLKXDESC_TYPE_COMMENT    UINT32_C(0x7ffffffe)
-/** Terminator type. */
-#define DMGBLKXDESC_TYPE_TERMINATOR UINT32_C(0xffffffff)
-
-/**
- * UDIF Resource Entry.
- */
-typedef struct DMGUDIFRSRCENTRY
-{
-    /** The ID. */
-    int32_t             iId;
-    /** Attributes. */
-    uint32_t            fAttributes;
-    /** The name. */
-    char               *pszName;
-    /** The CoreFoundation name. Can be NULL. */
-    char               *pszCFName;
-    /** The size of the data. */
-    size_t              cbData;
-    /** The raw data. */
-    uint8_t            *pbData;
-} DMGUDIFRSRCENTRY;
-/** Pointer to an UDIF resource entry. */
-typedef DMGUDIFRSRCENTRY *PDMGUDIFRSRCENTRY;
-/** Pointer to a const UDIF resource entry. */
-typedef DMGUDIFRSRCENTRY const *PCDMGUDIFRSRCENTRY;
-
-/**
- * UDIF Resource Array.
- */
-typedef struct DMGUDIFRSRCARRAY
-{
-    /** The array name. */
-    char                szName[12];
-    /** The number of occupied entries. */
-    uint32_t            cEntries;
-    /** The array entries.
-     * A lazy bird ASSUME there are no more than 4 entries in any DMG. Increase the
-     * size if DMGs with more are found.
-     * r=aeichner: Saw one with 6 here (image of a whole DVD) */
-    DMGUDIFRSRCENTRY    aEntries[10];
-} DMGUDIFRSRCARRAY;
-/** Pointer to a UDIF resource array. */
-typedef DMGUDIFRSRCARRAY *PDMGUDIFRSRCARRAY;
-/** Pointer to a const UDIF resource array. */
-typedef DMGUDIFRSRCARRAY const *PCDMGUDIFRSRCARRAY;
-
-/**
- * DMG extent types.
- */
-typedef enum DMGEXTENTTYPE
-{
-    /** Null, never used. */
-    DMGEXTENTTYPE_NULL = 0,
-    /** Raw image data. */
-    DMGEXTENTTYPE_RAW,
-    /** Zero extent, reads return 0 and writes have no effect. */
-    DMGEXTENTTYPE_ZERO,
-    /** Compressed extent - compression method ZLIB. */
-    DMGEXTENTTYPE_COMP_ZLIB,
-    /** 32bit hack. */
-    DMGEXTENTTYPE_32BIT_HACK = 0x7fffffff
-} DMGEXTENTTYPE, *PDMGEXTENTTYPE;
-
-/**
- * DMG extent mapping a virtual image block to real file offsets.
- */
-typedef struct DMGEXTENT
-{
-    /** Extent type. */
-    DMGEXTENTTYPE        enmType;
-    /** First sector this extent describes. */
-    uint64_t             uSectorExtent;
-    /** Number of sectors this extent describes. */
-    uint64_t             cSectorsExtent;
-    /** Start offset in the real file. */
-    uint64_t             offFileStart;
-    /** Number of bytes for the extent data in the file. */
-    uint64_t             cbFile;
-} DMGEXTENT;
-/** Pointer to an DMG extent. */
-typedef DMGEXTENT *PDMGEXTENT;
-
-/**
- * VirtualBox Apple Disk Image (DMG) interpreter instance data.
- */
-typedef struct DMGIMAGE
-{
-    /** Image name.
-     * Kept around for logging and delete-on-close purposes. */
-    const char         *pszFilename;
-    /** Storage handle. */
-    PVDIOSTORAGE        pStorage;
-    /** I/O interface. */
-    PVDINTERFACE        pInterfaceIO;
-    /** Async I/O interface callbacks. */
-    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE        pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE        pVDIfsImage;
-
-    /** Error callback. */
-    PVDINTERFACE        pInterfaceError;
-    /** Opaque data for error callback. */
-    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
-
-    /** Flags the image was opened with. */
-    uint32_t            uOpenFlags;
-    /** Image flags. */
-    unsigned            uImageFlags;
-    /** Total size of the virtual image. */
-    uint64_t            cbSize;
-    /** Size of the image. */
-    uint64_t            cbFile;
-    /** Physical geometry of this image. */
-    VDGEOMETRY          PCHSGeometry;
-    /** Logical geometry of this image. */
-    VDGEOMETRY          LCHSGeometry;
-
-    /** The resources.
-     * A lazy bird ASSUME there are only two arrays in the resource-fork section in
-     * the XML, namely 'blkx' and 'plst'. These have been assigned fixed indexes. */
-    DMGUDIFRSRCARRAY    aRsrcs[2];
-    /** The UDIF footer. */
-    DMGUDIF             Ftr;
-
-    /** Number of valid extents in the array. */
-    unsigned            cExtents;
-    /** Number of entries the array can hold. */
-    unsigned            cExtentsMax;
-    /** Pointer to the extent array. */
-    PDMGEXTENT          paExtents;
-    /** Index of the last accessed extent. */
-    unsigned            idxExtentLast;
-
-    /** Extent which owns the data in the buffer. */
-    PDMGEXTENT          pExtentDecomp;
-    /** Buffer holding the decompressed data for a extent. */
-    void               *pvDecompExtent;
-    /** Size of the buffer. */
-    size_t              cbDecompExtent;
-} DMGIMAGE;
-/** Pointer to an instance of the DMG Image Interpreter. */
-typedef DMGIMAGE *PDMGIMAGE;
-
-/** @name Resources indexes (into DMG::aRsrcs).
- * @{ */
-#define DMG_RSRC_IDX_BLKX   0
-#define DMG_RSRC_IDX_PLST   1
-/** @} */
-
-/** State for the input callout of the inflate reader. */
-typedef struct DMGINFLATESTATE
-{
-    /* Image this operation relates to. */
-    PDMGIMAGE pImage;
-    /* Total size of the data to read. */
-    size_t    cbSize;
-    /* Offset in the file to read. */
-    uint64_t  uFileOffset;
-    /* Current read position. */
-    ssize_t   iOffset;
-} DMGINFLATESTATE;
-
-/*******************************************************************************
-*   Defined Constants And Macros                                               *
-*******************************************************************************/
-/** @def DMG_PRINTF
- * Wrapper for LogRel.
- */
-#define DMG_PRINTF(a)  LogRel(a)
-
-/** @def DMG_VALIDATE
- * For validating a struct thing and log/print what's wrong.
- */
-# define DMG_VALIDATE(expr, logstuff) \
-    do { \
-        if (!(expr)) \
-        { \
-            LogRel(("DMG: validation failed: %s\nDMG: ", #expr)); \
-            LogRel(logstuff); \
-            fRc = false; \
-        } \
-    } while (0)
-
-/** VBoxDMG: Unable to parse the XML. */
-#define VERR_VD_DMG_XML_PARSE_ERROR         (-3280)
-
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aDmgFileExtensions[] =
-{
-    {"dmg", VDTYPE_DVD},
-    {NULL, VDTYPE_INVALID}
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif);
-static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif);
-
-static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId);
-static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId);
-
-static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum);
-static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum);
-static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix);
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) dmgError(PDMGIMAGE pThis, int rc, RT_SRC_POS_DECL,
-                         const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pThis->pInterfaceError)
-        pThis->pInterfaceErrorCallbacks->pfnError(pThis->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                  pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) dmgMessage(PDMGIMAGE pThis, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pThis->pInterfaceError)
-        rc = pThis->pInterfaceErrorCallbacks->pfnMessage(pThis->pInterfaceError->pvUser,
-                                                         pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) dmgFileOpen(PDMGIMAGE pThis, const char *pszFilename,
-                            uint32_t fOpen)
-{
-    return pThis->pInterfaceIOCallbacks->pfnOpen(pThis->pInterfaceIO->pvUser,
-                                                 pszFilename, fOpen,
-                                                 &pThis->pStorage);
-}
-
-DECLINLINE(int) dmgFileClose(PDMGIMAGE pThis)
-{
-    return pThis->pInterfaceIOCallbacks->pfnClose(pThis->pInterfaceIO->pvUser,
-                                                  pThis->pStorage);
-}
-
-DECLINLINE(int) dmgFileDelete(PDMGIMAGE pThis, const char *pszFilename)
-{
-    return pThis->pInterfaceIOCallbacks->pfnDelete(pThis->pInterfaceIO->pvUser,
-                                                   pszFilename);
-}
-
-DECLINLINE(int) dmgFileMove(PDMGIMAGE pThis, const char *pszSrc,
-                            const char *pszDst, unsigned fMove)
-{
-    return pThis->pInterfaceIOCallbacks->pfnMove(pThis->pInterfaceIO->pvUser,
-                                                 pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) dmgFileGetFreeSpace(PDMGIMAGE pThis, const char *pszFilename,
-                                    int64_t *pcbFree)
-{
-    return pThis->pInterfaceIOCallbacks->pfnGetFreeSpace(pThis->pInterfaceIO->pvUser,
-                                                         pszFilename, pcbFree);
-}
-
-DECLINLINE(int) dmgFileGetSize(PDMGIMAGE pThis, uint64_t *pcbSize)
-{
-    return pThis->pInterfaceIOCallbacks->pfnGetSize(pThis->pInterfaceIO->pvUser,
-                                                    pThis->pStorage, pcbSize);
-}
-
-DECLINLINE(int) dmgFileSetSize(PDMGIMAGE pThis, uint64_t cbSize)
-{
-    return pThis->pInterfaceIOCallbacks->pfnSetSize(pThis->pInterfaceIO->pvUser,
-                                                    pThis->pStorage, cbSize);
-}
-
-DECLINLINE(int) dmgFileWriteSync(PDMGIMAGE pThis, uint64_t uOffset,
-                                 const void *pvBuffer, size_t cbBuffer,
-                                 size_t *pcbWritten)
-{
-    return pThis->pInterfaceIOCallbacks->pfnWriteSync(pThis->pInterfaceIO->pvUser,
-                                                      pThis->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, pcbWritten);
-}
-
-DECLINLINE(int) dmgFileReadSync(PDMGIMAGE pThis, uint64_t uOffset,
-                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
-{
-    return pThis->pInterfaceIOCallbacks->pfnReadSync(pThis->pInterfaceIO->pvUser,
-                                                     pThis->pStorage, uOffset,
-                                                     pvBuffer, cbBuffer, pcbRead);
-}
-
-DECLINLINE(int) dmgFileFlushSync(PDMGIMAGE pThis)
-{
-    return pThis->pInterfaceIOCallbacks->pfnFlushSync(pThis->pInterfaceIO->pvUser,
-                                                      pThis->pStorage);
-}
-
-static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
-{
-    DMGINFLATESTATE *pInflateState = (DMGINFLATESTATE *)pvUser;
-
-    Assert(cbBuf);
-    if (pInflateState->iOffset < 0)
-    {
-        *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
-        if (pcbBuf)
-            *pcbBuf = 1;
-        pInflateState->iOffset = 0;
-        return VINF_SUCCESS;
-    }
-    cbBuf = RT_MIN(cbBuf, pInflateState->cbSize);
-    int rc = dmgFileReadSync(pInflateState->pImage, pInflateState->uFileOffset,
-                             pvBuf, cbBuf, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    pInflateState->uFileOffset += cbBuf;
-    pInflateState->iOffset += cbBuf;
-    pInflateState->cbSize -= cbBuf;
-    Assert(pcbBuf);
-    *pcbBuf = cbBuf;
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal: read from a file and inflate the compressed data,
- * distinguishing between async and normal operation
- */
-DECLINLINE(int) dmgFileInflateSync(PDMGIMAGE pImage, uint64_t uOffset, size_t cbToRead,
-                                   void *pvBuf, size_t cbBuf)
-{
-    int rc;
-    PRTZIPDECOMP pZip = NULL;
-    DMGINFLATESTATE InflateState;
-    size_t cbActuallyRead;
-
-    InflateState.pImage      = pImage;
-    InflateState.cbSize      = cbToRead;
-    InflateState.uFileOffset = uOffset;
-    InflateState.iOffset     = -1;
-
-    rc = RTZipDecompCreate(&pZip, &InflateState, dmgFileInflateHelper);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = RTZipDecompress(pZip, pvBuf, cbBuf, &cbActuallyRead);
-    RTZipDecompDestroy(pZip);
-    if (RT_FAILURE(rc))
-        return rc;
-    if (cbActuallyRead != cbBuf)
-        rc = VERR_VD_VMDK_INVALID_FORMAT;
-    return rc;
-}
-
-/**
- * Swaps endian.
- * @param   pUdif       The structure.
- */
-static void dmgSwapEndianUdif(PDMGUDIF pUdif)
-{
-#ifndef RT_BIG_ENDIAN
-    pUdif->u32Magic   = RT_BSWAP_U32(pUdif->u32Magic);
-    pUdif->u32Version = RT_BSWAP_U32(pUdif->u32Version);
-    pUdif->cbFooter   = RT_BSWAP_U32(pUdif->cbFooter);
-    pUdif->fFlags     = RT_BSWAP_U32(pUdif->fFlags);
-    pUdif->offRunData = RT_BSWAP_U64(pUdif->offRunData);
-    pUdif->offData    = RT_BSWAP_U64(pUdif->offData);
-    pUdif->cbData     = RT_BSWAP_U64(pUdif->cbData);
-    pUdif->offRsrc    = RT_BSWAP_U64(pUdif->offRsrc);
-    pUdif->cbRsrc     = RT_BSWAP_U64(pUdif->cbRsrc);
-    pUdif->iSegment   = RT_BSWAP_U32(pUdif->iSegment);
-    pUdif->cSegments  = RT_BSWAP_U32(pUdif->cSegments);
-    pUdif->offXml     = RT_BSWAP_U64(pUdif->offXml);
-    pUdif->cbXml      = RT_BSWAP_U64(pUdif->cbXml);
-    pUdif->u32Type    = RT_BSWAP_U32(pUdif->u32Type);
-    pUdif->cSectors   = RT_BSWAP_U64(pUdif->cSectors);
-#endif
-}
-
-
-/**
- * Swaps endian from host cpu to file.
- * @param   pUdif       The structure.
- */
-static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif)
-{
-    dmgSwapEndianUdif(pUdif);
-    dmgUdifIdHost2FileEndian(&pUdif->SegmentId);
-    dmgUdifCkSumHost2FileEndian(&pUdif->DataCkSum);
-    dmgUdifCkSumHost2FileEndian(&pUdif->MasterCkSum);
-}
-
-
-/**
- * Swaps endian from file to host cpu.
- * @param   pUdif       The structure.
- */
-static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif)
-{
-    dmgSwapEndianUdif(pUdif);
-    dmgUdifIdFile2HostEndian(&pUdif->SegmentId);
-    dmgUdifCkSumFile2HostEndian(&pUdif->DataCkSum);
-    dmgUdifCkSumFile2HostEndian(&pUdif->MasterCkSum);
-}
-
-/**
- * Swaps endian from file to host cpu.
- * @param   pBlkx       The blkx structure.
- */
-static void dmgBlkxFile2HostEndian(PDMGBLKX pBlkx)
-{
-    pBlkx->u32Magic            = RT_BE2H_U32(pBlkx->u32Magic);
-    pBlkx->u32Version          = RT_BE2H_U32(pBlkx->u32Version);
-    pBlkx->cSectornumberFirst  = RT_BE2H_U64(pBlkx->cSectornumberFirst);
-    pBlkx->cSectors            = RT_BE2H_U64(pBlkx->cSectors);
-    pBlkx->offDataStart        = RT_BE2H_U64(pBlkx->offDataStart);
-    pBlkx->cSectorsDecompress  = RT_BE2H_U32(pBlkx->cSectorsDecompress);
-    pBlkx->u32BlocksDescriptor = RT_BE2H_U32(pBlkx->u32BlocksDescriptor);
-    pBlkx->cBlocksRunCount     = RT_BE2H_U32(pBlkx->cBlocksRunCount);
-    dmgUdifCkSumFile2HostEndian(&pBlkx->BlkxCkSum);
-}
-
-/**
- * Swaps endian from file to host cpu.
- * @param   pBlkxDesc   The blkx descriptor structure.
- */
-static void dmgBlkxDescFile2HostEndian(PDMGBLKXDESC pBlkxDesc)
-{
-    pBlkxDesc->u32Type        = RT_BE2H_U32(pBlkxDesc->u32Type);
-    pBlkxDesc->u32Reserved    = RT_BE2H_U32(pBlkxDesc->u32Reserved);
-    pBlkxDesc->u64SectorStart = RT_BE2H_U64(pBlkxDesc->u64SectorStart);
-    pBlkxDesc->u64SectorCount = RT_BE2H_U64(pBlkxDesc->u64SectorCount);
-    pBlkxDesc->offData        = RT_BE2H_U64(pBlkxDesc->offData);
-    pBlkxDesc->cbData         = RT_BE2H_U64(pBlkxDesc->cbData);
-}
-
-/**
- * Validates an UDIF footer structure.
- *
- * @returns true if valid, false and LogRel()s on failure.
- * @param   pFtr        The UDIF footer to validate.
- * @param   offFtr      The offset of the structure.
- */
-static bool dmgUdifFtrIsValid(PCDMGUDIF pFtr, uint64_t offFtr)
-{
-    bool fRc = true;
-
-    DMG_VALIDATE(!(pFtr->fFlags & ~DMGUDIF_FLAGS_KNOWN_MASK), ("fFlags=%#RX32 fKnown=%RX32\n", pFtr->fFlags, DMGUDIF_FLAGS_KNOWN_MASK));
-    DMG_VALIDATE(pFtr->offRunData < offFtr, ("offRunData=%#RX64\n", pFtr->offRunData));
-    DMG_VALIDATE(pFtr->cbData    <= offFtr && pFtr->offData + pFtr->cbData <= offFtr, ("cbData=%#RX64 offData=%#RX64 offFtr=%#RX64\n", pFtr->cbData, pFtr->offData, offFtr));
-    DMG_VALIDATE(pFtr->offData    < offFtr, ("offData=%#RX64\n", pFtr->offData));
-    DMG_VALIDATE(pFtr->cbRsrc    <= offFtr && pFtr->offRsrc + pFtr->cbRsrc <= offFtr, ("cbRsrc=%#RX64 offRsrc=%#RX64 offFtr=%#RX64\n", pFtr->cbRsrc, pFtr->offRsrc, offFtr));
-    DMG_VALIDATE(pFtr->offRsrc    < offFtr, ("offRsrc=%#RX64\n", pFtr->offRsrc));
-    DMG_VALIDATE(pFtr->cSegments <= 1,      ("cSegments=%RU32\n", pFtr->cSegments));
-    DMG_VALIDATE(pFtr->iSegment  == 0 || pFtr->iSegment == 1, ("iSegment=%RU32 cSegments=%RU32\n", pFtr->iSegment, pFtr->cSegments));
-    DMG_VALIDATE(pFtr->cbXml    <= offFtr && pFtr->offXml + pFtr->cbXml <= offFtr, ("cbXml=%#RX64 offXml=%#RX64 offFtr=%#RX64\n", pFtr->cbXml, pFtr->offXml, offFtr));
-    DMG_VALIDATE(pFtr->offXml    < offFtr,  ("offXml=%#RX64\n", pFtr->offXml));
-    DMG_VALIDATE(pFtr->cbXml     > 128,     ("cbXml=%#RX64\n", pFtr->cbXml));
-    DMG_VALIDATE(pFtr->cbXml     < 10 * _1M,     ("cbXml=%#RX64\n", pFtr->cbXml));
-    DMG_VALIDATE(pFtr->u32Type == DMGUDIF_TYPE_DEVICE || pFtr->u32Type == DMGUDIF_TYPE_PARTITION,  ("u32Type=%RU32\n", pFtr->u32Type));
-    DMG_VALIDATE(pFtr->cSectors != 0,       ("cSectors=%#RX64\n", pFtr->cSectors));
-    fRc &= dmgUdifCkSumIsValid(&pFtr->DataCkSum, "DataCkSum");
-    fRc &= dmgUdifCkSumIsValid(&pFtr->MasterCkSum, "MasterCkSum");
-
-    return fRc;
-}
-
-
-static bool dmgBlkxIsValid(PCDMGBLKX pBlkx)
-{
-    bool fRc = true;
-
-    fRc &= dmgUdifCkSumIsValid(&pBlkx->BlkxCkSum, "BlkxCkSum");
-    DMG_VALIDATE(pBlkx->u32Magic == DMGBLKX_MAGIC, ("u32Magic=%#RX32 u32MagicExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_MAGIC));
-    DMG_VALIDATE(pBlkx->u32Version == DMGBLKX_VERSION, ("u32Version=%#RX32 u32VersionExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_VERSION));
-
-    return fRc;
-}
-
-/**
- * Swaps endian from host cpu to file.
- * @param   pId         The structure.
- */
-static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId)
-{
-    NOREF(pId);
-}
-
-
-/**
- * Swaps endian from file to host cpu.
- * @param   pId         The structure.
- */
-static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId)
-{
-    dmgUdifIdHost2FileEndian(pId);
-}
-
-
-/**
- * Swaps endian.
- * @param   pCkSum      The structure.
- */
-static void dmgSwapEndianUdifCkSum(PDMGUDIFCKSUM pCkSum, uint32_t u32Kind, uint32_t cBits)
-{
-#ifdef RT_BIG_ENDIAN
-    NOREF(pCkSum);
-    NOREF(u32Kind);
-    NOREF(cBits);
-#else
-    switch (u32Kind)
-    {
-        case DMGUDIFCKSUM_NONE:
-            /* nothing to do here */
-            break;
-
-        case DMGUDIFCKSUM_CRC32:
-            Assert(cBits == 32);
-            pCkSum->u32Kind      = RT_BSWAP_U32(pCkSum->u32Kind);
-            pCkSum->cBits        = RT_BSWAP_U32(pCkSum->cBits);
-            pCkSum->uSum.au32[0] = RT_BSWAP_U32(pCkSum->uSum.au32[0]);
-            break;
-
-        default:
-            AssertMsgFailed(("%x\n", u32Kind));
-            break;
-    }
-    NOREF(cBits);
-#endif
-}
-
-
-/**
- * Swaps endian from host cpu to file.
- * @param   pCkSum      The structure.
- */
-static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum)
-{
-    dmgSwapEndianUdifCkSum(pCkSum, pCkSum->u32Kind, pCkSum->cBits);
-}
-
-
-/**
- * Swaps endian from file to host cpu.
- * @param   pCkSum      The structure.
- */
-static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum)
-{
-    dmgSwapEndianUdifCkSum(pCkSum, RT_BE2H_U32(pCkSum->u32Kind), RT_BE2H_U32(pCkSum->cBits));
-}
-
-
-/**
- * Validates an UDIF checksum structure.
- *
- * @returns true if valid, false and LogRel()s on failure.
- * @param   pCkSum      The checksum structure.
- * @param   pszPrefix   The message prefix.
- * @remarks This does not check the checksummed data.
- */
-static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix)
-{
-    bool fRc = true;
-
-    switch (pCkSum->u32Kind)
-    {
-        case DMGUDIFCKSUM_NONE:
-            DMG_VALIDATE(pCkSum->cBits == 0, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
-            break;
-
-        case DMGUDIFCKSUM_CRC32:
-            DMG_VALIDATE(pCkSum->cBits == 32, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
-            break;
-
-        default:
-            DMG_VALIDATE(0, ("%s: u32Kind=%#RX32\n", pszPrefix, pCkSum->u32Kind));
-            break;
-    }
-    return fRc;
-}
-
-
-/**
- * Internal. Flush image data to disk.
- */
-static int dmgFlushImage(PDMGIMAGE pThis)
-{
-    int rc = VINF_SUCCESS;
-
-    if (   pThis->pStorage
-        && !(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-    {
-        /* @todo handle writable files, update checksums etc. */
-    }
-
-    return rc;
-}
-
-
-/**
- * Internal. Free all allocated space for representing an image except pThis,
- * and optionally delete the image from disk.
- */
-static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pThis)
-    {
-        if (pThis->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-                dmgFlushImage(pThis);
-
-            dmgFileClose(pThis);
-            pThis->pStorage = NULL;
-        }
-
-        for (unsigned iRsrc = 0; iRsrc < RT_ELEMENTS(pThis->aRsrcs); iRsrc++)
-            for (unsigned i = 0; i < pThis->aRsrcs[iRsrc].cEntries; i++)
-            {
-                if (pThis->aRsrcs[iRsrc].aEntries[i].pbData)
-                {
-                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pbData);
-                    pThis->aRsrcs[iRsrc].aEntries[i].pbData = NULL;
-                }
-                if (pThis->aRsrcs[iRsrc].aEntries[i].pszName)
-                {
-                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszName);
-                    pThis->aRsrcs[iRsrc].aEntries[i].pszName = NULL;
-                }
-                if (pThis->aRsrcs[iRsrc].aEntries[i].pszCFName)
-                {
-                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
-                    pThis->aRsrcs[iRsrc].aEntries[i].pszCFName = NULL;
-                }
-            }
-
-        if (fDelete && pThis->pszFilename)
-            dmgFileDelete(pThis, pThis->pszFilename);
-
-        if (pThis->pvDecompExtent)
-        {
-            RTMemFree(pThis->pvDecompExtent);
-            pThis->pvDecompExtent = NULL;
-            pThis->cbDecompExtent = 0;
-        }
-
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-#define STARTS_WITH(pszString, szStart) \
-        ( strncmp(pszString, szStart, sizeof(szStart) - 1) == 0 )
-
-#define STARTS_WITH_WORD(pszString, szWord) \
-        (   STARTS_WITH(pszString, szWord) \
-         && !RT_C_IS_ALNUM((pszString)[sizeof(szWord) - 1]) )
-
-#define SKIP_AHEAD(psz, szWord) \
-        do { \
-            (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
-        } while (0)
-
-#define REQUIRE_WORD(psz, szWord) \
-        do { \
-            if (!STARTS_WITH_WORD(psz, szWord)) \
-                return psz; \
-            (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
-        } while (0)
-
-#define REQUIRE_TAG(psz, szTag) \
-        do { \
-            if (!STARTS_WITH(psz, "<" szTag ">")) \
-                return psz; \
-            (psz) = RTStrStripL((psz) + sizeof("<" szTag ">") - 1); \
-        } while (0)
-
-#define REQUIRE_TAG_NO_STRIP(psz, szTag) \
-        do { \
-            if (!STARTS_WITH(psz, "<" szTag ">")) \
-                return psz; \
-            (psz) += sizeof("<" szTag ">") - 1; \
-        } while (0)
-
-#define REQUIRE_END_TAG(psz, szTag) \
-        do { \
-            if (!STARTS_WITH(psz, "</" szTag ">")) \
-                return psz; \
-            (psz) = RTStrStripL((psz) + sizeof("</" szTag ">") - 1); \
-        } while (0)
-
-
-/**
- * Finds the next tag end.
- *
- * @returns Pointer to a '>' or '\0'.
- * @param   pszCur      The current position.
- */
-static const char *dmgXmlFindTagEnd(const char *pszCur)
-{
-    /* Might want to take quoted '>' into account? */
-    char ch;
-    while ((ch = *pszCur) != '\0' && ch != '>')
-        pszCur++;
-    return pszCur;
-}
-
-
-/**
- * Finds the end tag.
- *
- * Does not deal with '<tag attr="1"/>' style tags.
- *
- * @returns Pointer to the first char in the end tag. NULL if another tag
- *          was encountered first or if we hit the end of the file.
- * @param   ppszCur     The current position (IN/OUT).
- * @param   pszTag      The tag name.
- */
-static const char *dmgXmlFindEndTag(const char **ppszCur, const char *pszTag)
-{
-    const char         *psz = *ppszCur;
-    char                ch;
-    while ((ch = *psz))
-    {
-        if (ch == '<')
-        {
-            size_t const cchTag = strlen(pszTag);
-            if (    psz[1] == '/'
-                &&  !memcmp(&psz[2], pszTag, cchTag)
-                &&  psz[2 + cchTag] == '>')
-            {
-                *ppszCur = psz + 2 + cchTag + 1;
-                return psz;
-            }
-            break;
-        }
-        psz++;
-    }
-    return NULL;
-}
-
-
-/**
- * Reads a signed 32-bit value.
- *
- * @returns NULL on success, pointer to the offending text on failure.
- * @param   ppszCur     The text position (IN/OUT).
- * @param   pi32        Where to store the value.
- */
-static const char *dmgXmlParseS32(const char **ppszCur, int32_t *pi32)
-{
-    const char *psz = *ppszCur;
-
-    /*
-     * <string>-1</string>
-     */
-    REQUIRE_TAG_NO_STRIP(psz, "string");
-
-    char *pszNext;
-    int rc = RTStrToInt32Ex(psz, &pszNext, 0, pi32);
-    if (rc != VWRN_TRAILING_CHARS)
-        return *ppszCur;
-    psz = pszNext;
-
-    REQUIRE_END_TAG(psz, "string");
-    *ppszCur = psz;
-    return NULL;
-}
-
-
-/**
- * Reads an unsigned 32-bit value.
- *
- * @returns NULL on success, pointer to the offending text on failure.
- * @param   ppszCur     The text position (IN/OUT).
- * @param   pu32        Where to store the value.
- */
-static const char *dmgXmlParseU32(const char **ppszCur, uint32_t *pu32)
-{
-    const char *psz = *ppszCur;
-
-    /*
-     * <string>0x00ff</string>
-     */
-    REQUIRE_TAG_NO_STRIP(psz, "string");
-
-    char *pszNext;
-    int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
-    if (rc != VWRN_TRAILING_CHARS)
-        return *ppszCur;
-    psz = pszNext;
-
-    REQUIRE_END_TAG(psz, "string");
-    *ppszCur = psz;
-    return NULL;
-}
-
-
-/**
- * Reads a string value.
- *
- * @returns NULL on success, pointer to the offending text on failure.
- * @param   ppszCur     The text position (IN/OUT).
- * @param   ppszString  Where to store the pointer to the string. The caller
- *                      must free this using RTMemFree.
- */
-static const char *dmgXmlParseString(const char **ppszCur, char **ppszString)
-{
-    const char *psz = *ppszCur;
-
-    /*
-     * <string>Driver Descriptor Map (DDM : 0)</string>
-     */
-    REQUIRE_TAG_NO_STRIP(psz, "string");
-
-    const char *pszStart = psz;
-    const char *pszEnd = dmgXmlFindEndTag(&psz, "string");
-    if (!pszEnd)
-        return *ppszCur;
-    psz = RTStrStripL(psz);
-
-    *ppszString = (char *)RTMemDupEx(pszStart, pszEnd - pszStart, 1);
-    if (!*ppszString)
-        return *ppszCur;
-
-    *ppszCur = psz;
-    return NULL;
-}
-
-
-/**
- * Parses the BASE-64 coded data tags.
- *
- * @returns NULL on success, pointer to the offending text on failure.
- * @param   ppszCur     The text position (IN/OUT).
- * @param   ppbData     Where to store the pointer to the data we've read. The
- *                      caller must free this using RTMemFree.
- * @param   pcbData     The number of bytes we're returning.
- */
-static const char *dmgXmlParseData(const char **ppszCur, uint8_t **ppbData, size_t *pcbData)
-{
-    const char *psz = *ppszCur;
-
-    /*
-     * <data>   AAAAA...    </data>
-     */
-    REQUIRE_TAG(psz, "data");
-
-    const char *pszStart = psz;
-    ssize_t cbData = RTBase64DecodedSize(pszStart, (char **)&psz);
-    if (cbData == -1)
-        return *ppszCur;
-    const char *pszEnd = psz;
-
-    REQUIRE_END_TAG(psz, "data");
-
-    *ppbData = (uint8_t *)RTMemAlloc(cbData);
-    if (!*ppbData)
-        return *ppszCur;
-    char *pszIgnored;
-    int rc = RTBase64Decode(pszStart, *ppbData, cbData, pcbData, &pszIgnored);
-    if (RT_FAILURE(rc))
-    {
-        RTMemFree(*ppbData);
-        *ppbData = NULL;
-        return *ppszCur;
-    }
-
-    *ppszCur = psz;
-    return NULL;
-}
-
-
-/**
- * Parses the XML resource-fork in a rather presumptive manner.
- *
- * This function is supposed to construct the DMG::aRsrcs instance data
- * parts.
- *
- * @returns NULL on success, pointer to the problematic text on failure.
- * @param   pThis       The DMG instance data.
- * @param   pszXml      The XML text to parse, UTF-8.
- * @param   cch         The size of the the XML text.
- */
-static const char *dmgOpenXmlToRsrc(PDMGIMAGE pThis, char const *pszXml)
-{
-    const char *psz = pszXml;
-
-    /*
-     * Verify the ?xml, !DOCTYPE and plist tags.
-     */
-    SKIP_AHEAD(psz, "");
-
-    /* <?xml version="1.0" encoding="UTF-8"?> */
-    REQUIRE_WORD(psz, "<?xml");
-    while (*psz != '?')
-    {
-        if (!*psz)
-            return psz;
-        if (STARTS_WITH_WORD(psz, "version="))
-        {
-            SKIP_AHEAD(psz, "version=");
-            REQUIRE_WORD(psz, "\"1.0\"");
-        }
-        else if (STARTS_WITH_WORD(psz, "encoding="))
-        {
-            SKIP_AHEAD(psz, "encoding=");
-            REQUIRE_WORD(psz, "\"UTF-8\"");
-        }
-        else
-            return psz;
-    }
-    SKIP_AHEAD(psz, "?>");
-
-    /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
-    REQUIRE_WORD(psz, "<!DOCTYPE");
-    REQUIRE_WORD(psz, "plist");
-    REQUIRE_WORD(psz, "PUBLIC");
-    psz = dmgXmlFindTagEnd(psz);
-    REQUIRE_WORD(psz, ">");
-
-    /* <plist version="1.0"> */
-    REQUIRE_WORD(psz, "<plist");
-    REQUIRE_WORD(psz, "version=");
-    REQUIRE_WORD(psz, "\"1.0\"");
-    REQUIRE_WORD(psz, ">");
-
-    /*
-     * Descend down to the 'resource-fork' dictionary.
-     * ASSUME it's the only top level dictionary.
-     */
-    /* <dict> <key>resource-fork</key> */
-    REQUIRE_TAG(psz, "dict");
-    REQUIRE_WORD(psz, "<key>resource-fork</key>");
-
-    /*
-     * Parse the keys in the resource-fork dictionary.
-     * ASSUME that there are just two, 'blkx' and 'plst'.
-     */
-    REQUIRE_TAG(psz, "dict");
-    while (!STARTS_WITH_WORD(psz, "</dict>"))
-    {
-        /*
-         * Parse the key and Create the resource-fork entry.
-         */
-        unsigned iRsrc;
-        if (STARTS_WITH_WORD(psz, "<key>blkx</key>"))
-        {
-            REQUIRE_WORD(psz, "<key>blkx</key>");
-            iRsrc = DMG_RSRC_IDX_BLKX;
-            strcpy(&pThis->aRsrcs[iRsrc].szName[0], "blkx");
-        }
-        else if (STARTS_WITH_WORD(psz, "<key>plst</key>"))
-        {
-            REQUIRE_WORD(psz, "<key>plst</key>");
-            iRsrc = DMG_RSRC_IDX_PLST;
-            strcpy(&pThis->aRsrcs[iRsrc].szName[0], "plst");
-        }
-        else
-        {
-            SKIP_AHEAD(psz, "</array>");
-            continue;
-        }
-
-
-        /*
-         * Descend into the array and add the elements to the resource entry.
-         */
-        /* <array> */
-        REQUIRE_TAG(psz, "array");
-        while (!STARTS_WITH_WORD(psz, "</array>"))
-        {
-            REQUIRE_TAG(psz, "dict");
-            uint32_t i = pThis->aRsrcs[iRsrc].cEntries;
-            if (i == RT_ELEMENTS(pThis->aRsrcs[iRsrc].aEntries))
-                return psz;
-
-            while (!STARTS_WITH_WORD(psz, "</dict>"))
-            {
-
-                /* switch on the key. */
-                const char *pszErr;
-                if (STARTS_WITH_WORD(psz, "<key>Attributes</key>"))
-                {
-                    REQUIRE_WORD(psz, "<key>Attributes</key>");
-                    pszErr = dmgXmlParseU32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].fAttributes);
-                }
-                else if (STARTS_WITH_WORD(psz, "<key>ID</key>"))
-                {
-                    REQUIRE_WORD(psz, "<key>ID</key>");
-                    pszErr = dmgXmlParseS32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].iId);
-                }
-                else if (STARTS_WITH_WORD(psz, "<key>Name</key>"))
-                {
-                    REQUIRE_WORD(psz, "<key>Name</key>");
-                    pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszName);
-                }
-                else if (STARTS_WITH_WORD(psz, "<key>CFName</key>"))
-                {
-                    REQUIRE_WORD(psz, "<key>CFName</key>");
-                    pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
-                }
-                else if (STARTS_WITH_WORD(psz, "<key>Data</key>"))
-                {
-                    REQUIRE_WORD(psz, "<key>Data</key>");
-                    pszErr = dmgXmlParseData(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pbData, &pThis->aRsrcs[iRsrc].aEntries[i].cbData);
-                }
-                else
-                    pszErr = psz;
-                if (pszErr)
-                    return pszErr;
-            } /* while not </dict> */
-            REQUIRE_END_TAG(psz, "dict");
-
-            pThis->aRsrcs[iRsrc].cEntries++;
-        } /* while not </array> */
-        REQUIRE_END_TAG(psz, "array");
-
-    } /* while not </dict> */
-    REQUIRE_END_TAG(psz, "dict");
-
-    /*
-     * ASSUMING there is only the 'resource-fork', we'll now see the end of
-     * the outer dict, plist and text.
-     */
-    /* </dict> </plist> */
-    REQUIRE_END_TAG(psz, "dict");
-    REQUIRE_END_TAG(psz, "plist");
-
-    /* the end */
-    if (*psz)
-        return psz;
-
-    return NULL;
-}
-
-#undef REQUIRE_END_TAG
-#undef REQUIRE_TAG_NO_STRIP
-#undef REQUIRE_TAG
-#undef REQUIRE_WORD
-#undef SKIP_AHEAD
-#undef STARTS_WITH_WORD
-#undef STARTS_WITH
-
-/**
- * Returns the data attached to a resource.
- *
- * @returns VBox status code.
- * @param   pThis        The DMG instance data.
- * @param   pcszRsrcName Name of the resource to get.
- */
-static int dmgGetRsrcData(PDMGIMAGE pThis, const char *pcszRsrcName,
-                          PCDMGUDIFRSRCARRAY *ppcRsrc)
-{
-    int rc = VERR_NOT_FOUND;
-
-    for (unsigned i = 0; i < RT_ELEMENTS(pThis->aRsrcs); i++)
-    {
-        if (!strcmp(pThis->aRsrcs[i].szName, pcszRsrcName))
-        {
-            *ppcRsrc = &pThis->aRsrcs[i];
-            rc = VINF_SUCCESS;
-            break;
-        }
-    }
-
-    return rc;
-}
-
-/**
- * Creates a new extent from the given blkx descriptor.
- *
- * @returns VBox status code.
- * @param   pThis          DMG instance data.
- * @param   uSectorPart    First sector the partition owning the blkx descriptor has.
- * @param   pBlkxDesc      The blkx descriptor.
- */
-static int dmgExtentCreateFromBlkxDesc(PDMGIMAGE pThis, uint64_t uSectorPart, PDMGBLKXDESC pBlkxDesc)
-{
-    int rc = VINF_SUCCESS;
-    DMGEXTENTTYPE enmExtentTypeNew;
-    PDMGEXTENT pExtentNew = NULL;
-
-    if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_RAW)
-        enmExtentTypeNew = DMGEXTENTTYPE_RAW;
-    else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_IGNORE)
-        enmExtentTypeNew = DMGEXTENTTYPE_ZERO;
-    else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_ZLIB)
-        enmExtentTypeNew = DMGEXTENTTYPE_COMP_ZLIB;
-    else
-    {
-        AssertMsgFailed(("This method supports only raw or zero extents!\n"));
-        return VERR_NOT_SUPPORTED;
-    }
-
-    /** @todo: Merge raw extents if possible to save memory. */
-#if 0
-    pExtentNew = pThis->pExtentLast;
-    if (   pExtentNew
-        && pExtentNew->enmType == enmExtentTypeNew
-        && enmExtentTypeNew == DMGEXTENTTYPE_RAW
-        && pExtentNew->uSectorExtent + pExtentNew->cSectorsExtent == offDevice + pBlkxDesc->u64SectorStart * DMG_SECTOR_SIZE;
-        && pExtentNew->offFileStart + pExtentNew->cbExtent == pBlkxDesc->offData)
-    {
-        /* Increase the last extent. */
-        pExtentNew->cbExtent += pBlkxDesc->cbData;
-    }
-    else
-#endif
-    {
-        if (pThis->cExtentsMax == pThis->cExtents)
-        {
-            pThis->cExtentsMax += 64;
-
-            /* Increase the array. */
-            PDMGEXTENT paExtentsNew = (PDMGEXTENT)RTMemRealloc(pThis->paExtents, sizeof(DMGEXTENT) * pThis->cExtentsMax);
-            if (!paExtentsNew)
-            {
-                rc = VERR_NO_MEMORY;
-                pThis->cExtentsMax -= 64;
-            }
-            else
-                pThis->paExtents = paExtentsNew;
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            pExtentNew = &pThis->paExtents[pThis->cExtents++];
-
-            pExtentNew->enmType        = enmExtentTypeNew;
-            pExtentNew->uSectorExtent  = uSectorPart + pBlkxDesc->u64SectorStart;
-            pExtentNew->cSectorsExtent = pBlkxDesc->u64SectorCount;
-            pExtentNew->offFileStart   = pBlkxDesc->offData;
-            pExtentNew->cbFile         = pBlkxDesc->cbData;
-        }
-    }
-
-    return rc;
-}
-
-/**
- * Find the extent for the given sector number.
- */
-static PDMGEXTENT dmgExtentGetFromOffset(PDMGIMAGE pThis, uint64_t uSector)
-{
-    /*
-     * We assume that the array is ordered from lower to higher sector
-     * numbers.
-     * This makes it possible to bisect the array to find the extent
-     * faster than using a linked list.
-     */
-    PDMGEXTENT pExtent = NULL;
-    unsigned idxCur = pThis->idxExtentLast;
-    unsigned idxMax = pThis->cExtents;
-    unsigned idxMin = 0;
-
-    while (idxMin < idxMax)
-    {
-        PDMGEXTENT pExtentCur = &pThis->paExtents[idxCur];
-
-        /* Determine the search direction. */
-        if (uSector < pExtentCur->uSectorExtent)
-        {
-            /* Search left from the current extent. */
-            idxMax = idxCur;
-        }
-        else if (uSector >= pExtentCur->uSectorExtent + pExtentCur->cSectorsExtent)
-        {
-            /* Search right from the current extent. */
-            idxMin = idxCur;
-        }
-        else
-        {
-            /* The sector lies in the extent, stop searching. */
-            pExtent = pExtentCur;
-            break;
-        }
-
-        idxCur = idxMin + (idxMax - idxMin) / 2;
-    }
-
-    if (pExtent)
-        pThis->idxExtentLast = idxCur;
-
-    return pExtent;
-}
-
-/**
- * Goes through the BLKX structure and creates the necessary extents.
- */
-static int dmgBlkxParse(PDMGIMAGE pThis, PDMGBLKX pBlkx)
-{
-    int rc = VINF_SUCCESS;
-    PDMGBLKXDESC pBlkxDesc = (PDMGBLKXDESC)(pBlkx + 1);
-
-    for (unsigned i = 0; i < pBlkx->cBlocksRunCount; i++)
-    {
-        dmgBlkxDescFile2HostEndian(pBlkxDesc);
-
-        switch (pBlkxDesc->u32Type)
-        {
-            case DMGBLKXDESC_TYPE_RAW:
-            case DMGBLKXDESC_TYPE_IGNORE:
-            case DMGBLKXDESC_TYPE_ZLIB:
-            {
-                rc = dmgExtentCreateFromBlkxDesc(pThis, pBlkx->cSectornumberFirst, pBlkxDesc);
-                break;
-            }
-            case DMGBLKXDESC_TYPE_COMMENT:
-            case DMGBLKXDESC_TYPE_TERMINATOR:
-                break;
-            default:
-                rc = VERR_VD_DMG_INVALID_HEADER;
-                break;
-        }
-
-        if (   pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_TERMINATOR
-            || RT_FAILURE(rc))
-                break;
-
-        pBlkxDesc++;
-    }
-
-    return rc;
-}
-
-/**
- * Worker for dmgOpen that reads in and validates all the necessary
- * structures from the image.
- *
- * @returns VBox status code.
- * @param   pThis       The DMG instance data.
- * @param   uOpenFlags  Flags for defining the open type.
- */
-static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
-{
-    pThis->uOpenFlags  = uOpenFlags;
-
-    pThis->pInterfaceError = VDInterfaceGet(pThis->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pThis->pInterfaceError)
-        pThis->pInterfaceErrorCallbacks = VDGetInterfaceError(pThis->pInterfaceError);
-
-    /* Get I/O interface. */
-    pThis->pInterfaceIO = VDInterfaceGet(pThis->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pThis->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pThis->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pThis->pInterfaceIO);
-    AssertPtrReturn(pThis->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    int rc = dmgFileOpen(pThis, pThis->pszFilename,
-                         VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                    false /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        return rc;
-    }
-
-    /*
-     * Read the footer.
-     */
-    rc = dmgFileGetSize(pThis, &pThis->cbFile);
-    if (RT_FAILURE(rc))
-        return rc;
-    if (pThis->cbFile < 1024)
-        return VERR_VD_DMG_INVALID_HEADER;
-    rc = dmgFileReadSync(pThis, pThis->cbFile - sizeof(pThis->Ftr), &pThis->Ftr, sizeof(pThis->Ftr), NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    dmgUdifFtrFile2HostEndian(&pThis->Ftr);
-
-    /*
-     * Do we recognize the footer structure? If so, is it valid?
-     */
-    if (pThis->Ftr.u32Magic != DMGUDIF_MAGIC)
-        return VERR_VD_DMG_INVALID_HEADER;
-    if (pThis->Ftr.u32Version != DMGUDIF_VER_CURRENT)
-        return VERR_VD_DMG_INVALID_HEADER;
-    if (pThis->Ftr.cbFooter != sizeof(pThis->Ftr))
-        return VERR_VD_DMG_INVALID_HEADER;
-
-    if (!dmgUdifFtrIsValid(&pThis->Ftr, pThis->cbFile - sizeof(pThis->Ftr)))
-    {
-        DMG_PRINTF(("Bad DMG: '%s' cbFile=%RTfoff\n", pThis->pszFilename, pThis->cbFile));
-        return VERR_VD_DMG_INVALID_HEADER;
-    }
-
-    pThis->cbSize = pThis->Ftr.cSectors * DMG_SECTOR_SIZE;
-
-    /*
-     * Read and parse the XML portion.
-     */
-    size_t cchXml = (size_t)pThis->Ftr.cbXml;
-    char *pszXml = (char *)RTMemAlloc(cchXml + 1);
-    if (!pszXml)
-        return VERR_NO_MEMORY;
-    rc = dmgFileReadSync(pThis, pThis->Ftr.offXml, pszXml, cchXml, NULL);
-    if (RT_SUCCESS(rc))
-    {
-        pszXml[cchXml] = '\0';
-        const char *pszError = dmgOpenXmlToRsrc(pThis, pszXml);
-        if (!pszError)
-        {
-            PCDMGUDIFRSRCARRAY pRsrcBlkx = NULL;
-
-            rc = dmgGetRsrcData(pThis, "blkx", &pRsrcBlkx);
-            if (RT_SUCCESS(rc))
-            {
-                for (unsigned idxBlkx = 0; idxBlkx < pRsrcBlkx->cEntries; idxBlkx++)
-                {
-                    PDMGBLKX pBlkx = NULL;
-
-                    if (pRsrcBlkx->aEntries[idxBlkx].cbData < sizeof(DMGBLKX))
-                    {
-                        rc = VERR_VD_DMG_INVALID_HEADER;
-                        break;
-                    }
-
-                    pBlkx = (PDMGBLKX)RTMemAllocZ(pRsrcBlkx->aEntries[idxBlkx].cbData);
-                    if (!pBlkx)
-                    {
-                        rc = VERR_NO_MEMORY;
-                        break;
-                    }
-
-                    memcpy(pBlkx, pRsrcBlkx->aEntries[idxBlkx].pbData, pRsrcBlkx->aEntries[idxBlkx].cbData);
-
-                    dmgBlkxFile2HostEndian(pBlkx);
-
-                    if (   dmgBlkxIsValid(pBlkx)
-                        && pRsrcBlkx->aEntries[idxBlkx].cbData == pBlkx->cBlocksRunCount * sizeof(DMGBLKXDESC) + sizeof(DMGBLKX))
-                        rc = dmgBlkxParse(pThis, pBlkx);
-                    else
-                        rc = VERR_VD_DMG_INVALID_HEADER;
-
-                    RTMemFree(pBlkx);
-
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-            }
-            else
-                rc = VERR_VD_DMG_INVALID_HEADER;
-        }
-        else
-        {
-            DMG_PRINTF(("**** XML DUMP BEGIN ***\n%s\n**** XML DUMP END ****\n", pszXml));
-            DMG_PRINTF(("**** Bad XML at %#lx (%lu) ***\n%.256s\n**** Bad XML END ****\n",
-                            (unsigned long)(pszError - pszXml), (unsigned long)(pszError - pszXml), pszError));
-            rc = VERR_VD_DMG_XML_PARSE_ERROR;
-        }
-    }
-    RTMemFree(pszXml);
-
-    if (RT_FAILURE(rc))
-        dmgFreeImage(pThis, false);
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int dmgCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
-                 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
-    int rc;
-    PVDIOSTORAGE pStorage;
-    uint64_t cbFile, offFtr = 0;
-    DMGUDIF Ftr;
-
-    /* Get I/O interface. */
-    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
-    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
-    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the file and read the footer.
-     */
-    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
-                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
-                                                                   false /* fCreate */),
-                                        &pStorage);
-    if (RT_SUCCESS(rc))
-        rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
-                                               &cbFile);
-    if (RT_SUCCESS(rc))
-    {
-        offFtr = cbFile - sizeof(Ftr);
-        rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
-                                                offFtr, &Ftr, sizeof(Ftr), NULL);
-    }
-    else
-    {
-        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-        rc = VERR_VD_DMG_INVALID_HEADER;
-    }
-    if (RT_SUCCESS(rc))
-    {
-        dmgUdifFtrFile2HostEndian(&Ftr);
-
-        /*
-         * Do we recognize this stuff? Does it look valid?
-         */
-        if (    Ftr.u32Magic    == DMGUDIF_MAGIC
-            &&  Ftr.u32Version  == DMGUDIF_VER_CURRENT
-            &&  Ftr.cbFooter    == sizeof(Ftr))
-        {
-            if (dmgUdifFtrIsValid(&Ftr, offFtr))
-            {
-                rc = VINF_SUCCESS;
-                *penmType = VDTYPE_DVD;
-            }
-            else
-            {
-                DMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
-                rc = VERR_VD_DMG_INVALID_HEADER;
-            }
-        }
-        else
-            rc = VERR_VD_DMG_INVALID_HEADER;
-    }
-
-    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int dmgOpen(const char *pszFilename, unsigned uOpenFlags,
-                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                   VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc = VINF_SUCCESS;
-    PDMGIMAGE pThis;
-
-    /* Check open flags. All valid flags are (in principle) supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /*
-     * Reject combinations we don't currently support.
-     *
-     * There is no point in being paranoid about the input here as we're just a
-     * simple backend and can expect the caller to be the only user and already
-     * have validate what it passes thru to us.
-     */
-    if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-    {
-        rc = VERR_NOT_SUPPORTED;
-        goto out;
-    }
-
-    /*
-     * Create the basic instance data structure and open the file,
-     * then hand it over to a worker function that does all the rest.
-     */
-    pThis = (PDMGIMAGE)RTMemAllocZ(sizeof(*pThis));
-    if (!pThis)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    pThis->pszFilename = pszFilename;
-    pThis->pStorage    = NULL;
-    pThis->pVDIfsDisk  = pVDIfsDisk;
-    pThis->pVDIfsImage = pVDIfsImage;
-
-    rc = dmgOpenImage(pThis, uOpenFlags);
-  if (RT_SUCCESS(rc))
-        *ppBackendData = pThis;
-    else
-        RTMemFree(pThis);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int dmgCreate(const char *pszFilename, uint64_t cbSize,
-                     unsigned uImageFlags, const char *pszComment,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     PCRTUUID pUuid, unsigned uOpenFlags,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc = VERR_NOT_SUPPORTED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int dmgRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-    int rc = VERR_NOT_SUPPORTED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int dmgClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    rc = dmgFreeImage(pThis, fDelete);
-    RTMemFree(pThis);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                   size_t cbToRead, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    PDMGEXTENT pExtent = NULL;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pThis);
-    Assert(uOffset % DMG_SECTOR_SIZE == 0);
-    Assert(cbToRead % DMG_SECTOR_SIZE == 0);
-
-    if (   uOffset + cbToRead > pThis->cbSize
-        || cbToRead == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pExtent = dmgExtentGetFromOffset(pThis, DMG_BYTE2BLOCK(uOffset));
-
-    if (pExtent)
-    {
-        uint64_t uExtentRel = DMG_BYTE2BLOCK(uOffset) - pExtent->uSectorExtent;
-
-        /* Remain in this extent. */
-        cbToRead = RT_MIN(cbToRead, DMG_BLOCK2BYTE(pExtent->cSectorsExtent - uExtentRel));
-
-        switch (pExtent->enmType)
-        {
-            case DMGEXTENTTYPE_RAW:
-            {
-                rc = dmgFileReadSync(pThis, pExtent->offFileStart + DMG_BLOCK2BYTE(uExtentRel), pvBuf, cbToRead, NULL);
-                break;
-            }
-            case DMGEXTENTTYPE_ZERO:
-            {
-                memset(pvBuf, 0, cbToRead);
-                break;
-            }
-            case DMGEXTENTTYPE_COMP_ZLIB:
-            {
-                if (pThis->pExtentDecomp != pExtent)
-                {
-                    if (DMG_BLOCK2BYTE(pExtent->cSectorsExtent) > pThis->cbDecompExtent)
-                    {
-                        if (RT_LIKELY(pThis->pvDecompExtent))
-                            RTMemFree(pThis->pvDecompExtent);
-
-                        pThis->pvDecompExtent = RTMemAllocZ(DMG_BLOCK2BYTE(pExtent->cSectorsExtent));
-                        if (!pThis->pvDecompExtent)
-                            rc = VERR_NO_MEMORY;
-                        else
-                            pThis->cbDecompExtent = DMG_BLOCK2BYTE(pExtent->cSectorsExtent);
-                    }
-
-                    if (RT_SUCCESS(rc))
-                    {
-                        rc = dmgFileInflateSync(pThis, pExtent->offFileStart, pExtent->cbFile,
-                                                pThis->pvDecompExtent,
-                                                RT_MIN(pThis->cbDecompExtent, DMG_BLOCK2BYTE(pExtent->cSectorsExtent)));
-                        if (RT_SUCCESS(rc))
-                            pThis->pExtentDecomp = pExtent;
-                    }
-                }
-
-                if (RT_SUCCESS(rc))
-                    memcpy(pvBuf, (uint8_t *)pThis->pvDecompExtent + DMG_BLOCK2BYTE(uExtentRel), cbToRead);
-                break;
-            }
-            default:
-                AssertMsgFailed(("Invalid extent type\n"));
-        }
-
-        if (RT_SUCCESS(rc))
-            *pcbActuallyRead = cbToRead;
-    }
-    else
-        rc = VERR_INVALID_PARAMETER;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int dmgWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                    size_t cbToWrite, size_t *pcbWriteProcess,
-                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
-                 pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc = VERR_NOT_IMPLEMENTED;
-
-    AssertPtr(pThis);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    AssertMsgFailed(("Not implemented\n"));
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int dmgFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    rc = dmgFlushImage(pThis);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned dmgGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        return 1;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t dmgGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pThis);
-
-    if (pThis && pThis->pStorage)
-        cb = pThis->cbSize;
-
-    LogFlowFunc(("returns %llu\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t dmgGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        uint64_t cbFile;
-        if (pThis->pStorage)
-        {
-            int rc = dmgFileGetSize(pThis, &cbFile);
-            if (RT_SUCCESS(rc))
-                cb = cbFile;
-        }
-    }
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (pThis->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pThis->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
-                 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pThis->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
-{
-     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (pThis->LCHSGeometry.cCylinders)
-        {
-            *pLCHSGeometry = pThis->LCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
-                 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pThis->LCHSGeometry = *pLCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned dmgGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        uImageFlags = pThis->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned dmgGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        uOpenFlags = pThis->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int dmgSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pThis || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO || VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    rc = dmgFreeImage(pThis, false);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = dmgOpenImage(pThis, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int dmgGetComment(void *pBackendData, char *pszComment,
-                         size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int dmgSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PDMGIMAGE pImage = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int dmgGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int dmgSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int dmgGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int dmgSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int dmgGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int dmgSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int dmgGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int dmgSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pThis);
-
-    if (pThis)
-    {
-        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void dmgDump(void *pBackendData)
-{
-    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
-
-    AssertPtr(pThis);
-    if (pThis)
-    {
-        dmgMessage(pThis, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
-                    pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors,
-                    pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors,
-                    pThis->cbSize / 512);
-    }
-}
-
-
-VBOXHDDBACKEND g_DmgBackend =
-{
-    /* pszBackendName */
-    "DMG",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-    VD_CAP_FILE | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aDmgFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    dmgCheckIfValid,
-    /* pfnOpen */
-    dmgOpen,
-    /* pfnCreate */
-    dmgCreate,
-    /* pfnRename */
-    dmgRename,
-    /* pfnClose */
-    dmgClose,
-    /* pfnRead */
-    dmgRead,
-    /* pfnWrite */
-    dmgWrite,
-    /* pfnFlush */
-    dmgFlush,
-    /* pfnGetVersion */
-    dmgGetVersion,
-    /* pfnGetSize */
-    dmgGetSize,
-    /* pfnGetFileSize */
-    dmgGetFileSize,
-    /* pfnGetPCHSGeometry */
-    dmgGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    dmgSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    dmgGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    dmgSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    dmgGetImageFlags,
-    /* pfnGetOpenFlags */
-    dmgGetOpenFlags,
-    /* pfnSetOpenFlags */
-    dmgSetOpenFlags,
-    /* pfnGetComment */
-    dmgGetComment,
-    /* pfnSetComment */
-    dmgSetComment,
-    /* pfnGetUuid */
-    dmgGetUuid,
-    /* pfnSetUuid */
-    dmgSetUuid,
-    /* pfnGetModificationUuid */
-    dmgGetModificationUuid,
-    /* pfnSetModificationUuid */
-    dmgSetModificationUuid,
-    /* pfnGetParentUuid */
-    dmgGetParentUuid,
-    /* pfnSetParentUuid */
-    dmgSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    dmgGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    dmgSetParentModificationUuid,
-    /* pfnDump */
-    dmgDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    NULL,
-    /* pfnAsyncRead */
-    NULL,
-    /* pfnAsyncWrite */
-    NULL,
-    /* pfnAsyncFlush */
-    NULL,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    NULL
-};
Index: /trunk/src/VBox/Devices/Storage/DrvVD.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DrvVD.cpp	(revision 33566)
+++ /trunk/src/VBox/Devices/Storage/DrvVD.cpp	(revision 33567)
@@ -21,5 +21,5 @@
 *******************************************************************************/
 #define LOG_GROUP LOG_GROUP_DRV_VD
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 #include <VBox/pdmdrv.h>
 #include <VBox/pdmasynccompletion.h>
Index: unk/src/VBox/Devices/Storage/ISCSIHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/ISCSIHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,5615 +1,0 @@
-/* $Id$ */
-/** @file
- * iSCSI initiator driver, VD backend.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_ISCSI
-#include <VBox/VBoxHDD-Plugin.h>
-#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
-#include "VDICore.h"
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/alloc.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include <iprt/string.h>
-#include <iprt/asm.h>
-#include <iprt/thread.h>
-#include <iprt/semaphore.h>
-#include <iprt/md5.h>
-#include <iprt/tcp.h>
-#include <iprt/time.h>
-#include <VBox/scsi.h>
-
-
-/*******************************************************************************
-*   Defined Constants And Macros                                               *
-*******************************************************************************/
-
-/** Default port number to use for iSCSI. */
-#define ISCSI_DEFAULT_PORT 3260
-
-
-/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
-#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
-/** Converts a hex char into the corresponding number in the range 0-15. */
-#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
-/* Converts a base64 char into the corresponding number in the range 0-63. */
-#define B64_2_NUM(c) ((c >= 'A' && c <= 'Z') ? (c - 'A') : (c >= 'a' && c <= 'z') ? (c - 'a' + 26) : (c >= '0' && c <= '9') ? (c - '0' + 52) : (c == '+') ? 62 : (c == '/') ? 63 : -1)
-
-
-/** Minimum CHAP_MD5 challenge length in bytes. */
-#define CHAP_MD5_CHALLENGE_MIN 16
-/** Maximum CHAP_MD5 challenge length in bytes. */
-#define CHAP_MD5_CHALLENGE_MAX 24
-
-
-/**
- * SCSI peripheral device type. */
-typedef enum SCSIDEVTYPE
-{
-    /** direct-access device. */
-    SCSI_DEVTYPE_DISK = 0,
-    /** sequential-access device. */
-    SCSI_DEVTYPE_TAPE,
-    /** printer device. */
-    SCSI_DEVTYPE_PRINTER,
-    /** processor device. */
-    SCSI_DEVTYPE_PROCESSOR,
-    /** write-once device. */
-    SCSI_DEVTYPE_WORM,
-    /** CD/DVD device. */
-    SCSI_DEVTYPE_CDROM,
-    /** scanner device. */
-    SCSI_DEVTYPE_SCANNER,
-    /** optical memory device. */
-    SCSI_DEVTYPE_OPTICAL,
-    /** medium changer. */
-    SCSI_DEVTYPE_CHANGER,
-    /** communications device. */
-    SCSI_DEVTYPE_COMMUNICATION,
-    /** storage array controller device. */
-    SCSI_DEVTYPE_RAIDCTL = 0x0c,
-    /** enclosure services device. */
-    SCSI_DEVTYPE_ENCLOSURE,
-    /** simplified direct-access device. */
-    SCSI_DEVTYPE_SIMPLEDISK,
-    /** optical card reader/writer device. */
-    SCSI_DEVTYPE_OCRW,
-    /** bridge controller device. */
-    SCSI_DEVTYPE_BRIDGE,
-    /** object-based storage device. */
-    SCSI_DEVTYPE_OSD
-} SCSIDEVTYPE;
-
-/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
-#define SCSI_DEVTYPE_MASK 0x1f
-
-/** Mask to extract the CmdQue bit out of the seventh byte of the INQUIRY response. */
-#define SCSI_INQUIRY_CMDQUE_MASK 0x02
-
-/** Maximum PDU payload size we can handle in one piece. Greater or equal than
- * s_iscsiConfigDefaultWriteSplit. */
-#define ISCSI_DATA_LENGTH_MAX _256K
-
-/** Maximum PDU size we can handle in one piece. */
-#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
-
-
-/** Version of the iSCSI standard which this initiator driver can handle. */
-#define ISCSI_MY_VERSION 0
-
-
-/** Length of ISCSI basic header segment. */
-#define ISCSI_BHS_SIZE 48
-
-
-/** Reserved task tag value. */
-#define ISCSI_TASK_TAG_RSVD 0xffffffff
-
-
-/**
- * iSCSI opcodes. */
-typedef enum ISCSIOPCODE
-{
-    /** NOP-Out. */
-    ISCSIOP_NOP_OUT = 0x00000000,
-    /** SCSI command. */
-    ISCSIOP_SCSI_CMD = 0x01000000,
-    /** SCSI task management request. */
-    ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
-    /** Login request. */
-    ISCSIOP_LOGIN_REQ = 0x03000000,
-    /** Text request. */
-    ISCSIOP_TEXT_REQ = 0x04000000,
-    /** SCSI Data-Out. */
-    ISCSIOP_SCSI_DATA_OUT = 0x05000000,
-    /** Logout request. */
-    ISCSIOP_LOGOUT_REQ = 0x06000000,
-    /** SNACK request. */
-    ISCSIOP_SNACK_REQ = 0x10000000,
-
-    /** NOP-In. */
-    ISCSIOP_NOP_IN = 0x20000000,
-    /** SCSI response. */
-    ISCSIOP_SCSI_RES = 0x21000000,
-    /** SCSI Task Management response. */
-    ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
-    /** Login response. */
-    ISCSIOP_LOGIN_RES = 0x23000000,
-    /** Text response. */
-    ISCSIOP_TEXT_RES = 0x24000000,
-    /** SCSI Data-In. */
-    ISCSIOP_SCSI_DATA_IN = 0x25000000,
-    /** Logout response. */
-    ISCSIOP_LOGOUT_RES = 0x26000000,
-    /** Ready To Transfer (R2T). */
-    ISCSIOP_R2T = 0x31000000,
-    /** Asynchronous message. */
-    ISCSIOP_ASYN_MSG = 0x32000000,
-    /** Reject. */
-    ISCSIOP_REJECT = 0x3f000000
-} ISCSIOPCODE;
-
-/** Mask for extracting the iSCSI opcode out of the first header word. */
-#define ISCSIOP_MASK 0x3f000000
-
-
-/** ISCSI BHS word 0: Request should be processed immediately. */
-#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
-
-/** ISCSI BHS word 0: This is the final PDU for this request/response. */
-#define ISCSI_FINAL_BIT 0x00800000
-/** ISCSI BHS word 0: Mask for extracting the CSG. */
-#define ISCSI_CSG_MASK 0x000c0000
-/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
-#define ISCSI_CSG_SHIFT 18
-/** ISCSI BHS word 0: Mask for extracting the NSG. */
-#define ISCSI_NSG_MASK 0x00030000
-/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
-#define ISCSI_NSG_SHIFT 16
-
-/** ISCSI BHS word 0: task attribute untagged */
-#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
-/** ISCSI BHS word 0: task attribute simple */
-#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
-/** ISCSI BHS word 0: task attribute ordered */
-#define ISCSI_TASK_ATTR_ORDERED 0x00020000
-/** ISCSI BHS word 0: task attribute head of queue */
-#define ISCSI_TASK_ATTR_HOQ 0x00030000
-/** ISCSI BHS word 0: task attribute ACA */
-#define ISCSI_TASK_ATTR_ACA 0x00040000
-
-/** ISCSI BHS word 0: transit to next login phase. */
-#define ISCSI_TRANSIT_BIT 0x00800000
-/** ISCSI BHS word 0: continue with login negotiation. */
-#define ISCSI_CONTINUE_BIT 0x00400000
-
-/** ISCSI BHS word 0: residual underflow. */
-#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
-/** ISCSI BHS word 0: residual overflow. */
-#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
-/** ISCSI BHS word 0: Bidirectional read residual underflow. */
-#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
-/** ISCSI BHS word 0: Bidirectional read residual overflow. */
-#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
-
-/** ISCSI BHS word 0: SCSI response mask. */
-#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
-/** ISCSI BHS word 0: SCSI status mask. */
-#define ISCSI_SCSI_STATUS_MASK 0x000000ff
-
-/** ISCSI BHS word 0: response includes status. */
-#define ISCSI_STATUS_BIT 0x00010000
-
-/** Maximum number of scatter/gather segments needed to send a PDU. */
-#define ISCSI_SG_SEGMENTS_MAX 4
-
-/** Number of entries in the command table. */
-#define ISCSI_CMD_WAITING_ENTRIES 32
-
-/**
- * iSCSI login status class. */
-typedef enum ISCSILOGINSTATUSCLASS
-{
-    /** Success. */
-    ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
-    /** Redirection. */
-    ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
-    /** Initiator error. */
-    ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
-    /** Target error. */
-    ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
-} ISCSILOGINSTATUSCLASS;
-
-
-/**
- * iSCSI connection state. */
-typedef enum ISCSISTATE
-{
-    /** Not having a connection/session at all. */
-    ISCSISTATE_FREE,
-    /** Currently trying to login. */
-    ISCSISTATE_IN_LOGIN,
-    /** Normal operation, corresponds roughly to the Full Feature Phase. */
-    ISCSISTATE_NORMAL,
-    /** Currently trying to logout. */
-    ISCSISTATE_IN_LOGOUT
-} ISCSISTATE;
-
-/**
- * iSCSI PDU send flags (and maybe more in the future). */
-typedef enum ISCSIPDUFLAGS
-{
-    /** No special flags */
-    ISCSIPDU_DEFAULT = 0,
-    /** Do not attempt to re-attach to the target if the connection is lost */
-    ISCSIPDU_NO_REATTACH = RT_BIT(1)
-} ISCSIPDUFLAGS;
-
-
-/*******************************************************************************
-*   Structures and Typedefs                                                    *
-*******************************************************************************/
-
-/**
- * iSCSI login negotiation parameter
- */
-typedef struct ISCSIPARAMETER
-{
-    /** Name of the parameter. */
-    const char *pszParamName;
-    /** Value of the parameter. */
-    const char *pszParamValue;
-    /** Length of the binary parameter. 0=zero-terminated string. */
-    size_t cbParamValue;
-} ISCSIPARAMETER;
-
-
-/**
- * iSCSI Response PDU buffer (scatter).
- */
-typedef struct ISCSIRES
-{
-    /** Length of PDU segment. */
-    size_t cbSeg;
-    /** Pointer to PDU segment. */
-    void *pvSeg;
-} ISCSIRES;
-/** Pointer to an iSCSI Response PDU buffer. */
-typedef ISCSIRES *PISCSIRES;
-/** Pointer to a const iSCSI Response PDU buffer. */
-typedef ISCSIRES const *PCISCSIRES;
-
-
-/**
- * iSCSI Request PDU buffer (gather).
- */
-typedef struct ISCSIREQ
-{
-    /** Length of PDU segment in bytes. */
-    size_t cbSeg;
-    /** Pointer to PDU segment. */
-    const void *pcvSeg;
-} ISCSIREQ;
-/** Pointer to an iSCSI Request PDU buffer. */
-typedef ISCSIREQ *PISCSIREQ;
-/** Pointer to a const iSCSI Request PDU buffer. */
-typedef ISCSIREQ const *PCISCSIREQ;
-
-
-/**
- * SCSI transfer directions.
- */
-typedef enum SCSIXFER
-{
-    SCSIXFER_NONE = 0,
-    SCSIXFER_TO_TARGET,
-    SCSIXFER_FROM_TARGET,
-    SCSIXFER_TO_FROM_TARGET
-} SCSIXFER, *PSCSIXFER;
-
-/** Forward declaration. */
-typedef struct ISCSIIMAGE *PISCSIIMAGE;
-
-/**
- * SCSI request structure.
- */
-typedef struct SCSIREQ
-{
-    /** Transfer direction. */
-    SCSIXFER        enmXfer;
-    /** Length of command block. */
-    size_t          cbCDB;
-    /** Length of Initiator2Target data buffer. */
-    size_t          cbI2TData;
-    /** Length of Target2Initiator data buffer. */
-    size_t          cbT2IData;
-    /** Length of sense buffer
-     * This contains the number of sense bytes received upon completion. */
-    size_t          cbSense;
-    /** Completion status of the command. */
-    uint8_t         status;
-    /** Pointer to command block. */
-    void           *pvCDB;
-    /** Pointer to sense buffer. */
-    void           *pvSense;
-    /** Pointer to the Initiator2Target S/G list. */
-    PRTSGSEG        paI2TSegs;
-    /** Number of entries in the I2T S/G list. */
-    unsigned        cI2TSegs;
-    /** Pointer to the Target2Initiator S/G list. */
-    PRTSGSEG        paT2ISegs;
-    /** Number of entries in the T2I S/G list. */
-    unsigned        cT2ISegs;
-    /** S/G buffer for the target to initiator bits. */
-    RTSGBUF         SgBufT2I;
-} SCSIREQ, *PSCSIREQ;
-
-/**
- * Async request structure holding all necessary data for
- * request processing.
- */
-typedef struct SCSIREQASYNC
-{
-    /** I/O context associated with this request. */
-    PVDIOCTX        pIoCtx;
-    /** Pointer to the SCSI request structure. */
-    PSCSIREQ        pScsiReq;
-    /** The CDB. */
-    uint8_t         abCDB[16];
-    /** The sense buffer. */
-    uint8_t         abSense[96];
-    /** Status code to return if we got sense data. */
-    int             rcSense;
-    /** Number of retries if the command completes with sense
-     * data before we return with an error.
-     */
-    unsigned        cSenseRetries;
-    /** The number of entries in the I2T S/G list. */
-    unsigned        cI2TSegs;
-    /** The number of entries in the T2I S/G list. */
-    unsigned        cT2ISegs;
-    /** The S/G list - variable in size.
-     * This array holds both the I2T and T2I segments.
-     * The I2T segments are first and the T2I are second.
-     */
-    RTSGSEG         aSegs[1];
-} SCSIREQASYNC, *PSCSIREQASYNC;
-
-typedef enum ISCSICMDTYPE
-{
-    /** Process a SCSI request. */
-    ISCSICMDTYPE_REQ = 0,
-    /** Call a function in the I/O thread. */
-    ISCSICMDTYPE_EXEC,
-    /** Usual 32bit hack. */
-    ISCSICMDTYPE_32BIT_HACK = 0x7fffffff
-} ISCSICMDTYPE;
-
-
-/** The command completion function. */
-typedef DECLCALLBACK(void) FNISCSICMDCOMPLETED(PISCSIIMAGE pImage, int rcReq, void *pvUser);
-/** Pointer to a command completion function. */
-typedef FNISCSICMDCOMPLETED *PFNISCSICMDCOMPLETED;
-
-/** The command execution function. */
-typedef DECLCALLBACK(int) FNISCSIEXEC(void *pvUser);
-/** Pointer to a command execution function. */
-typedef FNISCSIEXEC *PFNISCSIEXEC;
-
-/**
- * Structure used to complete a synchronous request.
- */
-typedef struct ISCSICMDSYNC
-{
-    /** Event semaphore to wakeup the waiting thread. */
-    RTSEMEVENT EventSem;
-    /** Status code of the command. */
-    int        rcCmd;
-} ISCSICMDSYNC, *PISCSICMDSYNC;
-
-/**
- * iSCSI command.
- * Used to forward requests to the I/O thread
- * if existing.
- */
-typedef struct ISCSICMD
-{
-    /** Next one in the list. */
-    struct ISCSICMD      *pNext;
-    /** Assigned ITT. */
-    uint32_t              Itt;
-    /** Completion callback. */
-    PFNISCSICMDCOMPLETED  pfnComplete;
-    /** Opaque user data. */
-    void                 *pvUser;
-    /** Command to execute. */
-    ISCSICMDTYPE          enmCmdType;
-    /** Command type dependent data. */
-    union
-    {
-        /** Process a SCSI request. */
-        struct
-        {
-            /** The SCSI request to process. */
-            PSCSIREQ              pScsiReq;
-        } ScsiReq;
-        /** Call a function in the I/O thread. */
-        struct
-        {
-            /** The method to execute. */
-            PFNISCSIEXEC          pfnExec;
-            /** User data. */
-            void                 *pvUser;
-        } Exec;
-    } CmdType;
-} ISCSICMD, *PISCSICMD;
-
-/**
- *  Send iSCSI PDU.
- *  Contains all necessary data to send a PDU.
- */
-typedef struct ISCSIPDUTX
-{
-    /** Pointer to the next PDu to send. */
-    struct ISCSIPDUTX *pNext;
-    /** The BHS. */
-    uint32_t    aBHS[12];
-    /** Assigned CmdSN for this PDU. */
-    uint32_t    CmdSN;
-    /** The S/G buffer used for sending. */
-    RTSGBUF     SgBuf;
-    /** Number of bytes to send until the PDU completed. */
-    size_t      cbSgLeft;
-    /** The iSCSI command this PDU belongs to. */
-    PISCSICMD   pIScsiCmd;
-    /** Number of segments in the request segments array. */
-    unsigned    cISCSIReq;
-    /** The request segments - variable in size. */
-    RTSGSEG     aISCSIReq[1];
-} ISCSIPDUTX, *PISCSIPDUTX;
-
-/**
- * Block driver instance data.
- */
-typedef struct ISCSIIMAGE
-{
-    /** Pointer to the filename (location). Not really used. */
-    const char          *pszFilename;
-    /** Pointer to the initiator name. */
-    char                *pszInitiatorName;
-    /** Pointer to the target name. */
-    char                *pszTargetName;
-    /** Pointer to the target address. */
-    char                *pszTargetAddress;
-    /** Pointer to the user name for authenticating the Initiator. */
-    char                *pszInitiatorUsername;
-    /** Pointer to the secret for authenticating the Initiator. */
-    uint8_t             *pbInitiatorSecret;
-    /** Length of the secret for authenticating the Initiator. */
-    size_t              cbInitiatorSecret;
-    /** Pointer to the user name for authenticating the Target. */
-    char                *pszTargetUsername;
-    /** Pointer to the secret for authenticating the Initiator. */
-    uint8_t             *pbTargetSecret;
-    /** Length of the secret for authenticating the Initiator. */
-    size_t              cbTargetSecret;
-    /** Limit for iSCSI writes, essentially limiting the amount of data
-     * written in a single write. This is negotiated with the target, so
-     * the actual size might be smaller. */
-    uint32_t            cbWriteSplit;
-    /** Initiator session identifier. */
-    uint64_t            ISID;
-    /** SCSI Logical Unit Number. */
-    uint64_t            LUN;
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE        pVDIfsDisk;
-    /** Error interface. */
-    PVDINTERFACE        pInterfaceError;
-    /** Error interface callback table. */
-    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE        pVDIfsImage;
-    /** Config interface. */
-    PVDINTERFACE        pInterfaceConfig;
-    /** Config interface callback table. */
-    PVDINTERFACECONFIG  pInterfaceConfigCallbacks;
-    /** I/O interface. */
-    PVDINTERFACE        pInterfaceIo;
-    /** I/O interface callback table. */
-    PVDINTERFACEIOINT   pInterfaceIoCallbacks;
-    /** TCP network stack interface. */
-    PVDINTERFACE        pInterfaceNet;
-    /** TCP network stack interface callback table. */
-    PVDINTERFACETCPNET  pInterfaceNetCallbacks;
-    /** Image open flags. */
-    unsigned            uOpenFlags;
-    /** Number of re-login retries when a connection fails. */
-    uint32_t            cISCSIRetries;
-    /** Sector size on volume. */
-    uint32_t            cbSector;
-    /** Size of volume in sectors. */
-    uint64_t            cVolume;
-    /** Total volume size in bytes. Easier than multiplying the above values all the time. */
-    uint64_t            cbSize;
-
-    /** Negotiated maximum data length when sending to target. */
-    uint32_t            cbSendDataLength;
-    /** Negotiated maximum data length when receiving from target. */
-    uint32_t            cbRecvDataLength;
-
-    /** Current state of the connection/session. */
-    ISCSISTATE          state;
-    /** Flag whether the first Login Response PDU has been seen. */
-    bool                FirstRecvPDU;
-    /** Initiator Task Tag of the last iSCSI request PDU. */
-    uint32_t            ITT;
-    /** Sequence number of the last command. */
-    uint32_t            CmdSN;
-    /** Sequence number of the next command expected by the target. */
-    uint32_t            ExpCmdSN;
-    /** Maximum sequence number accepted by the target (determines size of window). */
-    uint32_t            MaxCmdSN;
-    /** Expected sequence number of next status. */
-    uint32_t            ExpStatSN;
-    /** Currently active request. */
-    PISCSIREQ           paCurrReq;
-    /** Segment number of currently active request. */
-    uint32_t            cnCurrReq;
-    /** Pointer to receive PDU buffer. (Freed by RT) */
-    void                *pvRecvPDUBuf;
-    /** Length of receive PDU buffer. */
-    size_t              cbRecvPDUBuf;
-    /** Mutex protecting against concurrent use from several threads. */
-    RTSEMMUTEX          Mutex;
-
-    /** Pointer to the target hostname. */
-    char                *pszHostname;
-    /** Pointer to the target hostname. */
-    uint32_t            uPort;
-    /** Socket handle of the TCP connection. */
-    VDSOCKET            Socket;
-    /** Timeout for read operations on the TCP connection (in milliseconds). */
-    uint32_t            uReadTimeout;
-    /** Flag whether to automatically generate the initiator name. */
-    bool                fAutomaticInitiatorName;
-    /** Flag whether to use the host IP stack or DevINIP. */
-    bool                fHostIP;
-
-    /** Head of request queue */
-    PISCSICMD           pScsiReqQueue;
-    /** Mutex protecting the request queue from concurrent access. */
-    RTSEMMUTEX          MutexReqQueue;
-    /** I/O thread. */
-    RTTHREAD            hThreadIo;
-    /** Flag whether the thread should be still running. */
-    volatile bool       fRunning;
-    /* Flag whether the target supports command queuing. */
-    bool                fCmdQueuingSupported;
-    /** Flag whether extended select is supported. */
-    bool                fExtendedSelectSupported;
-    /** Padding used for aligning the PDUs. */
-    uint8_t             aPadding[4];
-    /** Socket events to poll for. */
-    uint32_t            fPollEvents;
-    /** Number of bytes to read to complete the current PDU. */
-    size_t              cbRecvPDUResidual;
-    /** Current position in the PDU buffer. */
-    uint8_t             *pbRecvPDUBufCur;
-    /** Flag whether we are currently reading the BHS. */
-    bool                fRecvPDUBHS;
-    /** List of PDUs waiting to get transmitted. */
-    PISCSIPDUTX         pIScsiPDUTxHead;
-    /** Tail of PDUs waiting to get transmitted. */
-    PISCSIPDUTX         pIScsiPDUTxTail;
-    /** PDU we are currently transmitting. */
-    PISCSIPDUTX         pIScsiPDUTxCur;
-    /** Number of commands waiting for an answer from the target.
-     * Used for timeout handling for poll.
-     */
-    unsigned            cCmdsWaiting;
-    /** Table of commands waiting for a response from the target. */
-    PISCSICMD           aCmdsWaiting[ISCSI_CMD_WAITING_ENTRIES];
-} ISCSIIMAGE;
-
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** Default initiator basename. */
-static const char *s_iscsiDefaultInitiatorBasename = "iqn.2009-08.com.sun.virtualbox.initiator";
-
-/** Default LUN. */
-static const char *s_iscsiConfigDefaultLUN = "0";
-
-/** Default timeout, 10 seconds. */
-static const char *s_iscsiConfigDefaultTimeout = "10000";
-
-/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
-static const char *s_iscsiConfigDefaultWriteSplit = "262144";
-
-/** Default host IP stack. */
-static const char *s_iscsiConfigDefaultHostIPStack = "1";
-
-/** Description of all accepted config parameters. */
-static const VDCONFIGINFO s_iscsiConfigInfo[] =
-{
-    { "TargetName",         NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
-    /* LUN is defined of string type to handle the "enc" prefix. */
-    { "LUN",                s_iscsiConfigDefaultLUN,            VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
-    { "TargetAddress",      NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
-    { "InitiatorName",      NULL,                               VDCFGVALUETYPE_STRING,  0 },
-    { "InitiatorUsername",  NULL,                               VDCFGVALUETYPE_STRING,  0 },
-    { "InitiatorSecret",    NULL,                               VDCFGVALUETYPE_BYTES,   0 },
-    { "TargetUsername",     NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_EXPERT },
-    { "TargetSecret",       NULL,                               VDCFGVALUETYPE_BYTES,   VD_CFGKEY_EXPERT },
-    { "WriteSplit",         s_iscsiConfigDefaultWriteSplit,     VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
-    { "Timeout",            s_iscsiConfigDefaultTimeout,        VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
-    { "HostIPStack",        s_iscsiConfigDefaultHostIPStack,    VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
-    { NULL,                 NULL,                               VDCFGVALUETYPE_INTEGER, 0 }
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-
-/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
-static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
-static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
-static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
-static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
-static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
-static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
-static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
-static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd);
-static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
-static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd);
-static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
-static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
-static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
-static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
-
-/* Serial number arithmetic comparison. */
-static bool serial_number_less(uint32_t sn1, uint32_t sn2);
-static bool serial_number_greater(uint32_t sn1, uint32_t sn2);
-
-/* CHAP-MD5 functions. */
-#ifdef IMPLEMENT_TARGET_AUTH
-static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
-#endif
-static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
-                                      const uint8_t *pbSecret, size_t cbSecret);
-
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) iscsiError(PISCSIIMAGE pImage, int rc, RT_SRC_POS_DECL,
-                           const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-
-#ifdef LOG_ENABLED
-    va_start(va, pszFormat);
-    Log(("iscsiError(%d/%s): %N\n", iLine, pszFunction, pszFormat, &va));
-    va_end(va);
-#endif
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) iscsiMessage(PISCSIIMAGE pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-DECLINLINE(bool) iscsiIsClientConnected(PISCSIIMAGE pImage)
-{
-    return    pImage->Socket != NIL_VDSOCKET
-           && pImage->pInterfaceNetCallbacks->pfnIsClientConnected(pImage->Socket);
-}
-
-/**
- * Calculates the hash for the given ITT used
- * to look up the command in the table.
- */
-DECLINLINE(uint32_t) iscsiIttHash(uint32_t Itt)
-{
-    return Itt % ISCSI_CMD_WAITING_ENTRIES;
-}
-
-static PISCSICMD iscsiCmdGetFromItt(PISCSIIMAGE pImage, uint32_t Itt)
-{
-    PISCSICMD pIScsiCmd = NULL;
-
-    pIScsiCmd = pImage->aCmdsWaiting[iscsiIttHash(Itt)];
-
-    while (   pIScsiCmd
-           && pIScsiCmd->Itt != Itt)
-        pIScsiCmd = pIScsiCmd->pNext;
-
-    return pIScsiCmd;
-}
-
-static void iscsiCmdInsert(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
-{
-    PISCSICMD pIScsiCmdOld;
-    uint32_t idx = iscsiIttHash(pIScsiCmd->Itt);
-
-    Assert(!pIScsiCmd->pNext);
-
-    pIScsiCmdOld = pImage->aCmdsWaiting[idx];
-    pIScsiCmd->pNext = pIScsiCmdOld;
-    pImage->aCmdsWaiting[idx] = pIScsiCmd;
-    pImage->cCmdsWaiting++;
-}
-
-static PISCSICMD iscsiCmdRemove(PISCSIIMAGE pImage, uint32_t Itt)
-{
-    PISCSICMD pIScsiCmd = NULL;
-    PISCSICMD pIScsiCmdPrev = NULL;
-    uint32_t idx = iscsiIttHash(Itt);
-
-    pIScsiCmd = pImage->aCmdsWaiting[idx];
-
-    while (   pIScsiCmd
-           && pIScsiCmd->Itt != Itt)
-    {
-        pIScsiCmdPrev = pIScsiCmd;
-        pIScsiCmd = pIScsiCmd->pNext;
-    }
-
-    if (pIScsiCmd)
-    {
-        if (pIScsiCmdPrev)
-        {
-            Assert(!pIScsiCmd->pNext || VALID_PTR(pIScsiCmd->pNext));
-            pIScsiCmdPrev->pNext = pIScsiCmd->pNext;
-        }
-        else
-        {
-            pImage->aCmdsWaiting[idx] = pIScsiCmd->pNext;
-            Assert(!pImage->aCmdsWaiting[idx] || VALID_PTR(pImage->aCmdsWaiting[idx]));
-        }
-        pImage->cCmdsWaiting--;
-    }
-
-    return pIScsiCmd;
-}
-
-/**
- * Removes all commands from the table and returns the
- * list head
- *
- * @returns Pointer to the head of teh command list.
- * @param   pImage    iSCSI connection to use.
- */
-static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
-{
-    PISCSICMD pIScsiCmdHead = NULL;
-
-    for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
-    {
-        PISCSICMD pHead;
-        PISCSICMD pTail;
-
-        pHead = pImage->aCmdsWaiting[idx];
-        pImage->aCmdsWaiting[idx] = NULL;
-
-        if (pHead)
-        {
-            /* Get the tail. */
-            pTail = pHead;
-            while (pTail->pNext)
-                pTail = pTail->pNext;
-
-            /* Concatenate. */
-            pTail->pNext = pIScsiCmdHead;
-            pIScsiCmdHead = pHead;
-        }
-    }
-    pImage->cCmdsWaiting = 0;
-
-    return pIScsiCmdHead;
-}
-
-static int iscsiTransportConnect(PISCSIIMAGE pImage)
-{
-    int rc;
-    if (!pImage->pszHostname)
-        return VERR_NET_DEST_ADDRESS_REQUIRED;
-
-    rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->Socket, pImage->pszHostname, pImage->uPort);
-    if (RT_FAILURE(rc))
-    {
-        if (   rc == VERR_NET_CONNECTION_REFUSED
-            || rc == VERR_NET_CONNECTION_RESET
-            || rc == VERR_NET_UNREACHABLE
-            || rc == VERR_NET_HOST_UNREACHABLE
-            || rc == VERR_NET_CONNECTION_TIMED_OUT)
-        {
-            /* Standardize return value for no connection. */
-            rc = VERR_NET_CONNECTION_REFUSED;
-        }
-        return rc;
-    }
-
-    /* Disable Nagle algorithm, we want things to be sent immediately. */
-    pImage->pInterfaceNetCallbacks->pfnSetSendCoalescing(pImage->Socket, false);
-
-    /* Make initiator name and ISID unique on this host. */
-    RTNETADDR LocalAddr;
-    rc = pImage->pInterfaceNetCallbacks->pfnGetLocalAddress(pImage->Socket,
-                                                            &LocalAddr);
-    if (RT_FAILURE(rc))
-        return rc;
-    if (   LocalAddr.uPort == RTNETADDR_PORT_NA
-        || LocalAddr.uPort > 65535)
-        return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
-    pImage->ISID &= ~65535ULL;
-    pImage->ISID |= LocalAddr.uPort;
-    /* Eliminate the port so that it isn't included below. */
-    LocalAddr.uPort = RTNETADDR_PORT_NA;
-    if (pImage->fAutomaticInitiatorName)
-    {
-        if (pImage->pszInitiatorName)
-            RTStrFree(pImage->pszInitiatorName);
-        RTStrAPrintf(&pImage->pszInitiatorName, "%s:01:%RTnaddr",
-                     s_iscsiDefaultInitiatorBasename, &LocalAddr);
-        if (!pImage->pszInitiatorName)
-            return VERR_NO_MEMORY;
-    }
-    return VINF_SUCCESS;
-}
-
-
-static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
-{
-    int rc = VINF_SUCCESS;
-    unsigned int i = 0;
-    size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
-    char *pDst;
-
-    LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
-    if (!iscsiIsClientConnected(pImage))
-    {
-        /* Reconnecting makes no sense in this case, as there will be nothing
-         * to receive. We would just run into a timeout. */
-        rc = VERR_BROKEN_PIPE;
-    }
-
-    if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= ISCSI_BHS_SIZE)
-    {
-        cbToRead = 0;
-        residual = ISCSI_BHS_SIZE;  /* Do not read more than the BHS length before the true PDU length is known. */
-        cbSegActual = residual;
-        pDst = (char *)paResponse[i].pvSeg;
-        uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
-        do
-        {
-            int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
-            if (cMilliesRemaining <= 0)
-            {
-                rc = VERR_TIMEOUT;
-                break;
-            }
-            Assert(cMilliesRemaining < 1000000);
-            rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
-                                                              cMilliesRemaining);
-            if (RT_FAILURE(rc))
-                break;
-            rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
-                                                         pDst, residual,
-                                                         &cbActuallyRead);
-            if (RT_FAILURE(rc))
-                break;
-            if (cbActuallyRead == 0)
-            {
-                /* The other end has closed the connection. */
-                pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
-                pImage->state = ISCSISTATE_FREE;
-                rc = VERR_NET_CONNECTION_RESET;
-                break;
-            }
-            if (cbToRead == 0)
-            {
-                /* Currently reading the BHS. */
-                residual -= cbActuallyRead;
-                pDst += cbActuallyRead;
-                if (residual <= 40)
-                {
-                    /* Enough data read to figure out the actual PDU size. */
-                    uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
-                    cbAHSLength = (word1 & 0xff000000) >> 24;
-                    cbAHSLength = ((cbAHSLength - 1) | 3) + 1;      /* Add padding. */
-                    cbDataLength = word1 & 0x00ffffff;
-                    cbDataLength = ((cbDataLength - 1) | 3) + 1;    /* Add padding. */
-                    cbToRead = residual + cbAHSLength + cbDataLength;
-                    residual += paResponse[0].cbSeg - ISCSI_BHS_SIZE;
-                    if (residual > cbToRead)
-                        residual = cbToRead;
-                    cbSegActual = ISCSI_BHS_SIZE + cbAHSLength + cbDataLength;
-                    /* Check whether we are already done with this PDU (no payload). */
-                    if (cbToRead == 0)
-                        break;
-                }
-            }
-            else
-            {
-                cbToRead -= cbActuallyRead;
-                if (cbToRead == 0)
-                    break;
-                pDst += cbActuallyRead;
-                residual -= cbActuallyRead;
-            }
-            if (residual == 0)
-            {
-                i++;
-                if (i >= cnResponse)
-                {
-                    /* No space left in receive buffers. */
-                    rc = VERR_BUFFER_OVERFLOW;
-                    break;
-                }
-                pDst = (char *)paResponse[i].pvSeg;
-                residual = paResponse[i].cbSeg;
-                if (residual > cbToRead)
-                    residual = cbToRead;
-                cbSegActual = residual;
-            }
-            LogFlowFunc(("cbToRead=%u residual=%u cbSegActual=%u cbActuallRead=%u\n",
-                         cbToRead, residual, cbSegActual, cbActuallyRead));
-        } while (true);
-    }
-    else
-    {
-        if (RT_SUCCESS(rc))
-            rc = VERR_BUFFER_OVERFLOW;
-    }
-    if (RT_SUCCESS(rc))
-    {
-        paResponse[i].cbSeg = cbSegActual;
-        for (i++; i < cnResponse; i++)
-            paResponse[i].cbSeg = 0;
-    }
-
-    if (RT_UNLIKELY(    RT_FAILURE(rc)
-                    && (   rc == VERR_NET_CONNECTION_RESET
-                        || rc == VERR_NET_CONNECTION_ABORTED
-                        || rc == VERR_NET_CONNECTION_RESET_BY_PEER
-                        || rc == VERR_NET_CONNECTION_REFUSED
-                        || rc == VERR_BROKEN_PIPE)))
-    {
-        /* Standardize return value for broken connection. */
-        rc = VERR_BROKEN_PIPE;
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
-{
-    int rc = VINF_SUCCESS;
-    uint32_t pad = 0;
-    unsigned int i;
-
-    LogFlowFunc(("cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
-    if (!iscsiIsClientConnected(pImage))
-    {
-        /* Attempt to reconnect if the connection was previously broken. */
-        rc = iscsiTransportConnect(pImage);
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        /* Construct scatter/gather buffer for entire request, worst case
-         * needs twice as many entries to allow for padding. */
-        unsigned cBuf = 0;
-        for (i = 0; i < cnRequest; i++)
-        {
-            cBuf++;
-            if (paRequest[i].cbSeg & 3)
-                cBuf++;
-        }
-        Assert(cBuf < ISCSI_SG_SEGMENTS_MAX);
-        RTSGBUF buf;
-        RTSGSEG aSeg[ISCSI_SG_SEGMENTS_MAX];
-        static char aPad[4] = { 0, 0, 0, 0 };
-        RTSgBufInit(&buf, &aSeg[0], cBuf);
-        unsigned iBuf = 0;
-        for (i = 0; i < cnRequest; i++)
-        {
-            /* Actual data chunk. */
-            aSeg[iBuf].pvSeg = (void *)paRequest[i].pcvSeg;
-            aSeg[iBuf].cbSeg = paRequest[i].cbSeg;
-            iBuf++;
-            /* Insert proper padding before the next chunk. */
-            if (paRequest[i].cbSeg & 3)
-            {
-                aSeg[iBuf].pvSeg = &aPad[0];
-                aSeg[iBuf].cbSeg = 4 - (paRequest[i].cbSeg & 3);
-                iBuf++;
-            }
-        }
-        /* Send out the request, the socket is set to send data immediately,
-         * avoiding unnecessary delays. */
-        rc = pImage->pInterfaceNetCallbacks->pfnSgWrite(pImage->Socket, &buf);
-
-    }
-
-    if (RT_UNLIKELY(    RT_FAILURE(rc)
-                    && (   rc == VERR_NET_CONNECTION_RESET
-                        || rc == VERR_NET_CONNECTION_ABORTED
-                        || rc == VERR_NET_CONNECTION_RESET_BY_PEER
-                        || rc == VERR_NET_CONNECTION_REFUSED
-                        || rc == VERR_BROKEN_PIPE)))
-    {
-        /* Standardize return value for broken connection. */
-        rc = VERR_BROKEN_PIPE;
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-static int iscsiTransportOpen(PISCSIIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-    size_t cbHostname = 0; /* shut up gcc */
-    const char *pcszPort = NULL; /* shut up gcc */
-    char *pszPortEnd;
-    uint16_t uPort;
-
-    /* Clean up previous connection data. */
-    if (iscsiIsClientConnected(pImage))
-    {
-        pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
-    }
-    if (pImage->pszHostname)
-    {
-        RTMemFree(pImage->pszHostname);
-        pImage->pszHostname = NULL;
-        pImage->uPort = 0;
-    }
-
-    /* Locate the port number via the colon separating the hostname from the port. */
-    if (*pImage->pszTargetAddress)
-    {
-        if (*pImage->pszTargetAddress != '[')
-        {
-            /* Normal hostname or IPv4 dotted decimal. */
-            pcszPort = strchr(pImage->pszTargetAddress, ':');
-            if (pcszPort != NULL)
-            {
-                cbHostname = pcszPort - pImage->pszTargetAddress;
-                pcszPort++;
-            }
-            else
-                cbHostname = strlen(pImage->pszTargetAddress);
-        }
-        else
-        {
-            /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
-            pcszPort = strchr(pImage->pszTargetAddress, ']');
-            if (pcszPort != NULL)
-            {
-                pcszPort++;
-                cbHostname = pcszPort - pImage->pszTargetAddress;
-                if (*pcszPort == '\0')
-                    pcszPort = NULL;
-                else if (*pcszPort != ':')
-                    rc = VERR_PARSE_ERROR;
-                else
-                    pcszPort++;
-            }
-            else
-                rc = VERR_PARSE_ERROR;
-        }
-    }
-    else
-        rc = VERR_PARSE_ERROR;
-
-    /* Now split address into hostname and port. */
-    if (RT_SUCCESS(rc))
-    {
-        pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
-        if (!pImage->pszHostname)
-            rc = VERR_NO_MEMORY;
-        else
-        {
-            memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
-            pImage->pszHostname[cbHostname] = '\0';
-            if (pcszPort != NULL)
-            {
-                rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
-                /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
-                if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
-                {
-                    pImage->uPort = uPort;
-                }
-                else
-                {
-                    rc = VERR_PARSE_ERROR;
-                }
-            }
-            else
-                pImage->uPort = ISCSI_DEFAULT_PORT;
-        }
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        if (!iscsiIsClientConnected(pImage))
-            rc = iscsiTransportConnect(pImage);
-    }
-    else
-    {
-        if (pImage->pszHostname)
-        {
-            RTMemFree(pImage->pszHostname);
-            pImage->pszHostname = NULL;
-        }
-        pImage->uPort = 0;
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-static int iscsiTransportClose(PISCSIIMAGE pImage)
-{
-    int rc;
-
-    LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
-    if (iscsiIsClientConnected(pImage))
-    {
-        rc = pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
-    }
-    else
-        rc = VINF_SUCCESS;
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * Attach to an iSCSI target. Performs all operations necessary to enter
- * Full Feature Phase.
- *
- * @returns VBox status.
- * @param   pImage      The iSCSI connection state to be used.
- */
-static int iscsiAttach(void *pvUser)
-{
-    int rc;
-    uint32_t itt;
-    uint32_t csg, nsg, substate;
-    uint64_t isid_tsih;
-    uint8_t bBuf[4096];         /* Should be large enough even for large authentication values. */
-    size_t cbBuf;
-    bool transit;
-    uint8_t pbChallenge[1024];  /* RFC3720 specifies this as maximum. */
-    size_t cbChallenge = 0;     /* shut up gcc */
-    uint8_t bChapIdx;
-    uint8_t aResponse[RTMD5HASHSIZE];
-    uint32_t cnISCSIReq;
-    ISCSIREQ aISCSIReq[4];
-    uint32_t aReqBHS[12];
-    uint32_t cnISCSIRes;
-    ISCSIRES aISCSIRes[2];
-    uint32_t aResBHS[12];
-    char *pszNext;
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
-
-    bool fParameterNeg = true;;
-    pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
-    pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
-    char szMaxDataLength[16];
-    RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
-    ISCSIPARAMETER aParameterNeg[] =
-    {
-        { "HeaderDigest", "None", 0 },
-        { "DataDigest", "None", 0 },
-        { "MaxConnections", "1", 0 },
-        { "InitialR2T", "No", 0 },
-        { "ImmediateData", "Yes", 0 },
-        { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
-        { "MaxBurstLength", szMaxDataLength, 0 },
-        { "FirstBurstLength", szMaxDataLength, 0 },
-        { "DefaultTime2Wait", "0", 0 },
-        { "DefaultTime2Retain", "60", 0 },
-        { "DataPDUInOrder", "Yes", 0 },
-        { "DataSequenceInOrder", "Yes", 0 },
-        { "ErrorRecoveryLevel", "0", 0 },
-        { "MaxOutstandingR2T", "1", 0 }
-    };
-
-    LogFlowFunc(("entering\n"));
-
-    Assert(pImage->state == ISCSISTATE_FREE);
-
-    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
-
-    /* Make 100% sure the connection isn't reused for a new login. */
-    iscsiTransportClose(pImage);
-
-restart:
-    if (!iscsiIsClientConnected(pImage))
-    {
-        rc = iscsiTransportOpen(pImage);
-        if (RT_FAILURE(rc))
-            goto out;
-    }
-
-    pImage->state = ISCSISTATE_IN_LOGIN;
-    pImage->ITT = 1;
-    pImage->FirstRecvPDU = true;
-    pImage->CmdSN = 1;
-    pImage->ExpCmdSN = 0;
-    pImage->MaxCmdSN = 1;
-    pImage->ExpStatSN = 0;
-
-    /*
-     * Send login request to target.
-     */
-    itt = iscsiNewITT(pImage);
-    csg = 0;
-    nsg = 0;
-    substate = 0;
-    isid_tsih = pImage->ISID << 16;  /* TSIH field currently always 0 */
-
-    do {
-        transit = false;
-        cbBuf = 0;
-        /* Handle all cases with a single switch statement. */
-        switch (csg << 8 | substate)
-        {
-            case 0x0000:    /* security negotiation, step 0: propose authentication. */
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
-                if (RT_FAILURE(rc))
-                    goto out;
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
-                if (RT_FAILURE(rc))
-                    goto out;
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
-                if (RT_FAILURE(rc))
-                    goto out;
-                if (pImage->pszInitiatorUsername == NULL)
-                {
-                    /* No authentication. Immediately switch to next phase. */
-                    rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
-                    if (RT_FAILURE(rc))
-                        goto out;
-                    nsg = 1;
-                    transit = true;
-                }
-                else
-                {
-                    rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
-                    if (RT_FAILURE(rc))
-                        goto out;
-                }
-                break;
-            case 0x0001:    /* security negotiation, step 1: propose CHAP_MD5 variant. */
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
-                if (RT_FAILURE(rc))
-                    goto out;
-                break;
-            case 0x0002:    /* security negotiation, step 2: send authentication info. */
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
-                if (RT_FAILURE(rc))
-                    goto out;
-                chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
-                                          pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
-                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
-                if (RT_FAILURE(rc))
-                    goto out;
-                nsg = 1;
-                transit = true;
-                break;
-            case 0x0100:    /* login operational negotiation, step 0: set parameters. */
-                if (fParameterNeg)
-                {
-                    for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
-                    {
-                        rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
-                                                  aParameterNeg[i].pszParamName,
-                                                  aParameterNeg[i].pszParamValue,
-                                                  aParameterNeg[i].cbParamValue);
-                        if (RT_FAILURE(rc))
-                            goto out;
-                    }
-                    fParameterNeg = false;
-                }
-
-                nsg = 3;
-                transit = true;
-                break;
-            case 0x0300:    /* full feature phase. */
-            default:
-                /* Should never come here. */
-                AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
-                break;
-        }
-
-        aReqBHS[0] = RT_H2N_U32(    ISCSI_IMMEDIATE_DELIVERY_BIT
-                                |   (csg << ISCSI_CSG_SHIFT)
-                                |   (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
-                                |   ISCSI_MY_VERSION            /* Minimum version. */
-                                |   (ISCSI_MY_VERSION << 8)     /* Maximum version. */
-                                |   ISCSIOP_LOGIN_REQ);     /* C=0 */
-        aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf);     /* TotalAHSLength=0 */
-        aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
-        aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
-        aReqBHS[4] = itt;
-        aReqBHS[5] = RT_H2N_U32(1 << 16);   /* CID=1,reserved */
-        aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-        aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-        aReqBHS[8] = 0;             /* reserved */
-        aReqBHS[9] = 0;             /* reserved */
-        aReqBHS[10] = 0;            /* reserved */
-        aReqBHS[11] = 0;            /* reserved */
-
-        cnISCSIReq = 0;
-        aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
-        aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
-        cnISCSIReq++;
-
-        aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
-        aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
-        cnISCSIReq++;
-
-        rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
-        if (RT_SUCCESS(rc))
-        {
-            ISCSIOPCODE cmd;
-            ISCSILOGINSTATUSCLASS loginStatusClass;
-
-            cnISCSIRes = 0;
-            aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
-            aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
-            cnISCSIRes++;
-            aISCSIRes[cnISCSIRes].pvSeg = bBuf;
-            aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
-            cnISCSIRes++;
-
-            rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
-            if (RT_FAILURE(rc))
-                break;
-            /** @todo collect partial login responses with Continue bit set. */
-            Assert(aISCSIRes[0].pvSeg == aResBHS);
-            Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
-            Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
-
-            cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
-            if (cmd == ISCSIOP_LOGIN_RES)
-            {
-                if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
-                {
-                    iscsiTransportClose(pImage);
-                    rc = VERR_PARSE_ERROR;
-                    break;  /* Give up immediately, as a RFC violation in version fields is very serious. */
-                }
-
-                loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
-                switch (loginStatusClass)
-                {
-                    case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
-                        uint32_t targetCSG;
-                        uint32_t targetNSG;
-                        bool targetTransit;
-
-                        if (pImage->FirstRecvPDU)
-                        {
-                            pImage->FirstRecvPDU = false;
-                            pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
-                        }
-
-                        targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
-                        targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
-                        targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
-
-                        /* Handle all cases with a single switch statement. */
-                        switch (csg << 8 | substate)
-                        {
-                            case 0x0000:    /* security negotiation, step 0: receive final authentication. */
-                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
-                                if (RT_FAILURE(rc))
-                                    break;
-
-                                const char *pcszAuthMethod;
-
-                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
-                                if (RT_FAILURE(rc))
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                if (strcmp(pcszAuthMethod, "None") == 0)
-                                {
-                                    /* Authentication offered, but none required.  Skip to operational parameters. */
-                                    csg = 1;
-                                    nsg = 1;
-                                    transit = true;
-                                    substate = 0;
-                                    break;
-                                }
-                                else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
-                                {
-                                    /* CHAP authentication required, continue with next substate. */
-                                    substate++;
-                                    break;
-                                }
-
-                                /* Unknown auth method or login response PDU headers incorrect. */
-                                rc = VERR_PARSE_ERROR;
-                                break;
-                            case 0x0001:    /* security negotiation, step 1: receive final CHAP variant and challenge. */
-                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
-                                if (RT_FAILURE(rc))
-                                    break;
-
-                                const char *pcszChapAuthMethod;
-                                const char *pcszChapIdxTarget;
-                                const char *pcszChapChallengeStr;
-
-                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
-                                if (RT_FAILURE(rc))
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                if (strcmp(pcszChapAuthMethod, "5") != 0)
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
-                                if (RT_FAILURE(rc))
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
-                                if ((rc > VINF_SUCCESS) || *pszNext != '\0')
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
-                                if (RT_FAILURE(rc))
-                                {
-                                    rc = VERR_PARSE_ERROR;
-                                    break;
-                                }
-                                cbChallenge = sizeof(pbChallenge);
-                                rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
-                                if (RT_FAILURE(rc))
-                                    break;
-                                substate++;
-                                transit = true;
-                                break;
-                            case 0x0002:    /* security negotiation, step 2: check authentication success. */
-                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
-                                if (RT_FAILURE(rc))
-                                    break;
-
-                                if (targetCSG == 0 && targetNSG == 1 && targetTransit)
-                                {
-                                    /* Target wants to continue in login operational state, authentication success. */
-                                    csg = 1;
-                                    nsg = 3;
-                                    substate = 0;
-                                    break;
-                                }
-                                rc = VERR_PARSE_ERROR;
-                                break;
-                            case 0x0100:    /* login operational negotiation, step 0: check results. */
-                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
-                                if (RT_FAILURE(rc))
-                                    break;
-
-                                if (targetCSG == 1 && targetNSG == 3 && targetTransit)
-                                {
-                                    /* Target wants to continue in full feature phase, login finished. */
-                                    csg = 3;
-                                    nsg = 3;
-                                    substate = 0;
-                                    break;
-                                }
-                                else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
-                                {
-                                    /* Target wants to negotiate certain parameters and
-                                     * stay in login operational negotiation. */
-                                    csg = 1;
-                                    nsg = 3;
-                                    substate = 0;
-                                }
-                                rc = VERR_PARSE_ERROR;
-                                break;
-                            case 0x0300:    /* full feature phase. */
-                            default:
-                                AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
-                                rc = VERR_PARSE_ERROR;
-                                break;
-                        }
-                        break;
-                    case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
-                        const char *pcszTargetRedir;
-
-                        /* Target has moved to some other location, as indicated in the TargetAddress key. */
-                        rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
-                        if (RT_FAILURE(rc))
-                        {
-                            rc = VERR_PARSE_ERROR;
-                            break;
-                        }
-                        if (pImage->pszTargetAddress)
-                            RTMemFree(pImage->pszTargetAddress);
-                        {
-                            size_t cb = strlen(pcszTargetRedir) + 1;
-                            pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
-                            if (!pImage->pszTargetAddress)
-                            {
-                                rc = VERR_NO_MEMORY;
-                                break;
-                            }
-                            memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
-                        }
-                        rc = iscsiTransportOpen(pImage);
-                        goto restart;
-                    case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
-                        iscsiTransportClose(pImage);
-                        rc = VERR_IO_GEN_FAILURE;
-                        goto out;
-                    case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
-                        iscsiTransportClose(pImage);
-                        rc = VINF_EOF;
-                        break;
-                    default:
-                        rc = VERR_PARSE_ERROR;
-                }
-
-                if (csg == 3)
-                {
-                    /*
-                     * Finished login, continuing with Full Feature Phase.
-                     */
-                    rc = VINF_SUCCESS;
-                    break;
-                }
-            }
-            else
-            {
-                AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
-            }
-        }
-        else
-            break;
-    } while (true);
-
-out:
-    if (RT_FAILURE(rc))
-    {
-        /*
-         * Close connection to target.
-         */
-        iscsiTransportClose(pImage);
-        pImage->state = ISCSISTATE_FREE;
-    }
-    else
-        pImage->state = ISCSISTATE_NORMAL;
-
-    RTSemMutexRelease(pImage->Mutex);
-
-    LogFlowFunc(("returning %Rrc\n", rc));
-    LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
-    return rc;
-}
-
-
-/**
- * Detach from an iSCSI target.
- *
- * @returns VBox status.
- * @param   pImage      The iSCSI connection state to be used.
- */
-static int iscsiDetach(void *pvUser)
-{
-    int rc;
-    uint32_t itt;
-    uint32_t cnISCSIReq = 0;
-    ISCSIREQ aISCSIReq[4];
-    uint32_t aReqBHS[12];
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
-
-    LogFlowFunc(("entering\n"));
-
-    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
-
-    if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
-    {
-        pImage->state = ISCSISTATE_IN_LOGOUT;
-
-        /*
-         * Send logout request to target.
-         */
-        itt = iscsiNewITT(pImage);
-        aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ);  /* I=0,F=1,Reason=close session */
-        aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
-        aReqBHS[2] = 0;             /* reserved */
-        aReqBHS[3] = 0;             /* reserved */
-        aReqBHS[4] = itt;
-        aReqBHS[5] = 0;             /* reserved */
-        aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-        aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-        aReqBHS[8] = 0;             /* reserved */
-        aReqBHS[9] = 0;             /* reserved */
-        aReqBHS[10] = 0;            /* reserved */
-        aReqBHS[11] = 0;            /* reserved */
-        pImage->CmdSN++;
-
-        aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
-        aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
-        cnISCSIReq++;
-
-        rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
-        if (RT_SUCCESS(rc))
-        {
-            /*
-             * Read logout response from target.
-             */
-            ISCSIRES aISCSIRes;
-            uint32_t aResBHS[12];
-
-            aISCSIRes.pvSeg = aResBHS;
-            aISCSIRes.cbSeg = sizeof(aResBHS);
-            rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
-            if (RT_SUCCESS(rc))
-            {
-                if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
-                    AssertMsgFailed(("iSCSI Logout response invalid\n"));
-            }
-            else
-                AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
-        }
-        else
-            AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
-    }
-
-    if (pImage->state != ISCSISTATE_FREE)
-    {
-        /*
-         * Close connection to target.
-         */
-        rc = iscsiTransportClose(pImage);
-        if (RT_FAILURE(rc))
-            AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
-    }
-
-    pImage->state = ISCSISTATE_FREE;
-
-    RTSemMutexRelease(pImage->Mutex);
-
-    LogFlowFunc(("leaving\n"));
-    LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Perform a command on an iSCSI target. Target must be already in
- * Full Feature Phase.
- *
- * @returns VBOX status.
- * @param   pImage      The iSCSI connection state to be used.
- * @param   pRequest    Command descriptor. Contains all information about
- *                      the command, its transfer directions and pointers
- *                      to the buffer(s) used for transferring data and
- *                      status information.
- */
-static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
-{
-    int rc;
-    uint32_t itt;
-    uint32_t cbData;
-    uint32_t cnISCSIReq = 0;
-    ISCSIREQ aISCSIReq[4];
-    uint32_t aReqBHS[12];
-
-    uint32_t *pDst = NULL;
-    size_t cbBufLength;
-    uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
-    uint32_t ExpDataSN = 0;
-    bool final = false;
-
-
-    LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
-
-    Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET);   /**< @todo not yet supported, would require AHS. */
-    Assert(pRequest->cbI2TData <= 0xffffff);    /* larger transfers would require R2T support. */
-    Assert(pRequest->cbCDB <= 16);      /* would cause buffer overrun below. */
-
-    /* If not in normal state, then the transport connection was dropped. Try
-     * to reestablish by logging in, the target might be responsive again. */
-    if (pImage->state == ISCSISTATE_FREE)
-        rc = iscsiAttach(pImage);
-
-    /* If still not in normal state, then the underlying transport connection
-     * cannot be established. Get out before bad things happen (and make
-     * sure the caller suspends the VM again). */
-    if (pImage->state != ISCSISTATE_NORMAL)
-    {
-        rc = VERR_NET_CONNECTION_REFUSED;
-        goto out;
-    }
-
-    /*
-     * Send SCSI command to target with all I2T data included.
-     */
-    cbData = 0;
-    if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
-        cbData = (uint32_t)pRequest->cbT2IData;
-    else
-        cbData = (uint32_t)pRequest->cbI2TData;
-
-    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
-
-    itt = iscsiNewITT(pImage);
-    memset(aReqBHS, 0, sizeof(aReqBHS));
-    aReqBHS[0] = RT_H2N_U32(    ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
-                            |   (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
-    aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
-    aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
-    aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
-    aReqBHS[4] = itt;
-    aReqBHS[5] = RT_H2N_U32(cbData);
-    aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-    aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-    memcpy(aReqBHS + 8, pRequest->pvCDB, pRequest->cbCDB);
-    pImage->CmdSN++;
-
-    aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
-    aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
-    cnISCSIReq++;
-
-    if (    pRequest->enmXfer == SCSIXFER_TO_TARGET
-        ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
-    {
-        Assert(pRequest->cI2TSegs == 1);
-        aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
-        aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg;  /* Padding done by transport. */
-        cnISCSIReq++;
-    }
-
-    rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
-    if (RT_FAILURE(rc))
-        goto out_release;
-
-    /* Place SCSI request in queue. */
-    pImage->paCurrReq = aISCSIReq;
-    pImage->cnCurrReq = cnISCSIReq;
-
-    /*
-     * Read SCSI response/data in PDUs from target.
-     */
-    if (    pRequest->enmXfer == SCSIXFER_FROM_TARGET
-        ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
-    {
-        Assert(pRequest->cT2ISegs == 1);
-        pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
-        cbBufLength = pRequest->paT2ISegs[0].cbSeg;
-    }
-    else
-        cbBufLength = 0;
-
-    do {
-        uint32_t cnISCSIRes = 0;
-        ISCSIRES aISCSIRes[4];
-        uint32_t aResBHS[12];
-
-        aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
-        aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
-        cnISCSIRes++;
-        if (cbBufLength != 0 &&
-            (   pRequest->enmXfer == SCSIXFER_FROM_TARGET
-             ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
-        {
-            aISCSIRes[cnISCSIRes].pvSeg = pDst;
-            aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
-            cnISCSIRes++;
-        }
-        /* Always reserve space for the status - it's impossible to tell
-         * beforehand whether this will be the final PDU or not. */
-        aISCSIRes[cnISCSIRes].pvSeg = aStatus;
-        aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
-        cnISCSIRes++;
-
-        rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
-        if (RT_FAILURE(rc))
-            break;
-
-        final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
-        ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
-        if (cmd == ISCSIOP_SCSI_RES)
-        {
-            /* This is the final PDU which delivers the status (and may be omitted if
-             * the last Data-In PDU included successful completion status). Note
-             * that ExpStatSN has been bumped already in iscsiRecvPDU. */
-            if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
-            {
-                /* SCSI Response in the wrong place or with a (target) failure. */
-                rc = VERR_PARSE_ERROR;
-                break;
-            }
-            /* The following is a bit tricky, as in error situations we may
-             * get the status only instead of the result data plus optional
-             * status. Thus the status may have ended up partially in the
-             * data area. */
-            pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
-            cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
-            if (cbData >= 2)
-            {
-                uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
-                if (cbStat + 2 > cbData)
-                {
-                    rc = VERR_BUFFER_OVERFLOW;
-                    break;
-                }
-                /* Truncate sense data if it doesn't fit into the buffer. */
-                pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
-                memcpy(pRequest->pvSense,
-                       ((const char *)aISCSIRes[1].pvSeg) + 2,
-                       RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
-                if (   cnISCSIRes > 2 && aISCSIRes[2].cbSeg
-                    && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
-                {
-                    memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg - 2,
-                           aISCSIRes[2].pvSeg,
-                           pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
-                }
-            }
-            else if (cbData == 1)
-            {
-                rc = VERR_PARSE_ERROR;
-                break;
-            }
-            else
-                pRequest->cbSense = 0;
-            break;
-        }
-        else if (cmd == ISCSIOP_SCSI_DATA_IN)
-        {
-            /* A Data-In PDU carries some data that needs to be added to the received
-             * data in response to the command. There may be both partial and complete
-             * Data-In PDUs, so collect data until the status is included or the status
-             * is sent in a separate SCSI Result frame (see above). */
-            if (final && aISCSIRes[2].cbSeg != 0)
-            {
-                /* The received PDU is partially stored in the buffer for status.
-                 * Must not happen under normal circumstances and is a target error. */
-                rc = VERR_BUFFER_OVERFLOW;
-                break;
-            }
-            uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
-            pDst = (uint32_t *)((char *)pDst + len);
-            cbBufLength -= len;
-            ExpDataSN++;
-            if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
-            {
-                pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
-                pRequest->cbSense = 0;
-                break;
-            }
-        }
-        else
-        {
-            rc = VERR_PARSE_ERROR;
-            break;
-        }
-    } while (true);
-
-    /* Remove SCSI request from queue. */
-    pImage->paCurrReq = NULL;
-    pImage->cnCurrReq = 0;
-
-out_release:
-    if (rc == VERR_TIMEOUT)
-    {
-        /* Drop connection in case the target plays dead. Much better than
-         * delaying the next requests until the timed out command actually
-         * finishes. Also keep in mind that command shouldn't take longer than
-         * about 30-40 seconds, or the guest will lose its patience. */
-        iscsiTransportClose(pImage);
-        pImage->state = ISCSISTATE_FREE;
-    }
-    RTSemMutexRelease(pImage->Mutex);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * Generate a new Initiator Task Tag.
- *
- * @returns Initiator Task Tag.
- * @param   pImage      The iSCSI connection state to be used.
- */
-static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
-{
-    uint32_t next_itt;
-
-    next_itt = pImage->ITT++;
-    if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
-        pImage->ITT = 0;
-    return RT_H2N_U32(next_itt);
-}
-
-
-/**
- * Send an iSCSI request. The request can consist of several segments, which
- * are padded to 4 byte boundaries and concatenated.
- *
- * @returns VBOX status
- * @param   pImage      The iSCSI connection state to be used.
- * @param   paReq       Pointer to array of iSCSI request sections.
- * @param   cnReq       Number of valid iSCSI request sections in the array.
- * @param   uFlags      Flags controlling the exact send semantics.
- */
-static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
-                        uint32_t uFlags)
-{
-    int rc = VINF_SUCCESS;
-    /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
-     * needs cleaning up of timeout/disconnect handling a bit, as otherwise
-     * too many incorrect errors are signalled. */
-    Assert(cnReq >= 1);
-    Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
-
-    for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
-    {
-        rc = iscsiTransportWrite(pImage, paReq, cnReq);
-        if (RT_SUCCESS(rc))
-            break;
-        if (   (uFlags & ISCSIPDU_NO_REATTACH)
-            || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
-            break;
-        /* No point in reestablishing the connection for a logout */
-        if (pImage->state == ISCSISTATE_IN_LOGOUT)
-            break;
-        RTThreadSleep(500);
-        if (pImage->state != ISCSISTATE_IN_LOGIN)
-        {
-            /* Attempt to re-login when a connection fails, but only when not
-             * currently logging in. */
-            rc = iscsiAttach(pImage);
-            if (RT_FAILURE(rc))
-                break;
-        }
-    }
-    return rc;
-}
-
-
-/**
- * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
- * split into several segments, as requested by the caller-provided buffer specification.
- * Remember that the response can be split into several PDUs by the sender, so make
- * sure that all parts are collected and processed appropriately by the caller.
- *
- * @returns VBOX status
- * @param   pImage      The iSCSI connection state to be used.
- * @param   paRes       Pointer to array of iSCSI response sections.
- * @param   cnRes       Number of valid iSCSI response sections in the array.
- */
-static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
-{
-    int rc = VINF_SUCCESS;
-    ISCSIRES aResBuf;
-
-    for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
-    {
-        aResBuf.pvSeg = pImage->pvRecvPDUBuf;
-        aResBuf.cbSeg = pImage->cbRecvPDUBuf;
-        rc = iscsiTransportRead(pImage, &aResBuf, 1);
-        if (RT_FAILURE(rc))
-        {
-            if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
-            {
-                /* No point in reestablishing the connection for a logout */
-                if (pImage->state == ISCSISTATE_IN_LOGOUT)
-                    break;
-                /* Connection broken while waiting for a response - wait a while and
-                 * try to restart by re-sending the original request (if any).
-                 * This also handles the connection reestablishment (login etc.). */
-                RTThreadSleep(500);
-                if (pImage->state != ISCSISTATE_IN_LOGIN)
-                {
-                    /* Attempt to re-login when a connection fails, but only when not
-                     * currently logging in. */
-                    rc = iscsiAttach(pImage);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-                if (pImage->paCurrReq != NULL)
-                {
-                    rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-            }
-            else
-            {
-                /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
-                break;
-            }
-        }
-        else
-        {
-            ISCSIOPCODE cmd;
-            const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
-
-            /* Check whether the received PDU is valid, and update the internal state of
-             * the iSCSI connection/session. */
-            rc = iscsiValidatePDU(&aResBuf, 1);
-            if (RT_FAILURE(rc))
-                continue;
-            cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
-            switch (cmd)
-            {
-                case ISCSIOP_SCSI_RES:
-                case ISCSIOP_SCSI_TASKMGMT_RES:
-                case ISCSIOP_SCSI_DATA_IN:
-                case ISCSIOP_R2T:
-                case ISCSIOP_ASYN_MSG:
-                case ISCSIOP_TEXT_RES:
-                case ISCSIOP_LOGIN_RES:
-                case ISCSIOP_LOGOUT_RES:
-                case ISCSIOP_REJECT:
-                case ISCSIOP_NOP_IN:
-                    if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
-                        pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
-                    if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
-                        pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
-                    break;
-                default:
-                    rc = VERR_PARSE_ERROR;
-            }
-            if (RT_FAILURE(rc))
-                continue;
-            if (    !pImage->FirstRecvPDU
-                &&  (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
-            {
-                if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
-                {
-                    /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
-                    if (    (cmd != ISCSIOP_R2T)
-                        &&  ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
-                        pImage->ExpStatSN++;
-                }
-                else
-                {
-                    rc = VERR_PARSE_ERROR;
-                    continue;
-                }
-            }
-            /* Finally check whether the received PDU matches what the caller wants. */
-            if (   itt == pcvResSeg[4]
-                && itt != ISCSI_TASK_TAG_RSVD)
-            {
-                /* Copy received PDU (one segment) to caller-provided buffers. */
-                uint32_t j;
-                size_t cbSeg;
-                const uint8_t *pSrc;
-
-                pSrc = (const uint8_t *)aResBuf.pvSeg;
-                cbSeg = aResBuf.cbSeg;
-                for (j = 0; j < cnRes; j++)
-                {
-                    if (cbSeg > paRes[j].cbSeg)
-                    {
-                        memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
-                        pSrc += paRes[j].cbSeg;
-                        cbSeg -= paRes[j].cbSeg;
-                    }
-                    else
-                    {
-                        memcpy(paRes[j].pvSeg, pSrc, cbSeg);
-                        paRes[j].cbSeg = cbSeg;
-                        cbSeg = 0;
-                        break;
-                    }
-                }
-                if (cbSeg != 0)
-                {
-                    rc = VERR_BUFFER_OVERFLOW;
-                    break;
-                }
-                for (j++; j < cnRes; j++)
-                    paRes[j].cbSeg = 0;
-                break;
-            }
-            else if (   cmd == ISCSIOP_NOP_IN
-                     && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
-            {
-                uint32_t cnISCSIReq;
-                ISCSIREQ aISCSIReq[4];
-                uint32_t aReqBHS[12];
-
-                aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
-                aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
-                aReqBHS[2] = pcvResSeg[2];      /* copy LUN from NOP-In */
-                aReqBHS[3] = pcvResSeg[3];      /* copy LUN from NOP-In */
-                aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
-                aReqBHS[5] = pcvResSeg[5];      /* copy TTT from NOP-In */
-                aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-                aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-                aReqBHS[8] = 0;             /* reserved */
-                aReqBHS[9] = 0;             /* reserved */
-                aReqBHS[10] = 0;            /* reserved */
-                aReqBHS[11] = 0;            /* reserved */
-
-                cnISCSIReq = 0;
-                aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
-                aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
-                cnISCSIReq++;
-
-                iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
-                /* Break if the caller wanted to process the NOP-in only. */
-                if (itt == ISCSI_TASK_TAG_RSVD)
-                    break;
-            }
-        }
-    }
-    return rc;
-}
-
-
-/**
- * Reset the PDU buffer
- *
- * @param   pImage      The iSCSI connection state to be used.
- */
-static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
-{
-    pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
-    pImage->fRecvPDUBHS       = true;
-    pImage->pbRecvPDUBufCur   = (uint8_t *)pImage->pvRecvPDUBuf;
-}
-
-static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
-{
-    if (!fFront)
-    {
-        /* Link the PDU at the tail of the list. */
-        if (!pImage->pIScsiPDUTxHead)
-            pImage->pIScsiPDUTxHead = pIScsiPDUTx;
-        else
-            pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
-        pImage->pIScsiPDUTxTail = pIScsiPDUTx;
-    }
-    else
-    {
-        /* Link PDU to at the front of the list. */
-        pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
-        pImage->pIScsiPDUTxHead = pIScsiPDUTx;
-        if (!pImage->pIScsiPDUTxTail)
-            pImage->pIScsiPDUTxTail = pIScsiPDUTx;
-    }
-}
-
-/**
- * Receives a PDU in a non blocking way.
- *
- * @returns VBOX status code.
- * @param   pImage      The iSCSI connection state to be used.
- */
-static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
-{
-    size_t cbActuallyRead = 0;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pImage=%#p\n", pImage));
-
-    /* Check if we are in the middle of a PDU receive. */
-    if (pImage->cbRecvPDUResidual == 0)
-    {
-        /*
-         * We are receiving a new PDU, don't read more than the BHS initially
-         * until we know the real size of the PDU.
-         */
-        iscsiRecvPDUReset(pImage);
-        LogFlow(("Receiving new PDU\n"));
-    }
-
-    rc = pImage->pInterfaceNetCallbacks->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
-                                                   pImage->cbRecvPDUResidual, &cbActuallyRead);
-    if (RT_SUCCESS(rc) && cbActuallyRead == 0)
-        rc = VERR_BROKEN_PIPE;
-
-    if (RT_SUCCESS(rc))
-    {
-        LogFlow(("Received %zu bytes\n", cbActuallyRead));
-        pImage->cbRecvPDUResidual -= cbActuallyRead;
-        pImage->pbRecvPDUBufCur   += cbActuallyRead;
-
-        /* Check if we received everything we wanted. */
-        if (   !pImage->cbRecvPDUResidual
-            && pImage->fRecvPDUBHS)
-        {
-            size_t cbAHSLength, cbDataLength;
-
-            /* If we were reading the BHS first get the actual PDU size now. */
-            uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
-            cbAHSLength = (word1 & 0xff000000) >> 24;
-            cbAHSLength = ((cbAHSLength - 1) | 3) + 1;      /* Add padding. */
-            cbDataLength = word1 & 0x00ffffff;
-            cbDataLength = ((cbDataLength - 1) | 3) + 1;    /* Add padding. */
-            pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
-            pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
-        }
-
-        if (!pImage->cbRecvPDUResidual)
-        {
-            /* We received the complete PDU with or without any payload now. */
-            LogFlow(("Received complete PDU\n"));
-            ISCSIRES aResBuf;
-            aResBuf.pvSeg = pImage->pvRecvPDUBuf;
-            aResBuf.cbSeg = pImage->cbRecvPDUBuf;
-            rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
-        }
-    }
-    else
-        LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
-
-    return rc;
-}
-
-static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
-{
-    size_t cbSent = 0;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pImage=%#p\n", pImage));
-
-    do
-    {
-        /*
-         * If there is no PDU active, get the first one from the list.
-         * Check that we are allowed to transfer the PDU by comparing the
-         * command sequence number and the maximum sequence number allowed by the target.
-         */
-        if (!pImage->pIScsiPDUTxCur)
-        {
-            if (   !pImage->pIScsiPDUTxHead
-                || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
-                break;
-
-            pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
-            pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
-            if (!pImage->pIScsiPDUTxHead)
-                pImage->pIScsiPDUTxTail = NULL;
-        }
-
-        /* Send as much as we can. */
-        rc = pImage->pInterfaceNetCallbacks->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
-        LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
-        if (RT_SUCCESS(rc))
-        {
-            LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
-            pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
-            RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
-            if (!pImage->pIScsiPDUTxCur->cbSgLeft)
-            {
-                /* PDU completed, free it and place the command on the waiting for response list. */
-                if (pImage->pIScsiPDUTxCur->pIScsiCmd)
-                {
-                    LogFlow(("Sent complete PDU, placing on waiting list\n"));
-                    iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
-                }
-                RTMemFree(pImage->pIScsiPDUTxCur);
-                pImage->pIScsiPDUTxCur = NULL;
-            }
-        }
-    } while (   RT_SUCCESS(rc)
-             && !pImage->pIScsiPDUTxCur);
-
-    if (rc == VERR_TRY_AGAIN)
-        rc = VINF_SUCCESS;
-
-    /* Add the write poll flag if we still have something to send, clear it otherwise. */
-    if (pImage->pIScsiPDUTxCur)
-        pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
-    else
-        pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
-
-    LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
-    return rc;
-}
-
-/**
- * Process a received PDU.
- *
- * @return VBOX status code.
- * @param  pImage      The iSCSI connection state to be used.
- * @param  paRes       Pointer to the array of iSCSI response sections.
- * @param  cnRes       Number of valid iSCSI response sections in the array.
- */
-static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
-{
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
-
-    /* Validate the PDU first. */
-    rc = iscsiValidatePDU(paRes, cnRes);
-    if (RT_SUCCESS(rc))
-    {
-        ISCSIOPCODE cmd;
-        const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
-
-        Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
-
-        do
-        {
-            cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
-            switch (cmd)
-            {
-                case ISCSIOP_SCSI_RES:
-                case ISCSIOP_SCSI_TASKMGMT_RES:
-                case ISCSIOP_SCSI_DATA_IN:
-                case ISCSIOP_R2T:
-                case ISCSIOP_ASYN_MSG:
-                case ISCSIOP_TEXT_RES:
-                case ISCSIOP_LOGIN_RES:
-                case ISCSIOP_LOGOUT_RES:
-                case ISCSIOP_REJECT:
-                case ISCSIOP_NOP_IN:
-                    if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
-                        pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
-                    if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
-                        pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
-                    break;
-                default:
-                    rc = VERR_PARSE_ERROR;
-            }
-
-            if (RT_FAILURE(rc))
-                break;
-
-            if (    !pImage->FirstRecvPDU
-                &&  (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
-            {
-                if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
-                {
-                    /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
-                    if (    (cmd != ISCSIOP_R2T)
-                        &&  ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
-                        pImage->ExpStatSN++;
-                }
-                else
-                {
-                   rc = VERR_PARSE_ERROR;
-                   break;
-                }
-            }
-
-            if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
-            {
-                /*
-                 * This is a response from the target for a request from the initiator.
-                 * Get the request and update its state.
-                 */
-                rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
-                /* Try to send more PDUs now that we updated the MaxCmdSN field */
-                if (   RT_SUCCESS(rc)
-                    && !pImage->pIScsiPDUTxCur)
-                    rc = iscsiSendPDUAsync(pImage);
-            }
-            else
-            {
-                /* This is a target initiated request (we handle only NOP-In request at the moment). */
-                if (   cmd == ISCSIOP_NOP_IN
-                    && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
-                {
-                    PISCSIPDUTX pIScsiPDUTx;
-                    uint32_t cnISCSIReq;
-                    uint32_t *paReqBHS;
-
-                    LogFlowFunc(("Sending NOP-Out\n"));
-
-                    /* Allocate a new PDU initialize it and put onto the waiting list. */
-                    pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
-                    if (!pIScsiPDUTx)
-                    {
-                        rc = VERR_NO_MEMORY;
-                        break;
-                    }
-                    paReqBHS = &pIScsiPDUTx->aBHS[0];
-                    paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
-                    paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
-                    paReqBHS[2] = pcvResSeg[2];      /* copy LUN from NOP-In */
-                    paReqBHS[3] = pcvResSeg[3];      /* copy LUN from NOP-In */
-                    paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
-                    paReqBHS[5] = pcvResSeg[5];      /* copy TTT from NOP-In */
-                    paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-                    paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-                    paReqBHS[8] = 0;             /* reserved */
-                    paReqBHS[9] = 0;             /* reserved */
-                    paReqBHS[10] = 0;            /* reserved */
-                    paReqBHS[11] = 0;            /* reserved */
-
-                    cnISCSIReq = 0;
-                    pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
-                    pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
-                    cnISCSIReq++;
-                    pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
-                    RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
-
-                    /* Link the PDU to the list. */
-                    iscsiPDUTxAdd(pImage, pIScsiPDUTx, false /* fFront */);
-
-                    /* Start transfer of a PDU if there is no one active at the moment. */
-                    if (!pImage->pIScsiPDUTxCur)
-                        rc = iscsiSendPDUAsync(pImage);
-                }
-            }
-        } while (0);
-    }
-
-    return rc;
-}
-
-/**
- * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
- *
- * @returns VBOX status
- * @param   paRes       Pointer to array of iSCSI response sections.
- * @param   cnRes       Number of valid iSCSI response sections in the array.
- */
-static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
-{
-    const uint32_t *pcrgResBHS;
-    uint32_t hw0;
-    Assert(cnRes >= 1);
-    Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
-
-    LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
-
-    pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
-    hw0 = RT_N2H_U32(pcrgResBHS[0]);
-    switch (hw0 & ISCSIOP_MASK)
-    {
-        case ISCSIOP_NOP_IN:
-            /* NOP-In responses must not be split into several PDUs nor it may contain
-             * ping data for target-initiated pings nor may both task tags be valid task tags. */
-            if (    (hw0 & ISCSI_FINAL_BIT) == 0
-                ||  (    RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
-                     &&  RT_N2H_U32(pcrgResBHS[1]) != 0)
-                ||  (    RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
-                     &&  RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_SCSI_RES:
-            /* SCSI responses must not be split into several PDUs nor must the residual
-             * bits be contradicting each other nor may the residual bits be set for PDUs
-             * containing anything else but a completed command response. Underflow
-             * is no reason for declaring a PDU as invalid, as the target may choose
-             * to return less data than we assume to get. */
-            if (    (hw0 & ISCSI_FINAL_BIT) == 0
-                ||  ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
-                ||  ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
-                ||  (    ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
-                     &&  ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
-                     &&  (hw0 & (  ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
-                                 | ISCSI_RESIDUAL_OVFL_BIT))))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_LOGIN_RES:
-            /* Login responses must not contain contradicting transit and continue bits. */
-            if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_TEXT_RES:
-            /* Text responses must not contain contradicting final and continue bits nor
-             * may the final bit be set for PDUs containing a target transfer tag other than
-             * the reserved transfer tag (and vice versa). */
-            if (    (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
-                ||  (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
-                ||  (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_SCSI_DATA_IN:
-            /* SCSI Data-in responses must not contain contradicting residual bits when
-             * status bit is set. */
-            if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_LOGOUT_RES:
-            /* Logout responses must not have the final bit unset and may not contain any
-             * data or additional header segments. */
-            if (    ((hw0 & ISCSI_FINAL_BIT) == 0)
-                ||  (RT_N2H_U32(pcrgResBHS[1]) != 0))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_ASYN_MSG:
-            /* Asynchronous Messages must not have the final bit unset and may not contain
-             * an initiator task tag. */
-            if (    ((hw0 & ISCSI_FINAL_BIT) == 0)
-                ||  (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
-                return VERR_PARSE_ERROR;
-            break;
-        case ISCSIOP_SCSI_TASKMGMT_RES:
-        case ISCSIOP_R2T:
-        case ISCSIOP_REJECT:
-        default:
-            /* Do some logging, ignore PDU. */
-            LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
-            return VERR_PARSE_ERROR;
-    }
-    /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
-
-    if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
-        return VERR_PARSE_ERROR;
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Prepares a PDU to transfer for the given command and adds it to the list.
- */
-static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
-{
-    int rc = VINF_SUCCESS;
-    uint32_t *paReqBHS;
-    size_t cbData = 0;
-    size_t cbSegs = 0;
-    PSCSIREQ pScsiReq;
-    PISCSIPDUTX pIScsiPDU = NULL;
-
-    LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
-
-    Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
-
-    pIScsiCmd->Itt = iscsiNewITT(pImage);
-    pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
-
-    if (pScsiReq->cT2ISegs)
-        RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
-
-    /*
-     * Allocate twice as much entries as required for padding (worst case).
-     * The additional segment is for the BHS.
-     */
-    size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
-    pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_OFFSETOF(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
-    if (!pIScsiPDU)
-        return VERR_NO_MEMORY;
-
-    pIScsiPDU->pIScsiCmd = pIScsiCmd;
-
-    if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
-        cbData = (uint32_t)pScsiReq->cbT2IData;
-    else
-        cbData = (uint32_t)pScsiReq->cbI2TData;
-
-    paReqBHS = pIScsiPDU->aBHS;
-
-    /* Setup the BHS. */
-    paReqBHS[0] = RT_H2N_U32(  ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
-                             | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
-    paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
-    paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
-    paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
-    paReqBHS[4] = pIScsiCmd->Itt;
-    paReqBHS[5] = RT_H2N_U32(cbData);
-    paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
-    paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
-    memcpy(paReqBHS + 8, pScsiReq->pvCDB, pScsiReq->cbCDB);
-
-    pIScsiPDU->CmdSN = pImage->CmdSN;
-    pImage->CmdSN++;
-
-    /* Setup the S/G buffers. */
-    uint32_t cnISCSIReq = 0;
-    pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
-    pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
-    cnISCSIReq++;
-    cbSegs = sizeof(pIScsiPDU->aBHS);
-    /* Padding is not necessary for the BHS. */
-
-    if (pScsiReq->cbI2TData)
-    {
-        for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
-        {
-            Assert(cnISCSIReq < cI2TSegs);
-            pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
-            pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
-            cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
-            cnISCSIReq++;
-
-            /* Add padding if necessary. */
-            if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
-            {
-                Assert(cnISCSIReq < cI2TSegs);
-                pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
-                pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
-                cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
-                cnISCSIReq++;
-            }
-        }
-    }
-
-    pIScsiPDU->cISCSIReq = cnISCSIReq;
-    pIScsiPDU->cbSgLeft  = cbSegs;
-    RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
-
-    /* Link the PDU to the list. */
-    iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
-
-    /* Start transfer of a PDU if there is no one active at the moment. */
-    if (!pImage->pIScsiPDUTxCur)
-        rc = iscsiSendPDUAsync(pImage);
-
-    return rc;
-}
-
-
-/**
- * Updates the state of a request from the PDU we received.
- *
- * @return VBox status code.
- * @param   pImage      iSCSI connection state to use.
- * @param   paRes       Pointer to array of iSCSI response sections.
- * @param   cnRes       Number of valid iSCSI response sections in the array.
- */
-static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
-{
-    int rc = VINF_SUCCESS;
-    PISCSICMD pIScsiCmd;
-    uint32_t *paResBHS;
-
-    LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
-
-    Assert(cnRes == 1);
-    Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
-
-    paResBHS = (uint32_t *)paRes[0].pvSeg;
-
-    pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
-
-    if (pIScsiCmd)
-    {
-        bool final = false;
-        PSCSIREQ pScsiReq;
-
-        LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
-
-        Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
-        pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
-
-        final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
-        ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
-        if (cmd == ISCSIOP_SCSI_RES)
-        {
-            /* This is the final PDU which delivers the status (and may be omitted if
-             * the last Data-In PDU included successful completion status). Note
-             * that ExpStatSN has been bumped already in iscsiRecvPDU. */
-            if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
-            {
-                /* SCSI Response in the wrong place or with a (target) failure. */
-                LogFlow(("Wrong ExpStatSN value in PDU\n"));
-                rc = VERR_PARSE_ERROR;
-            }
-            else
-            {
-                pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
-                size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
-                void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
-
-                if (cbData >= 2)
-                {
-                    uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
-                    if (cbStat + 2 > cbData)
-                    {
-                        rc = VERR_BUFFER_OVERFLOW;
-                    }
-                    else
-                    {
-                        /* Truncate sense data if it doesn't fit into the buffer. */
-                        pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
-                        memcpy(pScsiReq->pvSense, (uint8_t *)pvSense + 2,
-                               RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
-                    }
-                }
-                else if (cbData == 1)
-                    rc = VERR_PARSE_ERROR;
-                else
-                    pScsiReq->cbSense = 0;
-            }
-            iscsiCmdComplete(pImage, pIScsiCmd, rc);
-        }
-        else if (cmd == ISCSIOP_SCSI_DATA_IN)
-        {
-            /* A Data-In PDU carries some data that needs to be added to the received
-             * data in response to the command. There may be both partial and complete
-             * Data-In PDUs, so collect data until the status is included or the status
-             * is sent in a separate SCSI Result frame (see above). */
-            size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
-            void   *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
-
-            if (final && cbData > pScsiReq->cbT2IData)
-            {
-                /* The received PDU is bigger than what we requested.
-                 * Must not happen under normal circumstances and is a target error. */
-                rc = VERR_BUFFER_OVERFLOW;
-            }
-            else
-            {
-                /* Copy data from the received PDU into the T2I segments. */
-                size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
-                Assert(cbCopied == cbData);
-
-                if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
-                {
-                    pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
-                    pScsiReq->cbSense = 0;
-                    iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
-                }
-            }
-        }
-        else
-            rc = VERR_PARSE_ERROR;
-    }
-
-    /* Log any errors here but ignore the PDU. */
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
-        rc = VINF_SUCCESS;
-    }
-
-    return rc;
-}
-
-/**
- * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
- * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
- * by the caller. Strings must be in UTF-8 encoding.
- *
- * @returns VBOX status
- * @param   pbBuf       Pointer to the key-value buffer.
- * @param   cbBuf       Length of the key-value buffer.
- * @param   pcbBufCurr  Currently used portion of the key-value buffer.
- * @param   pszKey      Pointer to a string containing the key.
- * @param   pszValue    Pointer to either a string containing the value or to a large binary value.
- * @param   cbValue     Length of the binary value if applicable.
- */
-static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
-                                   const char *pcszValue, size_t cbValue)
-{
-    size_t cbBufTmp = *pcbBufCurr;
-    size_t cbKey = strlen(pcszKey);
-    size_t cbValueEnc;
-    uint8_t *pbCurr;
-
-    if (cbValue == 0)
-        cbValueEnc = strlen(pcszValue);
-    else
-        cbValueEnc = cbValue * 2 + 2;   /* 2 hex bytes per byte, 2 bytes prefix */
-
-    if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
-    {
-        /* Buffer would overflow, signal error. */
-        return VERR_BUFFER_OVERFLOW;
-    }
-
-    /*
-     * Append a key=value pair (zero terminated string) to the end of the buffer.
-     */
-    pbCurr = pbBuf + cbBufTmp;
-    memcpy(pbCurr, pcszKey, cbKey);
-    pbCurr += cbKey;
-    *pbCurr++ = '=';
-    if (cbValue == 0)
-    {
-        memcpy(pbCurr, pcszValue, cbValueEnc);
-        pbCurr += cbValueEnc;
-    }
-    else
-    {
-        *pbCurr++ = '0';
-        *pbCurr++ = 'x';
-        for (uint32_t i = 0; i < cbValue; i++)
-        {
-            uint8_t b;
-            b = pcszValue[i];
-            *pbCurr++ = NUM_2_HEX(b >> 4);
-            *pbCurr++ = NUM_2_HEX(b & 0xf);
-        }
-    }
-    *pbCurr = '\0';
-    *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Retrieve the value for a given key from the key=value buffer.
- *
- * @returns VBOX status.
- * @param   pbBuf       Buffer containing key=value pairs.
- * @param   cbBuf       Length of buffer with key=value pairs.
- * @param   pszKey      Pointer to key for which to retrieve the value.
- * @param   ppszValue   Pointer to value string pointer.
- */
-static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
-{
-    size_t cbKey = strlen(pcszKey);
-
-    while (cbBuf != 0)
-    {
-        size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
-
-        if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
-        {
-            *ppcszValue = (const char *)(pbBuf + cbKey + 1);
-            return VINF_SUCCESS;
-        }
-        pbBuf += cbKeyValNull;
-        cbBuf -= cbKeyValNull;
-    }
-    return VERR_INVALID_NAME;
-}
-
-
-/**
- * Convert a long-binary value from a value string to the binary representation.
- *
- * @returns VBOX status
- * @param   pszValue    Pointer to a string containing the textual value representation.
- * @param   pbValue     Pointer to the value buffer for the binary value.
- * @param   pcbValue    In: length of value buffer, out: actual length of binary value.
- */
-static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
-{
-    size_t cbValue = *pcbValue;
-    char c1, c2, c3, c4;
-    Assert(cbValue >= 1);
-
-    if (strlen(pcszValue) < 3)
-        return VERR_PARSE_ERROR;
-    if (*pcszValue++ != '0')
-        return VERR_PARSE_ERROR;
-    switch (*pcszValue++)
-    {
-        case 'x':
-        case 'X':
-            if (strlen(pcszValue) & 1)
-            {
-                c1 = *pcszValue++;
-                *pbValue++ = HEX_2_NUM(c1);
-                cbValue--;
-            }
-            while (*pcszValue != '\0')
-            {
-                if (cbValue == 0)
-                    return VERR_BUFFER_OVERFLOW;
-                c1 = *pcszValue++;
-                if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
-                    return VERR_PARSE_ERROR;
-                c2 = *pcszValue++;
-                if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
-                    return VERR_PARSE_ERROR;
-                *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
-                cbValue--;
-            }
-            *pcbValue -= cbValue;
-            break;
-        case 'b':
-        case 'B':
-            if ((strlen(pcszValue) & 3) != 0)
-                return VERR_PARSE_ERROR;
-            while (*pcszValue != '\0')
-            {
-                uint32_t temp;
-                if (cbValue == 0)
-                    return VERR_BUFFER_OVERFLOW;
-                c1 = *pcszValue++;
-                if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
-                    return VERR_PARSE_ERROR;
-                c2 = *pcszValue++;
-                if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
-                    return VERR_PARSE_ERROR;
-                c3 = *pcszValue++;
-                if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
-                    return VERR_PARSE_ERROR;
-                c4 = *pcszValue++;
-                if (    (c3 == '=' && c4 != '=')
-                    ||  ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
-                    return VERR_PARSE_ERROR;
-                temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
-                if (c3 == '=') {
-                    if (*pcszValue != '\0')
-                        return VERR_PARSE_ERROR;
-                    *pbValue++ = temp >> 16;
-                    cbValue--;
-                } else {
-                    temp |= B64_2_NUM(c3) << 6;
-                    if (c4 == '=') {
-                        if (*pcszValue != '\0')
-                            return VERR_PARSE_ERROR;
-                        if (cbValue < 2)
-                            return VERR_BUFFER_OVERFLOW;
-                        *pbValue++ = temp >> 16;
-                        *pbValue++ = (temp >> 8) & 0xff;
-                        cbValue -= 2;
-                    }
-                    else
-                    {
-                        temp |= B64_2_NUM(c4);
-                        if (cbValue < 3)
-                            return VERR_BUFFER_OVERFLOW;
-                        *pbValue++ = temp >> 16;
-                        *pbValue++ = (temp >> 8) & 0xff;
-                        *pbValue++ = temp & 0xff;
-                        cbValue -= 3;
-                    }
-                }
-            }
-            *pcbValue -= cbValue;
-            break;
-        default:
-            return VERR_PARSE_ERROR;
-    }
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Retrieve the relevant parameter values and update the initiator state.
- *
- * @returns VBOX status.
- * @param   pImage      Current iSCSI initiator state.
- * @param   pbBuf       Buffer containing key=value pairs.
- * @param   cbBuf       Length of buffer with key=value pairs.
- */
-static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
-{
-    int rc;
-    const char *pcszMaxRecvDataSegmentLength = NULL;
-    const char *pcszMaxBurstLength = NULL;
-    const char *pcszFirstBurstLength = NULL;
-    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
-    if (rc == VERR_INVALID_NAME)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-        return VERR_PARSE_ERROR;
-    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
-    if (rc == VERR_INVALID_NAME)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-        return VERR_PARSE_ERROR;
-    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
-    if (rc == VERR_INVALID_NAME)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-        return VERR_PARSE_ERROR;
-    if (pcszMaxRecvDataSegmentLength)
-    {
-        uint32_t cb = pImage->cbSendDataLength;
-        rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
-        AssertRC(rc);
-        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
-    }
-    if (pcszMaxBurstLength)
-    {
-        uint32_t cb = pImage->cbSendDataLength;
-        rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
-        AssertRC(rc);
-        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
-    }
-    if (pcszFirstBurstLength)
-    {
-        uint32_t cb = pImage->cbSendDataLength;
-        rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
-        AssertRC(rc);
-        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
-    }
-    return VINF_SUCCESS;
-}
-
-
-static bool serial_number_less(uint32_t s1, uint32_t s2)
-{
-    return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
-}
-
-static bool serial_number_greater(uint32_t s1, uint32_t s2)
-{
-    return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
-}
-
-
-#ifdef IMPLEMENT_TARGET_AUTH
-static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
-{
-    uint8_t cbChallenge;
-
-    cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
-    RTrand_bytes(pbChallenge, cbChallenge);
-    *pcbChallenge = cbChallenge;
-}
-#endif
-
-
-static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
-                                      const uint8_t *pbSecret, size_t cbSecret)
-{
-    RTMD5CONTEXT ctx;
-    uint8_t bId;
-
-    bId = id;
-    RTMd5Init(&ctx);
-    RTMd5Update(&ctx, &bId, 1);
-    RTMd5Update(&ctx, pbSecret, cbSecret);
-    RTMd5Update(&ctx, pbChallenge, cbChallenge);
-    RTMd5Final(pbResponse, &ctx);
-}
-
-/**
- * Internal. - Wrapper around the extended select callback of the net interface.
- */
-DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
-{
-    return pImage->pInterfaceNetCallbacks->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
-}
-
-/**
- * Internal. - Pokes a thread waiting for I/O.
- */
-DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
-{
-    return pImage->pInterfaceNetCallbacks->pfnPoke(pImage->Socket);
-}
-
-/**
- * Internal. - Get the next request from the queue.
- */
-DECLINLINE(PISCSICMD) iscsiCmdGet(PISCSIIMAGE pImage)
-{
-    int rc;
-    PISCSICMD pIScsiCmd = NULL;
-
-    rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
-    AssertRC(rc);
-
-    pIScsiCmd = pImage->pScsiReqQueue;
-    if (pIScsiCmd)
-    {
-        pImage->pScsiReqQueue = pIScsiCmd->pNext;
-        pIScsiCmd->pNext = NULL;
-    }
-
-    rc = RTSemMutexRelease(pImage->MutexReqQueue);
-    AssertRC(rc);
-
-    return pIScsiCmd;
-}
-
-
-/**
- * Internal. - Adds the given command to the queue.
- */
-DECLINLINE(int) iscsiCmdPut(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
-{
-    int rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
-    AssertRC(rc);
-
-    pIScsiCmd->pNext = pImage->pScsiReqQueue;
-    pImage->pScsiReqQueue = pIScsiCmd;
-
-    rc = RTSemMutexRelease(pImage->MutexReqQueue);
-    AssertRC(rc);
-
-    iscsiIoThreadPoke(pImage);
-
-    return rc;
-}
-
-/**
- * Internal. - Completes the request with the appropriate action.
- *             Synchronous requests are completed with waking up the thread
- *             and asynchronous ones by continuing the associated I/O context.
- */
-static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd)
-{
-    LogFlowFunc(("pImage=%#p pIScsiCmd=%#p rcCmd=%Rrc\n", pImage, pIScsiCmd, rcCmd));
-
-    /* Remove from the table first. */
-    iscsiCmdRemove(pImage, pIScsiCmd->Itt);
-
-    /* Call completion callback. */
-    pIScsiCmd->pfnComplete(pImage, rcCmd, pIScsiCmd->pvUser);
-
-    /* Free command structure. */
-#ifdef DEBUG
-    memset(pIScsiCmd, 0xff, sizeof(ISCSICMD));
-#endif
-    RTMemFree(pIScsiCmd);
-}
-
-/**
- * Reattaches the to the target after an error aborting
- * pending commands and resending them.
- *
- * @param    pImage    iSCSI connection state.
- */
-static void iscsiReattach(PISCSIIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-    PISCSICMD pIScsiCmdHead = NULL;
-    PISCSICMD pIScsiCmd = NULL;
-    PISCSICMD pIScsiCmdCur = NULL;
-    PISCSIPDUTX pIScsiPDUTx = NULL;
-
-    /* Close connection. */
-    iscsiTransportClose(pImage);
-    pImage->state = ISCSISTATE_FREE;
-
-    /* Reset PDU we are receiving. */
-    iscsiRecvPDUReset(pImage);
-
-    /*
-     * Abort all PDUs we are about to transmit,
-     * the command need a new Itt if the relogin is successful.
-     */
-    while (pImage->pIScsiPDUTxHead)
-    {
-        pIScsiPDUTx = pImage->pIScsiPDUTxHead;
-        pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
-
-        pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
-
-        if (pIScsiCmd)
-        {
-            /* Place on command list. */
-            pIScsiCmd->pNext = pIScsiCmdHead;
-            pIScsiCmdHead = pIScsiCmd;
-        }
-        RTMemFree(pIScsiPDUTx);
-    }
-
-    /* Clear the current PDU too. */
-    if (pImage->pIScsiPDUTxCur)
-    {
-        pIScsiPDUTx = pImage->pIScsiPDUTxCur;
-
-        pImage->pIScsiPDUTxCur = NULL;
-        pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
-
-        if (pIScsiCmd)
-        {
-            pIScsiCmd->pNext = pIScsiCmdHead;
-            pIScsiCmdHead = pIScsiCmd;
-        }
-        RTMemFree(pIScsiPDUTx);
-    }
-
-    /*
-     * Get all commands which are waiting for a response
-     * They need to be resend too after a successful reconnect.
-     */
-    pIScsiCmd = iscsiCmdRemoveAll(pImage);
-
-    if (pIScsiCmd)
-    {
-        pIScsiCmdCur = pIScsiCmd;
-        while (pIScsiCmdCur->pNext)
-            pIScsiCmdCur = pIScsiCmdCur->pNext;
-
-        /*
-         * Place them in front of the list because they are the oldest requests
-         * and need to be processed first to minimize the risk to time out.
-         */
-        pIScsiCmdCur->pNext = pIScsiCmdHead;
-        pIScsiCmdHead = pIScsiCmd;
-    }
-
-    /* Try to attach. */
-    rc = iscsiAttach(pImage);
-    if (RT_SUCCESS(rc))
-    {
-        /* Phew, we have a connection again.
-         * Prepare new PDUs for the aborted commands.
-         */
-        while (pIScsiCmdHead)
-        {
-            pIScsiCmd = pIScsiCmdHead;
-            pIScsiCmdHead = pIScsiCmdHead->pNext;
-
-            pIScsiCmd->pNext = NULL;
-
-            rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
-            AssertRC(rc);
-        }
-    }
-    else
-    {
-        /*
-         * Still no luck, complete commands with error so the caller
-         * has a chance to inform the user and maybe resend the command.
-         */
-        while (pIScsiCmdHead)
-        {
-            pIScsiCmd = pIScsiCmdHead;
-            pIScsiCmdHead = pIScsiCmdHead->pNext;
-
-            iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
-        }
-    }
-}
-
-/**
- * Internal. Main iSCSI I/O worker.
- */
-static DECLCALLBACK(int) iscsiIoThreadWorker(RTTHREAD ThreadSelf, void *pvUser)
-{
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
-
-    /* Initialize the initial event mask. */
-    pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
-
-    while (pImage->fRunning)
-    {
-        uint32_t fEvents;
-        int rc;
-
-        fEvents = 0;
-
-        /* Wait for work or for data from the target. */
-        RTMSINTERVAL msWait;
-
-        if (pImage->cCmdsWaiting)
-        {
-            pImage->fPollEvents &= ~VD_INTERFACETCPNET_HINT_INTERRUPT;
-            msWait = pImage->uReadTimeout;
-        }
-        else
-        {
-            pImage->fPollEvents |= VD_INTERFACETCPNET_HINT_INTERRUPT;
-            msWait = RT_INDEFINITE_WAIT;
-        }
-
-        LogFlow(("Waiting for events fPollEvents=%#x\n", pImage->fPollEvents));
-        rc = iscsiIoThreadWait(pImage, msWait, pImage->fPollEvents, &fEvents);
-        if (rc == VERR_INTERRUPTED)
-        {
-            /* Check the queue. */
-            PISCSICMD pIScsiCmd = iscsiCmdGet(pImage);
-
-            while (pIScsiCmd)
-            {
-                switch (pIScsiCmd->enmCmdType)
-                {
-                    case ISCSICMDTYPE_REQ:
-                    {
-                        /* If there is no connection complete the command with an error. */
-                        if (RT_LIKELY(iscsiIsClientConnected(pImage)))
-                        {
-                            rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
-                            AssertRC(rc);
-                        }
-                        else
-                            iscsiCmdComplete(pImage, pIScsiCmd, VERR_NET_CONNECTION_REFUSED);
-                        break;
-                    }
-                    case ISCSICMDTYPE_EXEC:
-                    {
-                        rc = pIScsiCmd->CmdType.Exec.pfnExec(pIScsiCmd->CmdType.Exec.pvUser);
-                        iscsiCmdComplete(pImage, pIScsiCmd, rc);
-                        break;
-                    }
-                    default:
-                        AssertMsgFailed(("Invalid command type %d\n", pIScsiCmd->enmCmdType));
-                }
-
-                pIScsiCmd = iscsiCmdGet(pImage);
-            }
-        }
-        else if (rc == VERR_TIMEOUT && pImage->cCmdsWaiting)
-        {
-            /*
-             * We are waiting for a response from the target but
-             * it didn't answered yet.
-             * We assume the connection is broken and try to reconnect.
-             */
-            LogFlow(("Timed out while waiting for an answer from the target, reconnecting\n"));
-            iscsiReattach(pImage);
-        }
-        else if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
-        {
-            Assert(pImage->state == ISCSISTATE_NORMAL);
-            LogFlow(("Got socket events %#x\n", fEvents));
-
-            if (fEvents & VD_INTERFACETCPNET_EVT_READ)
-            {
-                /* Continue or start a new PDU receive task */
-                LogFlow(("There is data on the socket\n"));
-                rc = iscsiRecvPDUAsync(pImage);
-                if (rc == VERR_BROKEN_PIPE)
-                    iscsiReattach(pImage);
-                else if (RT_FAILURE(rc))
-                    LogRel(("iSCSI: Handling incoming request failed %Rrc\n", rc));
-            }
-
-            if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
-            {
-                LogFlow(("The socket is writable\n"));
-                rc = iscsiSendPDUAsync(pImage);
-                if (RT_FAILURE(rc))
-                    LogRel(("iSCSI: Sending PDU failed %Rrc\n", rc));
-            }
-
-            if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
-            {
-                LogFlow(("An error ocurred\n"));
-                iscsiReattach(pImage);
-            }
-        }
-        else
-        {
-            LogRel(("iSCSI: Waiting for I/O failed rc=%Rrc\n", rc));
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal. - Enqueues a request asynchronously.
- */
-static int iscsiCommandAsync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq,
-                             PFNISCSICMDCOMPLETED pfnComplete, void *pvUser)
-{
-    int rc;
-
-    if (pImage->fExtendedSelectSupported)
-    {
-        PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
-        if (!pIScsiCmd)
-            return VERR_NO_MEMORY;
-
-        /* Init the command structure. */
-        pIScsiCmd->pNext                    = NULL;
-        pIScsiCmd->enmCmdType               = ISCSICMDTYPE_REQ;
-        pIScsiCmd->pfnComplete              = pfnComplete;
-        pIScsiCmd->pvUser                   = pvUser;
-        pIScsiCmd->CmdType.ScsiReq.pScsiReq = pScsiReq;
-
-        rc = iscsiCmdPut(pImage, pIScsiCmd);
-        if (RT_FAILURE(rc))
-            RTMemFree(pIScsiCmd);
-    }
-    else
-        rc = VERR_NOT_SUPPORTED;
-
-    return rc;
-}
-
-static void iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
-{
-    PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
-
-    pIScsiCmdSync->rcCmd = rcReq;
-    int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
-    AssertRC(rc);
-}
-
-/**
- * Internal. - Enqueues a request in a synchronous fashion
- * i.e. returns when the request completed.
- */
-static int iscsiCommandSync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq, bool fRetry, int rcSense)
-{
-    int rc;
-
-    if (pImage->fExtendedSelectSupported)
-    {
-        ISCSICMDSYNC IScsiCmdSync;
-
-        /* Create event semaphore. */
-        rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
-        if (RT_FAILURE(rc))
-            return rc;
-
-        if (fRetry)
-        {
-            for (unsigned i = 0; i < 10; i++)
-            {
-                rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
-                if (RT_FAILURE(rc))
-                    break;
-
-                rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
-                AssertRC(rc);
-                rc = IScsiCmdSync.rcCmd;
-
-                if (    (RT_SUCCESS(rc) && !pScsiReq->cbSense)
-                    ||  RT_FAILURE(rc))
-                    break;
-                rc = rcSense;
-            }
-        }
-        else
-        {
-            rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
-            if (RT_SUCCESS(rc))
-            {
-                rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
-                AssertRC(rc);
-                rc = IScsiCmdSync.rcCmd;
-
-                if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
-                        rc = rcSense;
-            }
-        }
-
-        RTSemEventDestroy(IScsiCmdSync.EventSem);
-    }
-    else
-    {
-        if (fRetry)
-        {
-            for (unsigned i = 0; i < 10; i++)
-            {
-                rc = iscsiCommand(pImage, pScsiReq);
-                if (    (RT_SUCCESS(rc) && !pScsiReq->cbSense)
-                    ||  RT_FAILURE(rc))
-                    break;
-                rc = rcSense;
-            }
-        }
-        else
-        {
-            rc = iscsiCommand(pImage, pScsiReq);
-            if (RT_SUCCESS(rc) && pScsiReq->cbSense > 0)
-                rc = rcSense;
-        }
-    }
-
-    return rc;
-}
-
-
-/**
- * Internal. - Executes a given function in a synchronous fashion
- *             on the I/O thread if available.
- */
-static int iscsiExecSync(PISCSIIMAGE pImage, PFNISCSIEXEC pfnExec, void *pvUser)
-{
-    int rc;
-
-    if (pImage->fExtendedSelectSupported)
-    {
-        ISCSICMDSYNC IScsiCmdSync;
-        PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
-        if (!pIScsiCmd)
-            return VERR_NO_MEMORY;
-
-        /* Create event semaphore. */
-        rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
-        if (RT_FAILURE(rc))
-        {
-            RTMemFree(pIScsiCmd);
-            return rc;
-        }
-
-        /* Init the command structure. */
-        pIScsiCmd->pNext                = NULL;
-        pIScsiCmd->enmCmdType           = ISCSICMDTYPE_EXEC;
-        pIScsiCmd->pfnComplete          = iscsiCommandCompleteSync;
-        pIScsiCmd->pvUser               = &IScsiCmdSync;
-        pIScsiCmd->CmdType.Exec.pfnExec = pfnExec;
-        pIScsiCmd->CmdType.Exec.pvUser  = pvUser;
-
-        rc = iscsiCmdPut(pImage, pIScsiCmd);
-        if (RT_FAILURE(rc))
-            RTMemFree(pIScsiCmd);
-        else
-        {
-            rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
-            AssertRC(rc);
-            rc = IScsiCmdSync.rcCmd;
-        }
-
-        RTSemEventDestroy(IScsiCmdSync.EventSem);
-    }
-    else
-    {
-        /* No I/O thread, execute in the current thread. */
-        rc = pfnExec(pvUser);
-    }
-
-    return rc;
-}
-
-
-static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
-{
-    bool fComplete = true;
-    size_t cbTransfered = 0;
-    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
-    PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
-
-    if (   RT_SUCCESS(rcReq)
-        && pScsiReq->cbSense > 0)
-    {
-        /* Try again if possible. */
-        if (pReqAsync->cSenseRetries > 0)
-        {
-            pReqAsync->cSenseRetries--;
-            pScsiReq->cbSense = sizeof(pReqAsync->abSense);
-            int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pReqAsync);
-            if (RT_SUCCESS(rc))
-                fComplete = false;
-            else
-                rcReq = pReqAsync->rcSense;
-        }
-        else
-            rcReq = pReqAsync->rcSense;
-    }
-
-    if (fComplete)
-    {
-        if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
-            cbTransfered = pScsiReq->cbT2IData;
-        else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
-            cbTransfered = pScsiReq->cbI2TData;
-        else
-            AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
-
-        /* Continue I/O context. */
-        pImage->pInterfaceIoCallbacks->pfnIoCtxCompleted(pImage->pInterfaceIo->pvUser,
-                                                         pReqAsync->pIoCtx, rcReq,
-                                                         cbTransfered);
-
-        RTMemFree(pScsiReq);
-        RTMemFree(pReqAsync);
-    }
-}
-
-
-/**
- * Internal. Free all allocated space for representing an image, and optionally
- * delete the image from disk.
- */
-static int iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-    Assert(!fDelete); /* This MUST be false, the flag isn't supported. */
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (pImage->Mutex != NIL_RTSEMMUTEX)
-        {
-            /* Detaching only makes sense when the mutex is there. Otherwise the
-             * failure happened long before we could attach to the target. */
-            iscsiExecSync(pImage, iscsiDetach, pImage);
-            RTSemMutexDestroy(pImage->Mutex);
-            pImage->Mutex = NIL_RTSEMMUTEX;
-        }
-        if (pImage->hThreadIo != NIL_RTTHREAD)
-        {
-            ASMAtomicXchgBool(&pImage->fRunning, false);
-            rc = iscsiIoThreadPoke(pImage);
-            AssertRC(rc);
-
-            /* Wait for the thread to terminate. */
-            rc = RTThreadWait(pImage->hThreadIo, RT_INDEFINITE_WAIT, NULL);
-            AssertRC(rc);
-        }
-        /* Destroy the socket. */
-        if (pImage->Socket != NIL_VDSOCKET)
-        {
-            pImage->pInterfaceNetCallbacks->pfnSocketDestroy(pImage->Socket);
-        }
-        if (pImage->MutexReqQueue != NIL_RTSEMMUTEX)
-        {
-            RTSemMutexDestroy(pImage->MutexReqQueue);
-            pImage->MutexReqQueue = NIL_RTSEMMUTEX;
-        }
-        if (pImage->pszTargetName)
-        {
-            RTMemFree(pImage->pszTargetName);
-            pImage->pszTargetName = NULL;
-        }
-        if (pImage->pszInitiatorName)
-        {
-            if (pImage->fAutomaticInitiatorName)
-                RTStrFree(pImage->pszInitiatorName);
-            else
-                RTMemFree(pImage->pszInitiatorName);
-            pImage->pszInitiatorName = NULL;
-        }
-        if (pImage->pszInitiatorUsername)
-        {
-            RTMemFree(pImage->pszInitiatorUsername);
-            pImage->pszInitiatorUsername = NULL;
-        }
-        if (pImage->pbInitiatorSecret)
-        {
-            RTMemFree(pImage->pbInitiatorSecret);
-            pImage->pbInitiatorSecret = NULL;
-        }
-        if (pImage->pszTargetUsername)
-        {
-            RTMemFree(pImage->pszTargetUsername);
-            pImage->pszTargetUsername = NULL;
-        }
-        if (pImage->pbTargetSecret)
-        {
-            RTMemFree(pImage->pbTargetSecret);
-            pImage->pbTargetSecret = NULL;
-        }
-        if (pImage->pvRecvPDUBuf)
-        {
-            RTMemFree(pImage->pvRecvPDUBuf);
-            pImage->pvRecvPDUBuf = NULL;
-        }
-
-        pImage->cbRecvPDUResidual = 0;
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Internal: Open an image, constructing all necessary data structures.
- */
-static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
-{
-    int rc;
-    char *pszLUN = NULL, *pszLUNInitial = NULL;
-    bool fLunEncoded = false;
-    uint32_t uWriteSplitDef = 0;
-    uint32_t uTimeoutDef = 0;
-    uint64_t uHostIPTmp = 0;
-    bool fHostIPDef = 0;
-    rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
-    AssertRC(rc);
-    rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
-    AssertRC(rc);
-    rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uHostIPTmp);
-    AssertRC(rc);
-    fHostIPDef = !!uHostIPTmp;
-
-    pImage->uOpenFlags      = uOpenFlags;
-
-    /* Get error signalling interface. */
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get TCP network stack interface. */
-    pImage->pInterfaceNet = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_TCPNET);
-    if (pImage->pInterfaceNet)
-        pImage->pInterfaceNetCallbacks = VDGetInterfaceTcpNet(pImage->pInterfaceNet);
-    else
-    {
-        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
-                        RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
-        goto out;
-    }
-
-    /* Get configuration interface. */
-    pImage->pInterfaceConfig = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_CONFIG);
-    if (pImage->pInterfaceConfig)
-        pImage->pInterfaceConfigCallbacks = VDGetInterfaceConfig(pImage->pInterfaceConfig);
-    else
-    {
-        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
-                        RT_SRC_POS, N_("iSCSI: configuration interface missing"));
-        goto out;
-    }
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIo = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    if (pImage->pInterfaceIo)
-        pImage->pInterfaceIoCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIo);
-    else
-    {
-        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
-                        RT_SRC_POS, N_("iSCSI: I/O interface missing"));
-        goto out;
-    }
-
-    /* This ISID will be adjusted later to make it unique on this host. */
-    pImage->ISID            = 0x800000000000ULL | 0x001234560000ULL;
-    pImage->cISCSIRetries   = 10;
-    pImage->state           = ISCSISTATE_FREE;
-    pImage->pvRecvPDUBuf    = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
-    pImage->cbRecvPDUBuf    = ISCSI_RECV_PDU_BUFFER_SIZE;
-    if (pImage->pvRecvPDUBuf == NULL)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->Mutex           = NIL_RTSEMMUTEX;
-    pImage->MutexReqQueue   = NIL_RTSEMMUTEX;
-    rc = RTSemMutexCreate(&pImage->Mutex);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = RTSemMutexCreate(&pImage->MutexReqQueue);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Validate configuration, detect unknown keys. */
-    if (!VDCFGAreKeysValid(pImage->pInterfaceConfigCallbacks,
-                           pImage->pInterfaceConfig->pvUser,
-                           "TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0WriteSplit\0Timeout\0HostIPStack\0"))
-    {
-        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
-        goto out;
-    }
-
-    /* Query the iSCSI upper level configuration. */
-    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
-                               pImage->pInterfaceConfig->pvUser,
-                               "TargetName", &pImage->pszTargetName);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
-        goto out;
-    }
-    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
-                               pImage->pInterfaceConfig->pvUser,
-                               "InitiatorName", &pImage->pszInitiatorName);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-    {
-        pImage->fAutomaticInitiatorName = true;
-        rc = VINF_SUCCESS;
-    }
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
-        goto out;
-    }
-    rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
-                                  pImage->pInterfaceConfig->pvUser,
-                                  "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
-        goto out;
-    }
-    pszLUNInitial = pszLUN;
-    if (!strncmp(pszLUN, "enc", 3))
-    {
-        fLunEncoded = true;
-        pszLUN += 3;
-    }
-    rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
-        goto out;
-    }
-    if (!fLunEncoded)
-    {
-        if (pImage->LUN <= 255)
-        {
-            pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
-        }
-        else if (pImage->LUN <= 16383)
-        {
-            pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
-        }
-        else
-        {
-            rc = VERR_OUT_OF_RANGE;
-            rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
-            goto out;
-        }
-    }
-    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
-                               pImage->pInterfaceConfig->pvUser,
-                               "TargetAddress", &pImage->pszTargetAddress);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
-        goto out;
-    }
-    pImage->pszInitiatorUsername = NULL;
-    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
-                               pImage->pInterfaceConfig->pvUser,
-                               "InitiatorUsername",
-                               &pImage->pszInitiatorUsername);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
-        goto out;
-    }
-    pImage->pbInitiatorSecret = NULL;
-    pImage->cbInitiatorSecret = 0;
-    rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
-                              pImage->pInterfaceConfig->pvUser,
-                              "InitiatorSecret",
-                              (void **)&pImage->pbInitiatorSecret,
-                              &pImage->cbInitiatorSecret);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
-        goto out;
-    }
-    pImage->pszTargetUsername = NULL;
-    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
-                               pImage->pInterfaceConfig->pvUser,
-                               "TargetUsername",
-                               &pImage->pszTargetUsername);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
-        goto out;
-    }
-    pImage->pbTargetSecret = NULL;
-    pImage->cbTargetSecret = 0;
-    rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
-                              pImage->pInterfaceConfig->pvUser,
-                              "TargetSecret", (void **)&pImage->pbTargetSecret,
-                              &pImage->cbTargetSecret);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
-        rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
-        goto out;
-    }
-    rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
-                          pImage->pInterfaceConfig->pvUser,
-                          "WriteSplit", &pImage->cbWriteSplit,
-                          uWriteSplitDef);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
-        goto out;
-    }
-
-    pImage->pszHostname    = NULL;
-    pImage->uPort          = 0;
-    pImage->Socket         = NIL_VDSOCKET;
-    /* Query the iSCSI lower level configuration. */
-    rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
-                          pImage->pInterfaceConfig->pvUser,
-                          "Timeout", &pImage->uReadTimeout,
-                          uTimeoutDef);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
-        goto out;
-    }
-    rc = VDCFGQueryBoolDef(pImage->pInterfaceConfigCallbacks,
-                           pImage->pInterfaceConfig->pvUser,
-                           "HostIPStack", &pImage->fHostIP,
-                           fHostIPDef);
-    if (RT_FAILURE(rc))
-    {
-        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
-        goto out;
-    }
-
-    /* Don't actually establish iSCSI transport connection if this is just an
-     * open to query the image information and the host IP stack isn't used.
-     * Even trying is rather useless, as in this context the InTnet IP stack
-     * isn't present. Returning dummies is the best possible result anyway. */
-    if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
-    {
-        LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
-        goto out;
-    }
-
-    memset(pImage->aCmdsWaiting, 0, sizeof(pImage->aCmdsWaiting));
-    pImage->cbRecvPDUResidual = 0;
-
-    /* Create the socket structure. */
-    rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT,
-                                                         &pImage->Socket);
-    if (RT_SUCCESS(rc))
-    {
-        pImage->fExtendedSelectSupported = true;
-        pImage->fRunning = true;
-        rc = RTThreadCreate(&pImage->hThreadIo, iscsiIoThreadWorker, pImage, 0,
-                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "iSCSI-Io");
-        if (RT_FAILURE(rc))
-        {
-            LogFunc(("Creating iSCSI I/O thread failed rc=%Rrc\n", rc));
-            goto out;
-        }
-    }
-    else if (rc == VERR_NOT_SUPPORTED)
-    {
-        /* Async I/O is not supported without extended select. */
-        if ((uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
-        {
-            LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
-            goto out;
-        }
-        else
-        {
-            pImage->fExtendedSelectSupported = false;
-            rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(0, &pImage->Socket);
-            if (RT_FAILURE(rc))
-            {
-                LogFunc(("Creating socket failed -> %Rrc\n", rc));
-                goto out;
-            }
-        }
-    }
-    else
-    {
-        LogFunc(("Creating socket failed -> %Rrc\n", rc));
-        goto out;
-    }
-
-    /*
-     * Attach to the iSCSI target. This implicitly establishes the iSCSI
-     * transport connection.
-     */
-    rc = iscsiExecSync(pImage, iscsiAttach, pImage);
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
-        goto out;
-    }
-    LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
-
-    SCSIREQ sr;
-    RTSGSEG DataSeg;
-    uint8_t sense[96];
-    uint8_t data8[8];
-    uint8_t data12[12];
-
-    /*
-     * Inquire available LUNs - purely dummy request.
-     */
-    uint8_t CDB_rlun[12];
-    uint8_t rlundata[16];
-    CDB_rlun[0] = SCSI_REPORT_LUNS;
-    CDB_rlun[1] = 0;        /* reserved */
-    CDB_rlun[2] = 0;        /* reserved */
-    CDB_rlun[3] = 0;        /* reserved */
-    CDB_rlun[4] = 0;        /* reserved */
-    CDB_rlun[5] = 0;        /* reserved */
-    CDB_rlun[6] = sizeof(rlundata) >> 24;
-    CDB_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
-    CDB_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
-    CDB_rlun[9] = sizeof(rlundata) & 0xff;
-    CDB_rlun[10] = 0;       /* reserved */
-    CDB_rlun[11] = 0;       /* control */
-
-    DataSeg.pvSeg = rlundata;
-    DataSeg.cbSeg = sizeof(rlundata);
-
-    sr.enmXfer = SCSIXFER_FROM_TARGET;
-    sr.cbCDB = sizeof(CDB_rlun);
-    sr.pvCDB = CDB_rlun;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = DataSeg.cbSeg;
-    sr.paT2ISegs = &DataSeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense   = sizeof(sense);
-    sr.pvSense   = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
-    if (RT_FAILURE(rc))
-    {
-        LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
-        return rc;
-    }
-
-    /*
-     * Inquire device characteristics - no tapes, scanners etc., please.
-     */
-    uint8_t CDB_inq[6];
-    CDB_inq[0] = SCSI_INQUIRY;
-    CDB_inq[1] = 0;         /* reserved */
-    CDB_inq[2] = 0;         /* reserved */
-    CDB_inq[3] = 0;         /* reserved */
-    CDB_inq[4] = sizeof(data8);
-    CDB_inq[5] = 0;         /* control */
-
-    DataSeg.pvSeg = data8;
-    DataSeg.cbSeg = sizeof(data8);
-
-    sr.enmXfer = SCSIXFER_FROM_TARGET;
-    sr.cbCDB = sizeof(CDB_inq);
-    sr.pvCDB = CDB_inq;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = DataSeg.cbSeg;
-    sr.paT2ISegs = &DataSeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense = sizeof(sense);
-    sr.pvSense = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
-    if (RT_SUCCESS(rc))
-    {
-        uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
-        if (devType != SCSI_DEVTYPE_DISK)
-        {
-            rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
-                            RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
-                            pImage->pszTargetAddress, pImage->pszTargetName,
-                            pImage->LUN, devType);
-            LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
-            goto out;
-        }
-        uint8_t uCmdQueue = (sr.cbT2IData >= 8) ? data8[7] & SCSI_INQUIRY_CMDQUE_MASK : 0;
-        if (uCmdQueue > 0)
-            pImage->fCmdQueuingSupported = true;
-        else if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
-        {
-            rc = VERR_NOT_SUPPORTED;
-            goto out;
-        }
-
-        LogRel(("iSCSI: target address %s, target name %s, %s command queuing\n",
-                pImage->pszTargetAddress, pImage->pszTargetName,
-                pImage->fCmdQueuingSupported ? "supports" : "doesn't support"));
-    }
-    else
-    {
-        LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
-        goto out;
-    }
-
-    /*
-     * Query write disable bit in the device specific parameter entry in the
-     * mode parameter header. Refuse read/write opening of read only disks.
-     */
-
-    uint8_t CDB_ms[6];
-    uint8_t data4[4];
-    CDB_ms[0] = SCSI_MODE_SENSE_6;
-    CDB_ms[1] = 0;          /* dbd=0/reserved */
-    CDB_ms[2] = 0x3f;       /* pc=0/page code=0x3f, ask for all pages */
-    CDB_ms[3] = 0;          /* subpage code=0, return everything in page_0 format */
-    CDB_ms[4] = sizeof(data4); /* allocation length=4 */
-    CDB_ms[5] = 0;          /* control */
-
-    DataSeg.pvSeg = data4;
-    DataSeg.cbSeg = sizeof(data4);
-
-    sr.enmXfer = SCSIXFER_FROM_TARGET;
-    sr.cbCDB = sizeof(CDB_ms);
-    sr.pvCDB = CDB_ms;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = DataSeg.cbSeg;
-    sr.paT2ISegs = &DataSeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense = sizeof(sense);
-    sr.pvSense = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
-    if (RT_SUCCESS(rc))
-    {
-        if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-    }
-    else
-    {
-        LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
-        goto out;
-    }
-
-    /*
-     * Determine sector size and capacity of the volume immediately.
-     */
-    uint8_t CDB_cap[16];
-
-    RT_ZERO(data12);
-    RT_ZERO(CDB_cap);
-    CDB_cap[0] = SCSI_SERVICE_ACTION_IN_16;
-    CDB_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16;   /* subcommand */
-    CDB_cap[10+3] = sizeof(data12);                     /* allocation length (dword) */
-
-    DataSeg.pvSeg = data12;
-    DataSeg.cbSeg = sizeof(data12);
-
-    sr.enmXfer = SCSIXFER_FROM_TARGET;
-    sr.cbCDB = sizeof(CDB_cap);
-    sr.pvCDB = CDB_cap;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = DataSeg.cbSeg;
-    sr.paT2ISegs = &DataSeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense = sizeof(sense);
-    sr.pvSense = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
-    if (   RT_SUCCESS(rc)
-        && sr.status == SCSI_STATUS_OK)
-    {
-        pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
-        pImage->cVolume++;
-        pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
-        pImage->cbSize = pImage->cVolume * pImage->cbSector;
-        if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
-        {
-            rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
-                            RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
-                            pImage->pszTargetAddress, pImage->pszTargetName,
-                            pImage->LUN, pImage->cVolume, pImage->cbSector);
-        }
-    }
-    else
-    {
-        uint8_t CDB_capfb[10];
-
-        RT_ZERO(data8);
-        CDB_capfb[0] = SCSI_READ_CAPACITY;
-        CDB_capfb[1] = 0;   /* reserved */
-        CDB_capfb[2] = 0;   /* reserved */
-        CDB_capfb[3] = 0;   /* reserved */
-        CDB_capfb[4] = 0;   /* reserved */
-        CDB_capfb[5] = 0;   /* reserved */
-        CDB_capfb[6] = 0;   /* reserved */
-        CDB_capfb[7] = 0;   /* reserved */
-        CDB_capfb[8] = 0;   /* reserved */
-        CDB_capfb[9] = 0;   /* control */
-
-        DataSeg.pvSeg = data8;
-        DataSeg.cbSeg = sizeof(data8);
-
-        sr.enmXfer = SCSIXFER_FROM_TARGET;
-        sr.cbCDB = sizeof(CDB_capfb);
-        sr.pvCDB = CDB_capfb;
-        sr.cbI2TData = 0;
-        sr.paI2TSegs = NULL;
-        sr.cI2TSegs  = 0;
-        sr.cbT2IData = DataSeg.cbSeg;
-        sr.paT2ISegs = &DataSeg;
-        sr.cT2ISegs  = 1;
-        sr.cbSense = sizeof(sense);
-        sr.pvSense = sense;
-
-        rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
-        if (RT_SUCCESS(rc))
-        {
-            pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
-            pImage->cVolume++;
-            pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
-            pImage->cbSize = pImage->cVolume * pImage->cbSector;
-            if (pImage->cVolume == 0 || pImage->cbSector != 512)
-            {
-                rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
-                                RT_SRC_POS, N_("iSCSI: fallback capacity detectio for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
-                                pImage->pszTargetAddress, pImage->pszTargetName,
-                                pImage->LUN, pImage->cVolume, pImage->cbSector);
-            }
-        }
-        else
-        {
-            LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
-            goto out;
-        }
-    }
-
-    /*
-     * Check the read and write cache bits.
-     * Try to enable the cache if it is disabled.
-     *
-     * We already checked that this is a block access device. No need
-     * to do it again.
-     */
-    uint8_t aCachingModePage[32];
-    uint8_t aCDBModeSense6[6];
-
-    memset(aCachingModePage, '\0', sizeof(aCachingModePage));
-    aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
-    aCDBModeSense6[1] = 0;
-    aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
-    aCDBModeSense6[3] = 0; /* Sub page code. */
-    aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
-    aCDBModeSense6[5] = 0;
-
-    DataSeg.pvSeg = aCachingModePage;
-    DataSeg.cbSeg = sizeof(aCachingModePage);
-
-    sr.enmXfer = SCSIXFER_FROM_TARGET;
-    sr.cbCDB = sizeof(aCDBModeSense6);
-    sr.pvCDB = aCDBModeSense6;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = DataSeg.cbSeg;
-    sr.paT2ISegs = &DataSeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense = sizeof(sense);
-    sr.pvSense = sense;
-    rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
-    if (   RT_SUCCESS(rc)
-        && (sr.status == SCSI_STATUS_OK)
-        && (aCachingModePage[0] >= 15)
-        && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
-        && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
-    {
-        uint32_t Offset = 4 + aCachingModePage[3];
-        /*
-         * Check if the read and/or the write cache is disabled.
-         * The write cache is disabled if bit 2 (WCE) is zero and
-         * the read cache is disabled if bit 0 (RCD) is set.
-         */
-        if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
-        {
-            /*
-             * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
-             * So one of the caches is disabled. Enable both caches.
-             * The rest is unchanged.
-             */
-            ASMBitSet(&aCachingModePage[Offset + 2], 2);
-            ASMBitClear(&aCachingModePage[Offset + 2], 0);
-
-            uint8_t aCDBCaching[6];
-            aCDBCaching[0] = SCSI_MODE_SELECT_6;
-            aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
-            aCDBCaching[2] = 0;
-            aCDBCaching[3] = 0;
-            aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
-            aCDBCaching[5] = 0;
-
-            DataSeg.pvSeg = aCachingModePage;
-            DataSeg.cbSeg = sizeof(aCachingModePage);
-
-            sr.enmXfer = SCSIXFER_TO_TARGET;
-            sr.cbCDB = sizeof(aCDBCaching);
-            sr.pvCDB = aCDBCaching;
-            sr.cbI2TData = DataSeg.cbSeg;
-            sr.paI2TSegs = &DataSeg;
-            sr.cI2TSegs  = 1;
-            sr.cbT2IData = 0;
-            sr.paT2ISegs = NULL;
-            sr.cT2ISegs  = 0;
-            sr.cbSense = sizeof(sense);
-            sr.pvSense = sense;
-            sr.status  = 0;
-            rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
-            if (   RT_SUCCESS(rc)
-                && (sr.status == SCSI_STATUS_OK))
-            {
-                LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
-            }
-            else
-            {
-                /* Log failures but continue. */
-                LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
-                        pImage->pszTargetName, rc, sr.status));
-                LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
-                rc = VINF_SUCCESS;
-            }
-        }
-    }
-    else
-    {
-        /* Log errors but continue. */
-        LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
-        LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
-        rc = VINF_SUCCESS;
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        iscsiFreeImage(pImage, false);
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                             PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
-
-    /* iSCSI images can't be checked for validity this way, as the filename
-     * just can't supply enough configuration information. */
-    int rc = VERR_VD_ISCSI_INVALID_HEADER;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PISCSIIMAGE pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename
-        || strchr(pszFilename, '"'))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    pImage->pszFilename = pszFilename;
-    pImage->pszInitiatorName = NULL;
-    pImage->pszTargetName = NULL;
-    pImage->pszTargetAddress = NULL;
-    pImage->pszInitiatorUsername = NULL;
-    pImage->pbInitiatorSecret = NULL;
-    pImage->pszTargetUsername = NULL;
-    pImage->pbTargetSecret = NULL;
-    pImage->paCurrReq = NULL;
-    pImage->pvRecvPDUBuf = NULL;
-    pImage->pszHostname = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = iscsiOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-    {
-        LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
-        LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
-        *ppBackendData = pImage;
-    }
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
-                       unsigned uImageFlags, const char *pszComment,
-                       PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                       PCRTUUID pUuid, unsigned uOpenFlags,
-                       unsigned uPercentStart, unsigned uPercentSpan,
-                       PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                       PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc = VERR_NOT_SUPPORTED;
-
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int iscsiClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(!fDelete);   /* This flag is unsupported. */
-
-    rc = iscsiFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                     size_t cbToRead, size_t *pcbActuallyRead)
-{
-    /** @todo reinstate logging of the target everywhere - dropped temporarily */
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    uint64_t lba;
-    uint16_t tls;
-    int rc;
-
-    Assert(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToRead % 512 == 0);
-
-    Assert(pImage->cbSector);
-    AssertPtr(pvBuf);
-
-    if (   uOffset + cbToRead > pImage->cbSize
-        || cbToRead == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /*
-     * Clip read size to a value which is supported by the target.
-     */
-    cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
-
-    lba = uOffset / pImage->cbSector;
-    tls = (uint16_t)(cbToRead / pImage->cbSector);
-    SCSIREQ sr;
-    RTSGSEG T2ISeg;
-    size_t cbCDB;
-    uint8_t abCDB[16];
-    uint8_t sense[96];
-
-    if (pImage->cVolume < _4G)
-    {
-        cbCDB = 10;
-        abCDB[0] = SCSI_READ_10;
-        abCDB[1] = 0;       /* reserved */
-        abCDB[2] = (lba >> 24) & 0xff;
-        abCDB[3] = (lba >> 16) & 0xff;
-        abCDB[4] = (lba >> 8) & 0xff;
-        abCDB[5] = lba & 0xff;
-        abCDB[6] = 0;       /* reserved */
-        abCDB[7] = (tls >> 8) & 0xff;
-        abCDB[8] = tls & 0xff;
-        abCDB[9] = 0;       /* control */
-    }
-    else
-    {
-        cbCDB = 16;
-        abCDB[0] = SCSI_READ_16;
-        abCDB[1] = 0;       /* reserved */
-        abCDB[2] = (lba >> 56) & 0xff;
-        abCDB[3] = (lba >> 48) & 0xff;
-        abCDB[4] = (lba >> 40) & 0xff;
-        abCDB[5] = (lba >> 32) & 0xff;
-        abCDB[6] = (lba >> 24) & 0xff;
-        abCDB[7] = (lba >> 16) & 0xff;
-        abCDB[8] = (lba >> 8) & 0xff;
-        abCDB[9] = lba & 0xff;
-        abCDB[10] = 0;      /* tls unused */
-        abCDB[11] = 0;      /* tls unused */
-        abCDB[12] = (tls >> 8) & 0xff;
-        abCDB[13] = tls & 0xff;
-        abCDB[14] = 0;      /* reserved */
-        abCDB[15] = 0;      /* reserved */
-    }
-
-    T2ISeg.pvSeg = pvBuf;
-    T2ISeg.cbSeg = cbToRead;
-
-    sr.enmXfer   = SCSIXFER_FROM_TARGET;
-    sr.cbCDB     = cbCDB;
-    sr.pvCDB     = abCDB;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = cbToRead;
-    sr.paT2ISegs = &T2ISeg;
-    sr.cT2ISegs  = 1;
-    sr.cbSense   = sizeof(sense);
-    sr.pvSense   = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, true, VERR_READ_ERROR);
-    if (RT_FAILURE(rc))
-    {
-        LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
-        *pcbActuallyRead = 0;
-    }
-    else
-        *pcbActuallyRead = sr.cbT2IData;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                     size_t cbToWrite, size_t *pcbWriteProcess,
-                     size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-     LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    uint64_t lba;
-    uint16_t tls;
-    int rc;
-
-    Assert(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    Assert(pImage->cbSector);
-    Assert(pvBuf);
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    *pcbPreRead = 0;
-    *pcbPostRead = 0;
-
-    /*
-     * Clip write size to a value which is supported by the target.
-     */
-    cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
-
-    lba = uOffset / pImage->cbSector;
-    tls = (uint16_t)(cbToWrite / pImage->cbSector);
-    SCSIREQ sr;
-    RTSGSEG I2TSeg;
-    size_t cbCDB;
-    uint8_t abCDB[16];
-    uint8_t sense[96];
-
-    if (pImage->cVolume < _4G)
-    {
-        cbCDB = 10;
-        abCDB[0] = SCSI_WRITE_10;
-        abCDB[1] = 0;       /* reserved */
-        abCDB[2] = (lba >> 24) & 0xff;
-        abCDB[3] = (lba >> 16) & 0xff;
-        abCDB[4] = (lba >> 8) & 0xff;
-        abCDB[5] = lba & 0xff;
-        abCDB[6] = 0;       /* reserved */
-        abCDB[7] = (tls >> 8) & 0xff;
-        abCDB[8] = tls & 0xff;
-        abCDB[9] = 0;       /* control */
-    }
-    else
-    {
-        cbCDB = 16;
-        abCDB[0] = SCSI_WRITE_16;
-        abCDB[1] = 0;       /* reserved */
-        abCDB[2] = (lba >> 56) & 0xff;
-        abCDB[3] = (lba >> 48) & 0xff;
-        abCDB[4] = (lba >> 40) & 0xff;
-        abCDB[5] = (lba >> 32) & 0xff;
-        abCDB[6] = (lba >> 24) & 0xff;
-        abCDB[7] = (lba >> 16) & 0xff;
-        abCDB[8] = (lba >> 8) & 0xff;
-        abCDB[9] = lba & 0xff;
-        abCDB[10] = 0;      /* tls unused */
-        abCDB[11] = 0;      /* tls unused */
-        abCDB[12] = (tls >> 8) & 0xff;
-        abCDB[13] = tls & 0xff;
-        abCDB[14] = 0;      /* reserved */
-        abCDB[15] = 0;      /* reserved */
-    }
-
-    I2TSeg.pvSeg = (void *)pvBuf;
-    I2TSeg.cbSeg = cbToWrite;
-
-    sr.enmXfer              = SCSIXFER_TO_TARGET;
-    sr.cbCDB                = cbCDB;
-    sr.pvCDB                = abCDB;
-    sr.cbI2TData            = cbToWrite;
-    sr.paI2TSegs            = &I2TSeg;
-    sr.cI2TSegs             = 1;
-    sr.cbT2IData            = 0;
-    sr.paT2ISegs            = NULL;
-    sr.cT2ISegs             = 0;
-    sr.cbSense              = sizeof(sense);
-    sr.pvSense              = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, true, VERR_WRITE_ERROR);
-    if (RT_FAILURE(rc))
-    {
-        LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
-        *pcbWriteProcess = 0;
-    }
-    else
-        *pcbWriteProcess = cbToWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int iscsiFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    SCSIREQ sr;
-    uint8_t abCDB[10];
-    uint8_t sense[96];
-
-    abCDB[0] = SCSI_SYNCHRONIZE_CACHE;
-    abCDB[1] = 0;       /* reserved */
-    abCDB[2] = 0;       /* LBA 0 */
-    abCDB[3] = 0;       /* LBA 0 */
-    abCDB[4] = 0;       /* LBA 0 */
-    abCDB[5] = 0;       /* LBA 0 */
-    abCDB[6] = 0;       /* reserved */
-    abCDB[7] = 0;       /* transfer everything to disk */
-    abCDB[8] = 0;       /* transfer everything to disk */
-    abCDB[9] = 0;       /* control */
-
-    sr.enmXfer   = SCSIXFER_NONE;
-    sr.cbCDB     = sizeof(abCDB);
-    sr.pvCDB     = abCDB;
-    sr.cbI2TData = 0;
-    sr.paI2TSegs = NULL;
-    sr.cI2TSegs  = 0;
-    sr.cbT2IData = 0;
-    sr.paT2ISegs = NULL;
-    sr.cT2ISegs  = 0;
-    sr.cbSense   = sizeof(sense);
-    sr.pvSense   = sense;
-
-    rc = iscsiCommandSync(pImage, &sr, false, VINF_SUCCESS);
-    if (RT_FAILURE(rc))
-        AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned iscsiGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-
-    Assert(pImage);
-    NOREF(pImage);
-
-    return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t iscsiGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-
-    Assert(pImage);
-
-    if (pImage)
-        return pImage->cbSize;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t iscsiGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-
-    Assert(pImage);
-    NOREF(pImage);
-
-    if (pImage)
-        return pImage->cbSize;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int iscsiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_VD_GEOMETRY_NOT_SET;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int iscsiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-        rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int iscsiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_VD_GEOMETRY_NOT_SET;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int iscsiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-        rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned iscsiGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    Assert(pImage);
-    NOREF(pImage);
-
-    uImageFlags = VD_IMAGE_FLAGS_FIXED;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned iscsiGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    Assert(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image if we actually need
-     * to do something. A read/write -> readonly transition doesn't need a
-     * reopen. In the other direction we don't have the necessary information
-     * as the "disk is readonly" flag is thrown away. Can be optimized too,
-     * but it's not worth the effort at the moment. */
-    if (   !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        && (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-    {
-        iscsiFreeImage(pImage, false);
-        rc = iscsiOpenImage(pImage, uOpenFlags);
-    }
-    else
-    {
-        pImage->uOpenFlags = uOpenFlags;
-        rc = VINF_SUCCESS;
-    }
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int iscsiGetComment(void *pBackendData, char *pszComment,
-                          size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int iscsiSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    Assert(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    Assert(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void iscsiDump(void *pBackendData)
-{
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-
-    Assert(pImage);
-    if (pImage)
-    {
-        /** @todo put something useful here */
-        iscsiMessage(pImage, "Header: cVolume=%u\n", pImage->cVolume);
-    }
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
-static bool iscsiIsAsyncIOSupported(void *pBackendData)
-{
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    return pImage->fCmdQueuingSupported;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int iscsiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
-                          PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
-                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    if (uOffset + cbToRead > pImage->cbSize)
-        return VERR_INVALID_PARAMETER;
-
-    /*
-     * Clip read size to a value which is supported by the target.
-     */
-    cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
-
-    unsigned cT2ISegs = 0;
-    size_t   cbSegs = 0;
-
-    /* Get the number of segments. */
-    cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
-                                                                   NULL, &cT2ISegs, cbToRead);
-    Assert(cbSegs == cbToRead);
-
-    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cT2ISegs]));
-    if (RT_LIKELY(pReqAsync))
-    {
-        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
-        if (pReq)
-        {
-            uint64_t lba;
-            uint16_t tls;
-            uint8_t *pbCDB = &pReqAsync->abCDB[0];
-            size_t cbCDB;
-
-            lba = uOffset / pImage->cbSector;
-            tls = (uint16_t)(cbToRead / pImage->cbSector);
-
-            cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
-                                                                           &pReqAsync->aSegs[0],
-                                                                           &cT2ISegs, cbToRead);
-            Assert(cbSegs == cbToRead);
-            pReqAsync->cT2ISegs = cT2ISegs;
-            pReqAsync->pIoCtx = pIoCtx;
-            pReqAsync->pScsiReq = pReq;
-            pReqAsync->cSenseRetries = 10;
-            pReqAsync->rcSense       = VERR_READ_ERROR;
-
-            if (pImage->cVolume < _4G)
-            {
-                cbCDB = 10;
-                pbCDB[0] = SCSI_READ_10;
-                pbCDB[1] = 0;       /* reserved */
-                pbCDB[2] = (lba >> 24) & 0xff;
-                pbCDB[3] = (lba >> 16) & 0xff;
-                pbCDB[4] = (lba >> 8) & 0xff;
-                pbCDB[5] = lba & 0xff;
-                pbCDB[6] = 0;       /* reserved */
-                pbCDB[7] = (tls >> 8) & 0xff;
-                pbCDB[8] = tls & 0xff;
-                pbCDB[9] = 0;       /* control */
-            }
-            else
-            {
-                cbCDB = 16;
-                pbCDB[0] = SCSI_READ_16;
-                pbCDB[1] = 0;       /* reserved */
-                pbCDB[2] = (lba >> 56) & 0xff;
-                pbCDB[3] = (lba >> 48) & 0xff;
-                pbCDB[4] = (lba >> 40) & 0xff;
-                pbCDB[5] = (lba >> 32) & 0xff;
-                pbCDB[6] = (lba >> 24) & 0xff;
-                pbCDB[7] = (lba >> 16) & 0xff;
-                pbCDB[8] = (lba >> 8) & 0xff;
-                pbCDB[9] = lba & 0xff;
-                pbCDB[10] = 0;      /* tls unused */
-                pbCDB[11] = 0;      /* tls unused */
-                pbCDB[12] = (tls >> 8) & 0xff;
-                pbCDB[13] = tls & 0xff;
-                pbCDB[14] = 0;      /* reserved */
-                pbCDB[15] = 0;      /* reserved */
-            }
-
-            pReq->enmXfer = SCSIXFER_FROM_TARGET;
-            pReq->cbCDB   = cbCDB;
-            pReq->pvCDB   = pReqAsync->abCDB;
-            pReq->cbI2TData = 0;
-            pReq->paI2TSegs = NULL;
-            pReq->cI2TSegs  = 0;
-            pReq->cbT2IData = cbToRead;
-            pReq->paT2ISegs = &pReqAsync->aSegs[pReqAsync->cI2TSegs];
-            pReq->cT2ISegs  = pReqAsync->cT2ISegs;
-            pReq->cbSense = sizeof(pReqAsync->abSense);
-            pReq->pvSense = pReqAsync->abSense;
-
-            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
-            if (RT_FAILURE(rc))
-                AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
-            else
-            {
-                *pcbActuallyRead = cbToRead;
-                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
-            }
-
-            RTMemFree(pReq);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-
-        RTMemFree(pReqAsync);
-    }
-    else
-        rc = VERR_NO_MEMORY;
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int iscsiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
-                           PVDIOCTX pIoCtx,
-                           size_t *pcbWriteProcess, size_t *pcbPreRead,
-                           size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
-                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    if (uOffset + cbToWrite > pImage->cbSize)
-        return VERR_INVALID_PARAMETER;
-
-    /*
-     * Clip read size to a value which is supported by the target.
-     */
-    cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
-
-    unsigned cI2TSegs = 0;
-    size_t   cbSegs = 0;
-
-    /* Get the number of segments. */
-    cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
-                                                                   NULL, &cI2TSegs, cbToWrite);
-    Assert(cbSegs == cbToWrite);
-
-    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cI2TSegs]));
-    if (RT_LIKELY(pReqAsync))
-    {
-        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
-        if (pReq)
-        {
-            uint64_t lba;
-            uint16_t tls;
-            uint8_t *pbCDB = &pReqAsync->abCDB[0];
-            size_t cbCDB;
-
-            lba = uOffset / pImage->cbSector;
-            tls = (uint16_t)(cbToWrite / pImage->cbSector);
-
-            cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
-                                                                           &pReqAsync->aSegs[0],
-                                                                           &cI2TSegs, cbToWrite);
-            Assert(cbSegs == cbToWrite);
-            pReqAsync->cI2TSegs = cI2TSegs;
-            pReqAsync->pIoCtx = pIoCtx;
-            pReqAsync->pScsiReq = pReq;
-            pReqAsync->cSenseRetries = 10;
-            pReqAsync->rcSense       = VERR_WRITE_ERROR;
-
-            if (pImage->cVolume < _4G)
-            {
-                cbCDB = 10;
-                pbCDB[0] = SCSI_WRITE_10;
-                pbCDB[1] = 0;       /* reserved */
-                pbCDB[2] = (lba >> 24) & 0xff;
-                pbCDB[3] = (lba >> 16) & 0xff;
-                pbCDB[4] = (lba >> 8) & 0xff;
-                pbCDB[5] = lba & 0xff;
-                pbCDB[6] = 0;       /* reserved */
-                pbCDB[7] = (tls >> 8) & 0xff;
-                pbCDB[8] = tls & 0xff;
-                pbCDB[9] = 0;       /* control */
-            }
-            else
-            {
-                cbCDB = 16;
-                pbCDB[0] = SCSI_WRITE_16;
-                pbCDB[1] = 0;       /* reserved */
-                pbCDB[2] = (lba >> 56) & 0xff;
-                pbCDB[3] = (lba >> 48) & 0xff;
-                pbCDB[4] = (lba >> 40) & 0xff;
-                pbCDB[5] = (lba >> 32) & 0xff;
-                pbCDB[6] = (lba >> 24) & 0xff;
-                pbCDB[7] = (lba >> 16) & 0xff;
-                pbCDB[8] = (lba >> 8) & 0xff;
-                pbCDB[9] = lba & 0xff;
-                pbCDB[10] = 0;      /* tls unused */
-                pbCDB[11] = 0;      /* tls unused */
-                pbCDB[12] = (tls >> 8) & 0xff;
-                pbCDB[13] = tls & 0xff;
-                pbCDB[14] = 0;      /* reserved */
-                pbCDB[15] = 0;      /* reserved */
-            }
-
-            pReq->enmXfer = SCSIXFER_TO_TARGET;
-            pReq->cbCDB   = cbCDB;
-            pReq->pvCDB   = pReqAsync->abCDB;
-            pReq->cbI2TData = cbToWrite;
-            pReq->paI2TSegs = &pReqAsync->aSegs[0];
-            pReq->cI2TSegs  = pReqAsync->cI2TSegs;
-            pReq->cbT2IData = 0;
-            pReq->paT2ISegs = NULL;
-            pReq->cT2ISegs  = 0;
-            pReq->cbSense = sizeof(pReqAsync->abSense);
-            pReq->pvSense = pReqAsync->abSense;
-
-            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
-            if (RT_FAILURE(rc))
-                AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
-            else
-            {
-                *pcbWriteProcess = cbToWrite;
-                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
-            }
-
-            RTMemFree(pReq);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-
-        RTMemFree(pReqAsync);
-    }
-    else
-        rc = VERR_NO_MEMORY;
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int iscsiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
-    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(sizeof(SCSIREQASYNC));
-    if (RT_LIKELY(pReqAsync))
-    {
-        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
-        if (pReq)
-        {
-            uint8_t *pbCDB = &pReqAsync->abCDB[0];
-
-            pReqAsync->pIoCtx        = pIoCtx;
-            pReqAsync->pScsiReq      = pReq;
-            pReqAsync->cSenseRetries = 0;
-            pReqAsync->rcSense       = VINF_SUCCESS;
-
-            pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
-            pbCDB[1] = 0;         /* reserved */
-            pbCDB[2] = 0;         /* reserved */
-            pbCDB[3] = 0;         /* reserved */
-            pbCDB[4] = 0;         /* reserved */
-            pbCDB[5] = 0;         /* reserved */
-            pbCDB[6] = 0;         /* reserved */
-            pbCDB[7] = 0;         /* reserved */
-            pbCDB[8] = 0;         /* reserved */
-            pbCDB[9] = 0;         /* control */
-
-            pReq->enmXfer = SCSIXFER_NONE;
-            pReq->cbCDB   = 10;
-            pReq->pvCDB   = pReqAsync->abCDB;
-            pReq->cbI2TData = 0;
-            pReq->paI2TSegs = NULL;
-            pReq->cI2TSegs  = 0;
-            pReq->cbT2IData = 0;
-            pReq->paT2ISegs = NULL;
-            pReq->cT2ISegs  = 0;
-            pReq->cbSense = sizeof(pReqAsync->abSense);
-            pReq->pvSense = pReqAsync->abSense;
-
-            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
-            if (RT_FAILURE(rc))
-                AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
-            else
-                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
-
-            RTMemFree(pReq);
-        }
-        else
-            rc = VERR_NO_MEMORY;
-
-        RTMemFree(pReqAsync);
-    }
-    else
-        rc = VERR_NO_MEMORY;
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
-static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
-{
-    char *pszTarget  = NULL;
-    char *pszLUN     = NULL;
-    char *pszAddress = NULL;
-    int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
-    if (RT_SUCCESS(rc))
-    {
-        rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
-        if (RT_SUCCESS(rc))
-        {
-            rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
-            if (RT_SUCCESS(rc))
-            {
-                if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
-                                 pszAddress, pszTarget, pszLUN) < 0)
-                    rc = VERR_NO_MEMORY;
-            }
-        }
-    }
-    RTMemFree(pszTarget);
-    RTMemFree(pszLUN);
-    RTMemFree(pszAddress);
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnComposeName */
-static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
-{
-    char *pszTarget  = NULL;
-    char *pszLUN     = NULL;
-    char *pszAddress = NULL;
-    int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
-    if (RT_SUCCESS(rc))
-    {
-        rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
-        if (RT_SUCCESS(rc))
-        {
-            rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
-            if (RT_SUCCESS(rc))
-            {
-                /** @todo think about a nicer looking location scheme for iSCSI */
-                if (RTStrAPrintf(pszName, "%s/%s/%s",
-                                 pszAddress, pszTarget, pszLUN) < 0)
-                    rc = VERR_NO_MEMORY;
-            }
-        }
-    }
-    RTMemFree(pszTarget);
-    RTMemFree(pszLUN);
-    RTMemFree(pszAddress);
-
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_ISCSIBackend =
-{
-    /* pszBackendName */
-    "iSCSI",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-    VD_CAP_CONFIG | VD_CAP_TCPNET | VD_CAP_ASYNC,
-    /* papszFileExtensions */
-    NULL,
-    /* paConfigInfo */
-    s_iscsiConfigInfo,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    iscsiCheckIfValid,
-    /* pfnOpen */
-    iscsiOpen,
-    /* pfnCreate */
-    iscsiCreate,
-    /* pfnRename */
-    NULL,
-    /* pfnClose */
-    iscsiClose,
-    /* pfnRead */
-    iscsiRead,
-    /* pfnWrite */
-    iscsiWrite,
-    /* pfnFlush */
-    iscsiFlush,
-    /* pfnGetVersion */
-    iscsiGetVersion,
-    /* pfnGetSize */
-    iscsiGetSize,
-    /* pfnGetFileSize */
-    iscsiGetFileSize,
-    /* pfnGetPCHSGeometry */
-    iscsiGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    iscsiSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    iscsiGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    iscsiSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    iscsiGetImageFlags,
-    /* pfnGetOpenFlags */
-    iscsiGetOpenFlags,
-    /* pfnSetOpenFlags */
-    iscsiSetOpenFlags,
-    /* pfnGetComment */
-    iscsiGetComment,
-    /* pfnSetComment */
-    iscsiSetComment,
-    /* pfnGetUuid */
-    iscsiGetUuid,
-    /* pfnSetUuid */
-    iscsiSetUuid,
-    /* pfnGetModificationUuid */
-    iscsiGetModificationUuid,
-    /* pfnSetModificationUuid */
-    iscsiSetModificationUuid,
-    /* pfnGetParentUuid */
-    iscsiGetParentUuid,
-    /* pfnSetParentUuid */
-    iscsiSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    iscsiGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    iscsiSetParentModificationUuid,
-    /* pfnDump */
-    iscsiDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    iscsiIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    iscsiAsyncRead,
-    /* pfnAsyncWrite */
-    iscsiAsyncWrite,
-    /* pfnAsyncFlush */
-    iscsiAsyncFlush,
-    /* pfnComposeLocation */
-    iscsiComposeLocation,
-    /* pfnComposeName */
-    iscsiComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    NULL
-};
Index: unk/src/VBox/Devices/Storage/ParallelsHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/ParallelsHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,1451 +1,0 @@
-/* $Id$ */
-/** @file
- *
- * Parallels hdd disk image, core code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-#define LOG_GROUP LOG_GROUP_VD_PARALLELS
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/assert.h>
-#include <iprt/mem.h>
-#include <iprt/uuid.h>
-#include <iprt/path.h>
-#include <iprt/string.h>
-
-#define PARALLELS_HEADER_MAGIC "WithoutFreeSpace"
-#define PARALLELS_DISK_VERSION 2
-
-/** The header of the parallels disk. */
-#pragma pack(1)
-typedef struct ParallelsHeader
-{
-    /** The magic header to identify a parallels hdd image. */
-    char        HeaderIdentifier[16];
-    /** The version of the disk image. */
-    uint32_t    uVersion;
-    /** The number of heads the hdd has. */
-    uint32_t    cHeads;
-    /** Number of cylinders. */
-    uint32_t    cCylinders;
-    /** Number of sectors per track. */
-    uint32_t    cSectorsPerTrack;
-    /** Number of entries in the allocation bitmap. */
-    uint32_t    cEntriesInAllocationBitmap;
-    /** Total number of sectors. */
-    uint32_t    cSectors;
-    /** Padding. */
-    char        Padding[24];
-} ParallelsHeader;
-#pragma pack()
-
-/**
- * Parallels image structure.
- */
-typedef struct PARALLELSIMAGE
-{
-    /** Image file name. */
-    const char         *pszFilename;
-    /** Opaque storage handle. */
-    PVDIOSTORAGE        pStorage;
-
-    /** I/O interface. */
-    PVDINTERFACE        pInterfaceIO;
-    /** I/O interface callbacks. */
-    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE        pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE        pVDIfsImage;
-    /** Error interface. */
-    PVDINTERFACE        pInterfaceError;
-    /** Error interface callbacks. */
-    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
-
-    /** Open flags passed by VBoxHDD layer. */
-    unsigned            uOpenFlags;
-    /** Image flags defined during creation or determined during open. */
-    unsigned            uImageFlags;
-    /** Total size of the image. */
-    uint64_t            cbSize;
-
-    /** Physical geometry of this image. */
-    VDGEOMETRY          PCHSGeometry;
-    /** Logical geometry of this image. */
-    VDGEOMETRY          LCHSGeometry;
-
-    /** Pointer to the allocation bitmap. */
-    uint32_t           *pAllocationBitmap;
-    /** Entries in the allocation bitmap. */
-    uint64_t            cAllocationBitmapEntries;
-    /** Flag whether the allocation bitmap was changed. */
-    bool                fAllocationBitmapChanged;
-    /** Current file size. */
-    uint64_t            cbFileCurrent;
-} PARALLELSIMAGE, *PPARALLELSIMAGE;
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aParallelsFileExtensions[] =
-{
-    {"hdd", VDTYPE_HDD},
-    {NULL, VDTYPE_INVALID}
-};
-
-/***************************************************
- * Internal functions                              *
- **************************************************/
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) parallelsError(PPARALLELSIMAGE pImage, int rc, RT_SRC_POS_DECL,
-                               const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) parallelsMessage(PPARALLELSIMAGE pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) parallelsFileOpen(PPARALLELSIMAGE pImage, const char *pszFilename,
-                                  uint32_t fOpen)
-{
-    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                  pszFilename, fOpen,
-                                                  &pImage->pStorage);
-}
-
-DECLINLINE(int) parallelsFileClose(PPARALLELSIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                   pImage->pStorage);
-}
-
-DECLINLINE(int) parallelsFileDelete(PPARALLELSIMAGE pImage, const char *pszFilename)
-{
-    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                    pszFilename);
-}
-
-DECLINLINE(int) parallelsFileMove(PPARALLELSIMAGE pImage, const char *pszSrc,
-                                  const char *pszDst, unsigned fMove)
-{
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) parallelsFileGetSize(PPARALLELSIMAGE pImage, uint64_t *pcbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, pcbSize);
-}
-
-DECLINLINE(int) parallelsFileSetSize(PPARALLELSIMAGE pImage, uint64_t cbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, cbSize);
-}
-
-DECLINLINE(int) parallelsFileWriteSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
-                                       const void *pvBuffer, size_t cbBuffer,
-                                       size_t *pcbWritten)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage, uOffset,
-                                                       pvBuffer, cbBuffer, pcbWritten);
-}
-
-DECLINLINE(int) parallelsFileReadSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
-                                      void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pImage->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, pcbRead);
-}
-
-DECLINLINE(int) parallelsFileFlushSync(PPARALLELSIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage);
-}
-
-DECLINLINE(int) parallelsFileReadUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
-                                           PVDIOCTX pIoCtx, size_t cbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
-                                                           pImage->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-DECLINLINE(int) parallelsFileWriteUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
-                                            PVDIOCTX pIoCtx, size_t cbWrite,
-                                            PFNVDXFERCOMPLETED pfnComplete,
-                                            void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) parallelsFileWriteMetaAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
-                                            void *pvBuffer, size_t cbBuffer,
-                                            PVDIOCTX pIoCtx,
-                                            PFNVDXFERCOMPLETED pfnComplete,
-                                            void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pvBuffer,
-                                                            cbBuffer, pIoCtx,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) parallelsFileFlushAsync(PPARALLELSIMAGE pImage, PVDIOCTX pIoCtx,
-                                        PFNVDXFERCOMPLETED pfnComplete,
-                                        void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                        pImage->pStorage,
-                                                        pIoCtx, pfnComplete,
-                                                        pvCompleteUser);
-}
-
-
-/**
- * Internal. Flush image data to disk.
- */
-static int parallelsFlushImage(PPARALLELSIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        return VINF_SUCCESS;
-
-    if (   !(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        && (pImage->fAllocationBitmapChanged))
-    {
-        pImage->fAllocationBitmapChanged = false;
-        /* Write the allocation bitmap to the file. */
-        rc = parallelsFileWriteSync(pImage, sizeof(ParallelsHeader),
-                                    pImage->pAllocationBitmap,
-                                    pImage->cAllocationBitmapEntries * sizeof(uint32_t),
-                                    NULL);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    /* Flush file. */
-    rc = parallelsFileFlushSync(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Internal. Free all allocated space for representing an image except pImage,
- * and optionally delete the image from disk.
- */
-static int parallelsFreeImage(PPARALLELSIMAGE pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (pImage->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-                parallelsFlushImage(pImage);
-
-            parallelsFileClose(pImage);
-            pImage->pStorage = NULL;
-        }
-
-        if (pImage->pAllocationBitmap)
-        {
-            RTMemFree(pImage->pAllocationBitmap);
-            pImage->pAllocationBitmap = NULL;
-        }
-
-        if (fDelete && pImage->pszFilename)
-            parallelsFileDelete(pImage, pImage->pszFilename);
-    }
-
-    return rc;
-}
-
-static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
-{
-    int rc = VINF_SUCCESS;
-    ParallelsHeader parallelsHeader;
-
-    /* Try to get error interface. */
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    rc = parallelsFileOpen(pImage, pImage->pszFilename,
-                           VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                      false /* fCreate */));
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = parallelsFileGetSize(pImage, &pImage->cbFileCurrent);
-    if (RT_FAILURE(rc))
-        goto out;
-    AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
-
-    rc = parallelsFileReadSync(pImage, 0, &parallelsHeader, sizeof(parallelsHeader), NULL);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    if (memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16))
-    {
-        /* Check if the file has hdd as extension. It is a fixed size raw image then. */
-        char *pszExtension = RTPathExt(pImage->pszFilename);
-        if (strcmp(pszExtension, ".hdd"))
-        {
-            rc = VERR_VD_PARALLELS_INVALID_HEADER;
-            goto out;
-        }
-
-        /* This is a fixed size image. */
-        pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-        pImage->cbSize = pImage->cbFileCurrent;
-
-        pImage->PCHSGeometry.cHeads     = 16;
-        pImage->PCHSGeometry.cSectors   = 63;
-        uint64_t cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
-        pImage->PCHSGeometry.cCylinders = (uint32_t)cCylinders;
-    }
-    else
-    {
-        if (parallelsHeader.uVersion != PARALLELS_DISK_VERSION)
-        {
-            rc = VERR_NOT_SUPPORTED;
-            goto out;
-        }
-
-        if (parallelsHeader.cEntriesInAllocationBitmap > (1 << 30))
-        {
-            rc = VERR_NOT_SUPPORTED;
-            goto out;
-        }
-
-        Log(("cSectors=%u\n", parallelsHeader.cSectors));
-        pImage->cbSize = ((uint64_t)parallelsHeader.cSectors) * 512;
-        pImage->uImageFlags = VD_IMAGE_FLAGS_NONE;
-        pImage->cAllocationBitmapEntries = parallelsHeader.cEntriesInAllocationBitmap;
-        pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ((uint32_t)pImage->cAllocationBitmapEntries * sizeof(uint32_t));
-        if (!pImage->pAllocationBitmap)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        rc = parallelsFileReadSync(pImage, sizeof(ParallelsHeader),
-                                   pImage->pAllocationBitmap,
-                                   pImage->cAllocationBitmapEntries * sizeof(uint32_t),
-                                   NULL);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        pImage->PCHSGeometry.cCylinders = parallelsHeader.cCylinders;
-        pImage->PCHSGeometry.cHeads     = parallelsHeader.cHeads;
-        pImage->PCHSGeometry.cSectors   = parallelsHeader.cSectorsPerTrack;
-    }
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int parallelsCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                                 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    int rc;
-    PVDIOSTORAGE pStorage;
-    ParallelsHeader parallelsHeader;
-
-    /* Get I/O interface. */
-    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
-    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
-    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
-                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
-                                                                   false /* fCreate */),
-                                        &pStorage);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
-                                            0, &parallelsHeader,
-                                            sizeof(ParallelsHeader), NULL);
-    if (RT_SUCCESS(rc))
-    {
-        if (   !memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16)
-            && (parallelsHeader.uVersion == PARALLELS_DISK_VERSION))
-            rc = VINF_SUCCESS;
-        else
-        {
-            /*
-             * The image may be an fixed size image.
-             * Unfortunately fixed sized parallels images
-             * are just raw files hence no magic header to
-             * check for.
-             * The code succeeds if the file is a multiple
-             * of 512 and if the file extensions is *.hdd
-             */
-            uint64_t cbFile;
-            char *pszExtension;
-
-            rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
-                                                   &cbFile);
-            if (RT_FAILURE(rc) || ((cbFile % 512) != 0))
-            {
-                pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-                return VERR_VD_PARALLELS_INVALID_HEADER;
-            }
-
-            pszExtension = RTPathExt(pszFilename);
-            if (!pszExtension || strcmp(pszExtension, ".hdd"))
-                rc = VERR_VD_PARALLELS_INVALID_HEADER;
-            else
-                rc = VINF_SUCCESS;
-        }
-    }
-
-    if (RT_SUCCESS(rc))
-        *penmType = VDTYPE_HDD;
-
-    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int parallelsOpen(const char *pszFilename, unsigned uOpenFlags,
-                         PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                         VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PPARALLELSIMAGE pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /** @todo r=klaus why this duplicate check, async is not claimed... */
-    if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
-    {
-        rc = VERR_NOT_SUPPORTED;
-        goto out;
-    }
-
-    pImage = (PPARALLELSIMAGE)RTMemAllocZ(sizeof(PARALLELSIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-    pImage->fAllocationBitmapChanged = false;
-
-    rc = parallelsOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pImage;
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int parallelsCreate(const char *pszFilename, uint64_t cbSize,
-                           unsigned uImageFlags, const char *pszComment,
-                           PCVDGEOMETRY pPCHSGeometry,
-                           PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
-                           unsigned uOpenFlags, unsigned uPercentStart,
-                           unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
-                           PVDINTERFACE pVDIfsImage,
-                           PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc = VERR_NOT_IMPLEMENTED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int parallelsRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-    int rc = VINF_SUCCESS;
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-
-    /* Check arguments. */
-    if (   !pImage
-        || !pszFilename
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Close the image. */
-    rc = parallelsFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Rename the file. */
-    rc = parallelsFileMove(pImage, pImage->pszFilename, pszFilename, 0);
-    if (RT_FAILURE(rc))
-    {
-        /* The move failed, try to reopen the original image. */
-        int rc2 = parallelsOpenImage(pImage, pImage->uOpenFlags);
-        if (RT_FAILURE(rc2))
-            rc = rc2;
-
-        goto out;
-    }
-
-    /* Update pImage with the new information. */
-    pImage->pszFilename = pszFilename;
-
-    /* Open the old image with new name. */
-    rc = parallelsOpenImage(pImage, pImage->uOpenFlags);
-    if (RT_FAILURE(rc))
-        goto out;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int parallelsClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    rc = parallelsFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int parallelsRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                         size_t cbBuf, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-    uint64_t uSector;
-    uint64_t uOffsetInFile;
-    uint32_t iIndexInAllocationTable;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbBuf % 512 == 0);
-
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        rc = parallelsFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
-    }
-    else
-    {
-        /* Calculate offset in the real file. */
-        uSector = uOffset / 512;
-        /* One chunk in the file is always one track big. */
-        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
-        uSector = uSector % pImage->PCHSGeometry.cSectors;
-
-        cbBuf = RT_MIN(cbBuf, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
-        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
-        {
-            rc = VERR_VD_BLOCK_FREE;
-        }
-        else
-        {
-            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-            rc = parallelsFileReadSync(pImage, uOffsetInFile, pvBuf, cbBuf, NULL);
-        }
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        if (pcbActuallyRead)
-            *pcbActuallyRead = cbBuf;
-
-        Log2(("parallelsRead: off=%#llx pvBuf=%p cbBuf=%d\n"
-                "%.*Rhxd\n",
-                uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int parallelsWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                          size_t cbBuf, size_t *pcbWriteProcess,
-                          size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-    uint64_t uSector;
-    uint64_t uOffsetInFile;
-    uint32_t iIndexInAllocationTable;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbBuf % 512 == 0);
-
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        rc = parallelsFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
-    }
-    else
-    {
-        /** Calculate offset in the real file. */
-        uSector = uOffset / 512;
-        /** One chunk in the file is always one track big. */
-        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
-        uSector = uSector % pImage->PCHSGeometry.cSectors;
-
-        cbBuf = RT_MIN(cbBuf, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
-        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
-        {
-            /* Allocate new chunk in the file. */
-            AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
-            pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
-            pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
-            pImage->fAllocationBitmapChanged = true;
-
-            uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(pImage->PCHSGeometry.cSectors * 512);
-
-            if (!pNewBlock)
-            {
-                rc = VERR_NO_MEMORY;
-                goto out;
-            }
-
-            uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
-            memcpy(pNewBlock + (uOffset - ((uint64_t)iIndexInAllocationTable * pImage->PCHSGeometry.cSectors * 512)),
-                   pvBuf, cbBuf);
-
-            /*
-             * Write the new block at the current end of the file.
-             */
-            rc = parallelsFileWriteSync(pImage, uOffsetInFile, pNewBlock,
-                                        pImage->PCHSGeometry.cSectors * 512,
-                                        NULL);
-
-            RTMemFree(pNewBlock);
-        }
-        else
-        {
-            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-            rc = parallelsFileWriteSync(pImage, uOffsetInFile, pvBuf, cbBuf, NULL);
-        }
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbBuf;
-
-    /* Stay on the safe side. Do not run the risk of confusing the higher
-     * level, as that can be pretty lethal to image consistency. */
-    *pcbPreRead = 0;
-    *pcbPostRead = 0;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int parallelsFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    rc = parallelsFlushImage(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned parallelsGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        return PARALLELS_DISK_VERSION;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t parallelsGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-        cb = pImage->cbSize;
-
-    LogFlowFunc(("returns %llu\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t parallelsGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-        cb = pImage->cbFileCurrent;
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int parallelsGetPCHSGeometry(void *pBackendData,
-                                    PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pImage->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int parallelsSetPCHSGeometry(void *pBackendData,
-                                    PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int parallelsGetLCHSGeometry(void *pBackendData,
-                                    PVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->LCHSGeometry.cCylinders)
-        {
-            *pLCHSGeometry = pImage->LCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int parallelsSetLCHSGeometry(void *pBackendData,
-                                    PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->LCHSGeometry = *pLCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned parallelsGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uImageFlags = pImage->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned parallelsGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int parallelsSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    /** @todo r=klaus add VD_OPEN_FLAGS_ASYNC_IO when async io has been tested */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    parallelsFreeImage(pImage, true);
-    rc = parallelsOpenImage(pImage, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int parallelsGetComment(void *pBackendData, char *pszComment,
-                               size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int parallelsSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int parallelsGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int parallelsSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int parallelsGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int parallelsSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int parallelsGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int parallelsSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int parallelsGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int parallelsSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void parallelsDump(void *pBackendData)
-{
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        parallelsMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u\n",
-                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
-                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors);
-    }
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
-static bool parallelsIsAsyncIOSupported(void *pBackendData)
-{
-#if 0 /** @todo: Remove when tested */
-    return true;
-#else
-    return false;
-#endif
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int parallelsAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
-                              PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
-    int rc = VINF_SUCCESS;
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    uint64_t uSector;
-    uint64_t uOffsetInFile;
-    uint32_t iIndexInAllocationTable;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToRead % 512 == 0);
-
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        rc = parallelsFileReadUserAsync(pImage, uOffset, pIoCtx, cbToRead);
-    }
-    else
-    {
-        /* Calculate offset in the real file. */
-        uSector = uOffset / 512;
-        /* One chunk in the file is always one track big. */
-        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
-        uSector = uSector % pImage->PCHSGeometry.cSectors;
-
-        cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
-        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
-        {
-            rc = VERR_VD_BLOCK_FREE;
-        }
-        else
-        {
-            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-            rc = parallelsFileReadUserAsync(pImage, uOffsetInFile, pIoCtx, cbToRead);
-        }
-    }
-
-    *pcbActuallyRead = cbToRead;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int parallelsAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
-                               PVDIOCTX pIoCtx,
-                               size_t *pcbWriteProcess, size_t *pcbPreRead,
-                               size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
-    int rc = VINF_SUCCESS;
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-    uint64_t uSector;
-    uint64_t uOffsetInFile;
-    uint32_t iIndexInAllocationTable;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        rc = parallelsFileWriteUserAsync(pImage, uOffset, pIoCtx, cbToWrite, NULL, NULL);
-    }
-    else
-    {
-        /* Calculate offset in the real file. */
-        uSector = uOffset / 512;
-        /* One chunk in the file is always one track big. */
-        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
-        uSector = uSector % pImage->PCHSGeometry.cSectors;
-
-        cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
-
-        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
-        {
-            if (fWrite & VD_WRITE_NO_ALLOC)
-            {
-                *pcbPreRead  = uSector * 512;
-                *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
-
-                if (pcbWriteProcess)
-                    *pcbWriteProcess = cbToWrite;
-                return VERR_VD_BLOCK_FREE;
-            }
-
-            /* Allocate new chunk in the file. */
-            Assert(uSector == 0);
-            AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
-            pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
-            pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
-            pImage->fAllocationBitmapChanged = true;
-            uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
-
-            /*
-             * Write the new block at the current end of the file.
-             */
-            rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
-            if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
-            {
-                /* Write the changed allocation bitmap entry. */
-                /** @todo: Error handling. */
-                rc = parallelsFileWriteMetaAsync(pImage,
-                                                 sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
-                                                 &pImage->pAllocationBitmap[iIndexInAllocationTable],
-                                                 sizeof(uint32_t), pIoCtx,
-                                                 NULL, NULL);
-            }
-        }
-        else
-        {
-            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
-            rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
-        }
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbToWrite;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int parallelsAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
-
-    LogFlowFunc(("pImage=#%p\n", pImage));
-
-    /* Flush the file, everything is up to date already. */
-    rc = parallelsFileFlushAsync(pImage, pIoCtx, NULL, NULL);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_ParallelsBackend =
-{
-    /* pszBackendName */
-    "Parallels",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-    VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aParallelsFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    parallelsCheckIfValid,
-    /* pfnOpen */
-    parallelsOpen,
-    /* pfnCreate */
-    parallelsCreate,
-    /* pfnRename */
-    parallelsRename,
-    /* pfnClose */
-    parallelsClose,
-    /* pfnRead */
-    parallelsRead,
-    /* pfnWrite */
-    parallelsWrite,
-    /* pfnFlush */
-    parallelsFlush,
-    /* pfnGetVersion */
-    parallelsGetVersion,
-    /* pfnGetSize */
-    parallelsGetSize,
-    /* pfnGetFileSize */
-    parallelsGetFileSize,
-    /* pfnGetPCHSGeometry */
-    parallelsGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    parallelsSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    parallelsGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    parallelsSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    parallelsGetImageFlags,
-    /* pfnGetOpenFlags */
-    parallelsGetOpenFlags,
-    /* pfnSetOpenFlags */
-    parallelsSetOpenFlags,
-    /* pfnGetComment */
-    parallelsGetComment,
-    /* pfnSetComment */
-    parallelsSetComment,
-    /* pfnGetUuid */
-    parallelsGetUuid,
-    /* pfnSetUuid */
-    parallelsSetUuid,
-    /* pfnGetModificationUuid */
-    parallelsGetModificationUuid,
-    /* pfnSetModificationUuid */
-    parallelsSetModificationUuid,
-    /* pfnGetParentUuid */
-    parallelsGetParentUuid,
-    /* pfnSetParentUuid */
-    parallelsSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    parallelsGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    parallelsSetParentModificationUuid,
-    /* pfnDump */
-    parallelsDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    parallelsIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    parallelsAsyncRead,
-    /* pfnAsyncWrite */
-    parallelsAsyncWrite,
-    /* pfnAsyncFlush */
-    parallelsAsyncFlush,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    NULL
-};
Index: unk/src/VBox/Devices/Storage/RawHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/RawHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,1484 +1,0 @@
-/* $Id$ */
-/** @file
- * RawHDDCore - Raw Disk image, Core Code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_RAW
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/assert.h>
-#include <iprt/alloc.h>
-#include <iprt/path.h>
-
-/*******************************************************************************
-*   Constants And Macros, Structures and Typedefs                              *
-*******************************************************************************/
-/**
- * ISO volume descriptor.
- */
-#pragma pack(1)
-typedef struct ISOVOLDESC
-{
-    union
-    {
-        /** Byte view. */
-        uint8_t au8[2048];
-        /** Field view. */
-        struct
-        {
-            /** Descriptor type. */
-            uint8_t u8Type;
-            /** Standard identifier */
-            uint8_t au8Id[5];
-            /** Descriptor version. */
-            uint8_t u8Version;
-            /** Rest depends on the descriptor type. */
-        } fields;
-    };
-} ISOVOLDESC, *PISOVOLDESC;
-#pragma pack()
-AssertCompileSize(ISOVOLDESC, 2048);
-
-/**
- * Raw image data structure.
- */
-typedef struct RAWIMAGE
-{
-    /** Image name. */
-    const char          *pszFilename;
-    /** Storage handle. */
-    PVDIOSTORAGE        pStorage;
-    /** I/O interface. */
-    PVDINTERFACE        pInterfaceIO;
-    /** Async I/O interface callbacks. */
-    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE        pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE        pVDIfsImage;
-
-    /** Error callback. */
-    PVDINTERFACE        pInterfaceError;
-    /** Opaque data for error callback. */
-    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
-
-    /** Open flags passed by VBoxHD layer. */
-    unsigned            uOpenFlags;
-    /** Image flags defined during creation or determined during open. */
-    unsigned            uImageFlags;
-    /** Total size of the image. */
-    uint64_t            cbSize;
-    /** Position in the image (only truly used for sequential access). */
-    uint64_t            offAccess;
-    /** Flag if this is a newly created image. */
-    bool                fCreate;
-    /** Physical geometry of this image. */
-    VDGEOMETRY          PCHSGeometry;
-    /** Logical geometry of this image. */
-    VDGEOMETRY          LCHSGeometry;
-
-} RAWIMAGE, *PRAWIMAGE;
-
-
-/** Size of write operations when filling an image with zeroes. */
-#define RAW_FILL_SIZE (128 * _1K)
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aRawFileExtensions[] =
-{
-    {"iso", VDTYPE_DVD},
-    {"img", VDTYPE_FLOPPY},
-    {"ima", VDTYPE_FLOPPY},
-    {NULL, VDTYPE_INVALID}
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) rawError(PRAWIMAGE pImage, int rc, RT_SRC_POS_DECL,
-                         const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) rawMessage(PRAWIMAGE pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) rawFileOpen(PRAWIMAGE pImage, const char *pszFilename,
-                            uint32_t fOpen)
-{
-    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                  pszFilename, fOpen,
-                                                  &pImage->pStorage);
-}
-
-DECLINLINE(int) rawFileClose(PRAWIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                   pImage->pStorage);
-}
-
-DECLINLINE(int) rawFileDelete(PRAWIMAGE pImage, const char *pszFilename)
-{
-    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                    pszFilename);
-}
-
-DECLINLINE(int) rawFileMove(PRAWIMAGE pImage, const char *pszSrc,
-                            const char *pszDst, unsigned fMove)
-{
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) rawFileGetFreeSpace(PRAWIMAGE pImage, const char *pszFilename,
-                                    int64_t *pcbFree)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
-                                                          pszFilename, pcbFree);
-}
-
-DECLINLINE(int) rawFileGetSize(PRAWIMAGE pImage, uint64_t *pcbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, pcbSize);
-}
-
-DECLINLINE(int) rawFileSetSize(PRAWIMAGE pImage, uint64_t cbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, cbSize);
-}
-
-DECLINLINE(int) rawFileWriteSync(PRAWIMAGE pImage, uint64_t uOffset,
-                                 const void *pvBuffer, size_t cbBuffer,
-                                 size_t *pcbWritten)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage, uOffset,
-                                                       pvBuffer, cbBuffer, pcbWritten);
-}
-
-DECLINLINE(int) rawFileReadSync(PRAWIMAGE pImage, uint64_t uOffset,
-                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pImage->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, pcbRead);
-}
-
-DECLINLINE(int) rawFileFlushSync(PRAWIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage);
-}
-
-DECLINLINE(int) rawFileReadUserAsync(PRAWIMAGE pImage, uint64_t uOffset,
-                                     PVDIOCTX pIoCtx, size_t cbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
-                                                           pImage->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-DECLINLINE(int) rawFileWriteUserAsync(PRAWIMAGE pImage, uint64_t uOffset,
-                                      PVDIOCTX pIoCtx, size_t cbWrite,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) rawFileFlushAsync(PRAWIMAGE pImage, PVDIOCTX pIoCtx,
-                                  PFNVDXFERCOMPLETED pfnComplete,
-                                  void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                        pImage->pStorage,
-                                                        pIoCtx, pfnComplete,
-                                                        pvCompleteUser);
-}
-
-
-/**
- * Internal. Flush image data to disk.
- */
-static int rawFlushImage(PRAWIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-
-    if (   pImage->pStorage
-        && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        rc = rawFileFlushSync(pImage);
-
-    return rc;
-}
-
-/**
- * Internal. Free all allocated space for representing an image except pImage,
- * and optionally delete the image from disk.
- */
-static int rawFreeImage(PRAWIMAGE pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (pImage->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-            {
-                /* For newly created images in sequential mode fill it to
-                 * the nominal size. */
-                if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
-                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                    && pImage->fCreate)
-                {
-                    /* Fill rest of image with zeroes, a must for sequential
-                     * images to reach the nominal size. */
-                    uint64_t uOff;
-                    void *pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE);
-                    if (!pvBuf)
-                        goto out;
-
-                    uOff = pImage->offAccess;
-                    /* Write data to all image blocks. */
-                    while (uOff < pImage->cbSize)
-                    {
-                        unsigned cbChunk = (unsigned)RT_MIN(pImage->cbSize,
-                                                            RAW_FILL_SIZE);
-
-                        rc = rawFileWriteSync(pImage, uOff, pvBuf, cbChunk,
-                                              NULL);
-                        if (RT_FAILURE(rc))
-                            goto out;
-
-                        uOff += cbChunk;
-                    }
-out:
-                    if (pvBuf)
-                        RTMemTmpFree(pvBuf);
-                }
-                rawFlushImage(pImage);
-            }
-
-            rawFileClose(pImage);
-            pImage->pStorage = NULL;
-        }
-
-        if (fDelete && pImage->pszFilename)
-            rawFileDelete(pImage, pImage->pszFilename);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Internal: Open an image, constructing all necessary data structures.
- */
-static int rawOpenImage(PRAWIMAGE pImage, unsigned uOpenFlags)
-{
-    int rc;
-
-    pImage->uOpenFlags = uOpenFlags;
-    pImage->fCreate = false;
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the image.
-     */
-    rc = rawFileOpen(pImage, pImage->pszFilename,
-                     VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                false /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        goto out;
-    }
-
-    rc = rawFileGetSize(pImage, &pImage->cbSize);
-    if (RT_FAILURE(rc))
-        goto out;
-    if (pImage->cbSize % 512)
-    {
-        rc = VERR_VD_RAW_INVALID_HEADER;
-        goto out;
-    }
-    pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-
-out:
-    if (RT_FAILURE(rc))
-        rawFreeImage(pImage, false);
-    return rc;
-}
-
-/**
- * Internal: Create a raw image.
- */
-static int rawCreateImage(PRAWIMAGE pImage, uint64_t cbSize,
-                          unsigned uImageFlags, const char *pszComment,
-                          PCVDGEOMETRY pPCHSGeometry,
-                          PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags,
-                          PFNVDPROGRESS pfnProgress, void *pvUser,
-                          unsigned uPercentStart, unsigned uPercentSpan)
-{
-    int rc;
-    RTFOFF cbFree = 0;
-    uint64_t uOff;
-    void *pvBuf = NULL;
-    int32_t fOpen;
-
-    if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
-    {
-        rc = rawError(pImage, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("Raw: cannot create diff image '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-
-    pImage->uImageFlags = uImageFlags;
-
-    pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
-
-    pImage->uImageFlags = uImageFlags;
-    pImage->fCreate = true;
-    pImage->PCHSGeometry = *pPCHSGeometry;
-    pImage->LCHSGeometry = *pLCHSGeometry;
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /* Create image file. */
-    fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */);
-    if (uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)
-        fOpen &= ~RTFILE_O_READ;
-    rc = rawFileOpen(pImage, pImage->pszFilename, fOpen);
-    if (RT_FAILURE(rc))
-    {
-        rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: cannot create image '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    if (!(uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
-    {
-        /* Check the free space on the disk and leave early if there is not
-         * sufficient space available. */
-        rc = rawFileGetFreeSpace(pImage, pImage->pszFilename, &cbFree);
-        if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbSize))
-        {
-            rc = rawError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("Raw: disk would overflow creating image '%s'"), pImage->pszFilename);
-            goto out;
-        }
-
-        /* Allocate & commit whole file if fixed image, it must be more
-         * effective than expanding file by write operations. */
-        rc = rawFileSetSize(pImage, cbSize);
-        if (RT_FAILURE(rc))
-        {
-            rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: setting image size failed for '%s'"), pImage->pszFilename);
-            goto out;
-        }
-
-        /* Fill image with zeroes. We do this for every fixed-size image since
-         * on some systems (for example Windows Vista), it takes ages to write
-         * a block near the end of a sparse file and the guest could complain
-         * about an ATA timeout. */
-        pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE);
-        if (!pvBuf)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        uOff = 0;
-        /* Write data to all image blocks. */
-        while (uOff < cbSize)
-        {
-            unsigned cbChunk = (unsigned)RT_MIN(cbSize, RAW_FILL_SIZE);
-
-            rc = rawFileWriteSync(pImage, uOff, pvBuf, cbChunk, NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: writing block failed for '%s'"), pImage->pszFilename);
-                goto out;
-            }
-
-            uOff += cbChunk;
-
-            if (pfnProgress)
-            {
-                rc = pfnProgress(pvUser,
-                                 uPercentStart + uOff * uPercentSpan * 98 / (cbSize * 100));
-                if (RT_FAILURE(rc))
-                    goto out;
-            }
-        }
-    }
-
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
-
-    pImage->cbSize = cbSize;
-
-    rc = rawFlushImage(pImage);
-
-out:
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan);
-
-    if (RT_FAILURE(rc))
-        rawFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int rawCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
-    PVDIOSTORAGE pStorage = NULL;
-    uint64_t cbFile;
-    int rc = VINF_SUCCESS;
-    ISOVOLDESC IsoVolDesc;
-    char *pszExtension = NULL;
-
-    /* Get I/O interface. */
-    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
-    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
-    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pszExtension = RTPathExt(pszFilename);
-
-    /*
-     * Open the file and read the footer.
-     */
-    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
-                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
-                                                                   false /* fCreate */),
-                                        &pStorage);
-    if (RT_SUCCESS(rc))
-        rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
-                                               &cbFile);
-
-    /* Try to guess the image type based on the extension. */
-    if (   RT_SUCCESS(rc)
-        && pszExtension)
-    {
-        if (!strcmp(pszExtension, ".iso")) /* DVD images. */
-        {
-            if (cbFile >= (32768 + sizeof(ISOVOLDESC)))
-            {
-                rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
-                                                        32768, &IsoVolDesc, sizeof(IsoVolDesc), NULL);
-            }
-            else
-                rc = VERR_VD_RAW_INVALID_HEADER;
-
-            if (RT_SUCCESS(rc))
-            {
-                /*
-                 * Do we recognize this stuff?
-                 */
-                if (   !memcmp(IsoVolDesc.fields.au8Id, "BEA01", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "BOOT2", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "CD001", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "CDW02", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "NSR02", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "NSR03", 5)
-                    || !memcmp(IsoVolDesc.fields.au8Id, "TEA01", 5))
-                {
-                    *penmType = VDTYPE_DVD;
-                    rc = VINF_SUCCESS;
-                }
-                else
-                    rc = VERR_VD_RAW_INVALID_HEADER;
-            }
-        }
-        else if (!strcmp(pszExtension, ".img") || !strcmp(pszExtension, ".ima")) /* Floppy images */
-        {
-            if (!(cbFile % 512))
-            {
-                *penmType = VDTYPE_FLOPPY;
-                rc = VINF_SUCCESS;
-            }
-            else
-                rc = VERR_VD_RAW_INVALID_HEADER;
-        }
-    }
-    else
-        rc = VERR_VD_RAW_INVALID_HEADER;
-
-    if (pStorage)
-        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int rawOpen(const char *pszFilename, unsigned uOpenFlags,
-                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                   VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PRAWIMAGE pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-
-    pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = rawOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pImage;
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int rawCreate(const char *pszFilename, uint64_t cbSize,
-                     unsigned uImageFlags, const char *pszComment,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     PCRTUUID pUuid, unsigned uOpenFlags,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc;
-    PRAWIMAGE pImage;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename
-        || !VALID_PTR(pPCHSGeometry)
-        || !VALID_PTR(pLCHSGeometry))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = rawCreateImage(pImage, cbSize, uImageFlags, pszComment,
-                        pPCHSGeometry, pLCHSGeometry, uOpenFlags,
-                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
-    if (RT_SUCCESS(rc))
-    {
-        /* So far the image is opened in read/write mode. Make sure the
-         * image is opened in read-only mode if the caller requested that. */
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rawFreeImage(pImage, false);
-            rc = rawOpenImage(pImage, uOpenFlags);
-            if (RT_FAILURE(rc))
-            {
-                RTMemFree(pImage);
-                goto out;
-            }
-        }
-        *ppBackendData = pImage;
-    }
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int rawRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-    int rc = VINF_SUCCESS;
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    /* Check arguments. */
-    if (   !pImage
-        || !pszFilename
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Close the image. */
-    rc = rawFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Rename the file. */
-    rc = rawFileMove(pImage, pImage->pszFilename, pszFilename, 0);
-    if (RT_FAILURE(rc))
-    {
-        /* The move failed, try to reopen the original image. */
-        int rc2 = rawOpenImage(pImage, pImage->uOpenFlags);
-        if (RT_FAILURE(rc2))
-            rc = rc2;
-
-        goto out;
-    }
-
-    /* Update pImage with the new information. */
-    pImage->pszFilename = pszFilename;
-
-    /* Open the old image with new name. */
-    rc = rawOpenImage(pImage, pImage->uOpenFlags);
-    if (RT_FAILURE(rc))
-        goto out;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int rawClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    rc = rawFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int rawRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                   size_t cbToRead, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToRead % 512 == 0);
-
-    if (   uOffset + cbToRead > pImage->cbSize
-        || cbToRead == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* For sequential access do not allow to go back. */
-    if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
-        && uOffset < pImage->offAccess)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    rc = rawFileReadSync(pImage, uOffset, pvBuf, cbToRead, NULL);
-    pImage->offAccess = uOffset + cbToRead;
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbToRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int rawWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                    size_t cbToWrite, size_t *pcbWriteProcess,
-                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    if (   uOffset + cbToWrite > pImage->cbSize
-        || cbToWrite == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* For sequential access do not allow to go back. */
-    if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
-        && uOffset < pImage->offAccess)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    rc = rawFileWriteSync(pImage, uOffset, pvBuf, cbToWrite, NULL);
-    pImage->offAccess = uOffset + cbToWrite;
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbToWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int rawFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    rc = rawFlushImage(pImage);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned rawGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        return 1;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t rawGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-        cb = pImage->cbSize;
-
-    LogFlowFunc(("returns %llu\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t rawGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        uint64_t cbFile;
-        if (pImage->pStorage)
-        {
-            int rc = rawFileGetSize(pImage, &cbFile);
-            if (RT_SUCCESS(rc))
-                cb += cbFile;
-        }
-    }
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int rawGetPCHSGeometry(void *pBackendData,
-                              PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pImage->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int rawSetPCHSGeometry(void *pBackendData,
-                              PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int rawGetLCHSGeometry(void *pBackendData,
-                              PVDGEOMETRY pLCHSGeometry)
-{
-     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->LCHSGeometry.cCylinders)
-        {
-            *pLCHSGeometry = pImage->LCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int rawSetLCHSGeometry(void *pBackendData,
-                               PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->LCHSGeometry = *pLCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned rawGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uImageFlags = pImage->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned rawGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int rawSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    rc = rawFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = rawOpenImage(pImage, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int rawGetComment(void *pBackendData, char *pszComment,
-                          size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int rawSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int rawGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int rawSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int rawGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int rawSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int rawGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int rawSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int rawGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int rawSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void rawDump(void *pBackendData)
-{
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        rawMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
-                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
-                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
-                    pImage->cbSize / 512);
-    }
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
-static bool rawIsAsyncIOSupported(void *pBackendData)
-{
-    return true;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int rawAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
-                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    int rc = VINF_SUCCESS;
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    rc = rawFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
-    if (RT_SUCCESS(rc))
-        *pcbActuallyRead = cbRead;
-
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int rawAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                         PVDIOCTX pIoCtx,
-                         size_t *pcbWriteProcess, size_t *pcbPreRead,
-                         size_t *pcbPostRead, unsigned fWrite)
-{
-    int rc = VINF_SUCCESS;
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    rc = rawFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
-    if (RT_SUCCESS(rc))
-    {
-        *pcbWriteProcess = cbWrite;
-        *pcbPostRead = 0;
-        *pcbPreRead  = 0;
-    }
-
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int rawAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
-
-    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        rc = rawFileFlushAsync(pImage, pIoCtx, NULL, NULL);
-
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_RawBackend =
-{
-    /* pszBackendName */
-    "RAW",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-    VD_CAP_CREATE_FIXED | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aRawFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    rawCheckIfValid,
-    /* pfnOpen */
-    rawOpen,
-    /* pfnCreate */
-    rawCreate,
-    /* pfnRename */
-    rawRename,
-    /* pfnClose */
-    rawClose,
-    /* pfnRead */
-    rawRead,
-    /* pfnWrite */
-    rawWrite,
-    /* pfnFlush */
-    rawFlush,
-    /* pfnGetVersion */
-    rawGetVersion,
-    /* pfnGetSize */
-    rawGetSize,
-    /* pfnGetFileSize */
-    rawGetFileSize,
-    /* pfnGetPCHSGeometry */
-    rawGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    rawSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    rawGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    rawSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    rawGetImageFlags,
-    /* pfnGetOpenFlags */
-    rawGetOpenFlags,
-    /* pfnSetOpenFlags */
-    rawSetOpenFlags,
-    /* pfnGetComment */
-    rawGetComment,
-    /* pfnSetComment */
-    rawSetComment,
-    /* pfnGetUuid */
-    rawGetUuid,
-    /* pfnSetUuid */
-    rawSetUuid,
-    /* pfnGetModificationUuid */
-    rawGetModificationUuid,
-    /* pfnSetModificationUuid */
-    rawSetModificationUuid,
-    /* pfnGetParentUuid */
-    rawGetParentUuid,
-    /* pfnSetParentUuid */
-    rawSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    rawGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    rawSetParentModificationUuid,
-    /* pfnDump */
-    rawDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    rawIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    rawAsyncRead,
-    /* pfnAsyncWrite */
-    rawAsyncWrite,
-    /* pfnAsyncFlush */
-    rawAsyncFlush,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    NULL
-};
Index: unk/src/VBox/Devices/Storage/VBoxHDD.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VBoxHDD.cpp	(revision 33566)
+++ 	(revision )
@@ -1,8134 +1,0 @@
-/* $Id$ */
-/** @file
- * VBoxHDD - VBox HDD Container implementation.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD
-#include <VBox/VBoxHDD.h>
-#include <VBox/err.h>
-#include <VBox/sup.h>
-#include <VBox/log.h>
-
-#include <iprt/alloc.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include <iprt/file.h>
-#include <iprt/string.h>
-#include <iprt/asm.h>
-#include <iprt/ldr.h>
-#include <iprt/dir.h>
-#include <iprt/path.h>
-#include <iprt/param.h>
-#include <iprt/memcache.h>
-#include <iprt/sg.h>
-#include <iprt/critsect.h>
-#include <iprt/list.h>
-#include <iprt/avl.h>
-
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/VBoxHDD-CachePlugin.h>
-
-#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
-
-/** Buffer size used for merging images. */
-#define VD_MERGE_BUFFER_SIZE    (16 * _1M)
-
-/** Maximum number of segments in one I/O task. */
-#define VD_IO_TASK_SEGMENTS_MAX 64
-
-/**
- * VD async I/O interface storage descriptor.
- */
-typedef struct VDIIOFALLBACKSTORAGE
-{
-    /** File handle. */
-    RTFILE         File;
-    /** Completion callback. */
-    PFNVDCOMPLETED pfnCompleted;
-    /** Thread for async access. */
-    RTTHREAD       ThreadAsync;
-} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
-
-/**
- * VBox HDD Container image descriptor.
- */
-typedef struct VDIMAGE
-{
-    /** Link to parent image descriptor, if any. */
-    struct VDIMAGE  *pPrev;
-    /** Link to child image descriptor, if any. */
-    struct VDIMAGE  *pNext;
-    /** Container base filename. (UTF-8) */
-    char            *pszFilename;
-    /** Data managed by the backend which keeps the actual info. */
-    void            *pBackendData;
-    /** Cached sanitized image flags. */
-    unsigned        uImageFlags;
-    /** Image open flags (only those handled generically in this code and which
-     * the backends will never ever see). */
-    unsigned        uOpenFlags;
-
-    /** Function pointers for the various backend methods. */
-    PCVBOXHDDBACKEND    Backend;
-
-    /** Pointer to list of VD interfaces, per-image. */
-    PVDINTERFACE        pVDIfsImage;
-
-    /** I/O interface to the upper layer. */
-    PVDINTERFACE        pInterfaceIO;
-    /** I/O interface callback table. */
-    PVDINTERFACEIO      pInterfaceIOCallbacks;
-
-    /** Per image internal I/O interface. */
-    VDINTERFACE         VDIIOInt;
-
-    /** Fallback I/O interface, only used if the caller doesn't provide it. */
-    VDINTERFACE         VDIIO;
-
-    /** Disk this image is part of */
-    PVBOXHDD            pDisk;
-} VDIMAGE, *PVDIMAGE;
-
-/**
- * uModified bit flags.
- */
-#define VD_IMAGE_MODIFIED_FLAG                  RT_BIT(0)
-#define VD_IMAGE_MODIFIED_FIRST                 RT_BIT(1)
-#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE   RT_BIT(2)
-
-
-/**
- * VBox HDD Cache image descriptor.
- */
-typedef struct VDCACHE
-{
-    /** Cache base filename. (UTF-8) */
-    char            *pszFilename;
-    /** Data managed by the backend which keeps the actual info. */
-    void            *pBackendData;
-    /** Cached sanitized image flags. */
-    unsigned        uImageFlags;
-    /** Image open flags (only those handled generically in this code and which
-     * the backends will never ever see). */
-    unsigned        uOpenFlags;
-
-    /** Function pointers for the various backend methods. */
-    PCVDCACHEBACKEND    Backend;
-
-    /** Pointer to list of VD interfaces, per-cache. */
-    PVDINTERFACE        pVDIfsCache;
-
-    /** I/O interface to the upper layer. */
-    PVDINTERFACE        pInterfaceIO;
-    /** I/O interface callback table. */
-    PVDINTERFACEIO      pInterfaceIOCallbacks;
-
-    /** Per image internal I/O interface. */
-    VDINTERFACE         VDIIOInt;
-
-    /** Fallback I/O interface, only used if the caller doesn't provide it. */
-    VDINTERFACE         VDIIO;
-
-    /** Disk this image is part of */
-    PVBOXHDD            pDisk;
-} VDCACHE, *PVDCACHE;
-
-/**
- * VBox HDD Container main structure, private part.
- */
-struct VBOXHDD
-{
-    /** Structure signature (VBOXHDDDISK_SIGNATURE). */
-    uint32_t            u32Signature;
-
-    /** Image type. */
-    VDTYPE              enmType;
-
-    /** Number of opened images. */
-    unsigned            cImages;
-
-    /** Base image. */
-    PVDIMAGE            pBase;
-
-    /** Last opened image in the chain.
-     * The same as pBase if only one image is used. */
-    PVDIMAGE            pLast;
-
-    /** If a merge to one of the parents is running this may be non-NULL
-     * to indicate to what image the writes should be additionally relayed. */
-    PVDIMAGE            pImageRelay;
-
-    /** Flags representing the modification state. */
-    unsigned            uModified;
-
-    /** Cached size of this disk. */
-    uint64_t            cbSize;
-    /** Cached PCHS geometry for this disk. */
-    VDGEOMETRY          PCHSGeometry;
-    /** Cached LCHS geometry for this disk. */
-    VDGEOMETRY          LCHSGeometry;
-
-    /** Pointer to list of VD interfaces, per-disk. */
-    PVDINTERFACE        pVDIfsDisk;
-    /** Pointer to the common interface structure for error reporting. */
-    PVDINTERFACE        pInterfaceError;
-    /** Pointer to the error interface callbacks we use if available. */
-    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
-
-    /** Pointer to the optional thread synchronization interface. */
-    PVDINTERFACE        pInterfaceThreadSync;
-    /** Pointer to the optional thread synchronization callbacks. */
-    PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
-
-    /** Internal I/O interface callback table for the images. */
-    VDINTERFACEIOINT    VDIIOIntCallbacks;
-
-    /** Callback table for the fallback I/O interface. */
-    VDINTERFACEIO       VDIIOCallbacks;
-
-    /** Memory cache for I/O contexts */
-    RTMEMCACHE          hMemCacheIoCtx;
-    /** Memory cache for I/O tasks. */
-    RTMEMCACHE          hMemCacheIoTask;
-    /** Critical section protecting the disk against concurrent access. */
-    RTCRITSECT          CritSect;
-    /** Flag whether the disk is currently locked by growing write or a flush
-     * request. Other flush or growing write requests need to wait until
-     * the current one completes.
-     */
-    volatile bool       fLocked;
-    /** List of waiting requests. - Protected by the critical section. */
-    RTLISTNODE          ListWriteLocked;
-
-    /** Pointer to the L2 disk cache if any. */
-    PVDCACHE            pCache;
-};
-
-# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
-    do \
-    { \
-        AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
-                  ("Thread does not own critical section\n"));\
-    } while(0)
-
-/**
- * VBox parent read descriptor, used internally for compaction.
- */
-typedef struct VDPARENTSTATEDESC
-{
-    /** Pointer to disk descriptor. */
-    PVBOXHDD pDisk;
-    /** Pointer to image descriptor. */
-    PVDIMAGE pImage;
-} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
-
-/**
- * Transfer direction.
- */
-typedef enum VDIOCTXTXDIR
-{
-    /** Read */
-    VDIOCTXTXDIR_READ = 0,
-    /** Write */
-    VDIOCTXTXDIR_WRITE,
-    /** Flush */
-    VDIOCTXTXDIR_FLUSH,
-    /** 32bit hack */
-    VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
-} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
-
-/** Transfer function */
-typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
-/** Pointer to a transfer function. */
-typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
-
-/**
- * I/O context
- */
-typedef struct VDIOCTX
-{
-    /** Disk this is request is for. */
-    PVBOXHDD                     pDisk;
-    /** Return code. */
-    int                          rcReq;
-    /** Transfer direction */
-    VDIOCTXTXDIR                 enmTxDir;
-    /** Number of bytes left until this context completes. */
-    volatile uint32_t            cbTransferLeft;
-    /** Current offset */
-    volatile uint64_t            uOffset;
-    /** Number of bytes to transfer */
-    volatile size_t              cbTransfer;
-    /** Current image in the chain. */
-    PVDIMAGE                     pImage;
-    /** S/G buffer */
-    RTSGBUF                      SgBuf;
-    /** Flag whether the I/O context is blocked because it is in the growing list. */
-    bool                         fBlocked;
-    /** Number of data transfers currently pending. */
-    volatile uint32_t            cDataTransfersPending;
-    /** How many meta data transfers are pending. */
-    volatile uint32_t            cMetaTransfersPending;
-    /** Flag whether the request finished */
-    volatile bool                fComplete;
-    /** Temporary allocated memory which is freed
-     * when the context completes. */
-    void                        *pvAllocation;
-    /** Transfer function. */
-    PFNVDIOCTXTRANSFER           pfnIoCtxTransfer;
-    /** Next transfer part after the current one completed. */
-    PFNVDIOCTXTRANSFER           pfnIoCtxTransferNext;
-    /** Parent I/O context if any. Sets the type of the context (root/child) */
-    PVDIOCTX                     pIoCtxParent;
-    /** Type dependent data (root/child) */
-    union
-    {
-        /** Root data */
-        struct
-        {
-            /** Completion callback */
-            PFNVDASYNCTRANSFERCOMPLETE   pfnComplete;
-            /** User argument 1 passed on completion. */
-            void                        *pvUser1;
-            /** User argument 1 passed on completion. */
-            void                        *pvUser2;
-        } Root;
-        /** Child data */
-        struct
-        {
-            /** Saved start offset */
-            uint64_t                     uOffsetSaved;
-            /** Saved transfer size */
-            size_t                       cbTransferLeftSaved;
-            /** Number of bytes transferred from the parent if this context completes. */
-            size_t                       cbTransferParent;
-            /** Number of bytes to pre read */
-            size_t                       cbPreRead;
-            /** Number of bytes to post read. */
-            size_t                       cbPostRead;
-            /** Number of bytes to write left in the parent. */
-            size_t                       cbWriteParent;
-            /** Write type dependent data. */
-            union
-            {
-                /** Optimized */
-                struct
-                {
-                    /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
-                    size_t               cbFill;
-                    /** Bytes to copy instead of reading from the parent */
-                    size_t               cbWriteCopy;
-                    /** Bytes to read from the image. */
-                    size_t               cbReadImage;
-                } Optimized;
-            } Write;
-        } Child;
-    } Type;
-} VDIOCTX;
-
-typedef struct VDIOCTXDEFERRED
-{
-    /** Node in the list of deferred requests.
-     * A request can be deferred if the image is growing
-     * and the request accesses the same range or if
-     * the backend needs to read or write metadata from the disk
-     * before it can continue. */
-    RTLISTNODE NodeDeferred;
-    /** I/O context this entry points to. */
-    PVDIOCTX   pIoCtx;
-} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
-
-/**
- * I/O task.
- */
-typedef struct VDIOTASK
-{
-    /** Storage this task belongs to. */
-    PVDIOSTORAGE                 pIoStorage;
-    /** Optional completion callback. */
-    PFNVDXFERCOMPLETED           pfnComplete;
-    /** Opaque user data. */
-    void                        *pvUser;
-    /** Flag whether this is a meta data transfer. */
-    bool                         fMeta;
-    /** Type dependent data. */
-    union
-    {
-        /** User data transfer. */
-        struct
-        {
-            /** Number of bytes this task transferred. */
-            uint32_t             cbTransfer;
-            /** Pointer to the I/O context the task belongs. */
-            PVDIOCTX             pIoCtx;
-        } User;
-        /** Meta data transfer. */
-        struct
-        {
-            /** Meta transfer this task is for. */
-            PVDMETAXFER          pMetaXfer;
-        } Meta;
-    } Type;
-} VDIOTASK, *PVDIOTASK;
-
-/**
- * Storage handle.
- */
-typedef struct VDIOSTORAGE
-{
-    /** Image this storage handle belongs to. */
-    PVDIMAGE                     pImage;
-    /** AVL tree for pending async metadata transfers. */
-    PAVLRFOFFTREE                pTreeMetaXfers;
-    /** Storage handle */
-    void                        *pStorage;
-} VDIOSTORAGE;
-
-/**
- *  Metadata transfer.
- *
- *  @note This entry can't be freed if either the list is not empty or
- *  the reference counter is not 0.
- *  The assumption is that the backends don't need to read huge amounts of
- *  metadata to complete a transfer so the additional memory overhead should
- *  be relatively small.
- */
-typedef struct VDMETAXFER
-{
-    /** AVL core for fast search (the file offset is the key) */
-    AVLRFOFFNODECORE Core;
-    /** I/O storage for this transfer. */
-    PVDIOSTORAGE     pIoStorage;
-    /** Flags. */
-    uint32_t         fFlags;
-    /** List of I/O contexts waiting for this metadata transfer to complete. */
-    RTLISTNODE       ListIoCtxWaiting;
-    /** Number of references to this entry. */
-    unsigned         cRefs;
-    /** Size of the data stored with this entry. */
-    size_t           cbMeta;
-    /** Data stored - variable size. */
-    uint8_t          abData[1];
-} VDMETAXFER;
-
-/**
- * The transfer direction for the metadata.
- */
-#define VDMETAXFER_TXDIR_MASK  0x3
-#define VDMETAXFER_TXDIR_NONE  0x0
-#define VDMETAXFER_TXDIR_WRITE 0x1
-#define VDMETAXFER_TXDIR_READ  0x2
-#define VDMETAXFER_TXDIR_FLUSH 0x3
-#define VDMETAXFER_TXDIR_GET(flags)      ((flags) & VDMETAXFER_TXDIR_MASK)
-#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
-
-extern VBOXHDDBACKEND g_RawBackend;
-extern VBOXHDDBACKEND g_VmdkBackend;
-extern VBOXHDDBACKEND g_VDIBackend;
-extern VBOXHDDBACKEND g_VhdBackend;
-extern VBOXHDDBACKEND g_ParallelsBackend;
-extern VBOXHDDBACKEND g_DmgBackend;
-#ifdef VBOX_WITH_ISCSI
-extern VBOXHDDBACKEND g_ISCSIBackend;
-#endif
-
-static unsigned g_cBackends = 0;
-static PVBOXHDDBACKEND *g_apBackends = NULL;
-static PVBOXHDDBACKEND aStaticBackends[] =
-{
-    &g_VmdkBackend,
-    &g_VDIBackend,
-    &g_VhdBackend,
-    &g_ParallelsBackend,
-    &g_DmgBackend,
-    &g_RawBackend
-#ifdef VBOX_WITH_ISCSI
-    ,&g_ISCSIBackend
-#endif
-};
-
-/**
- * Supported backends for the disk cache.
- */
-extern VDCACHEBACKEND g_VciCacheBackend;
-
-static unsigned g_cCacheBackends = 0;
-static PVDCACHEBACKEND *g_apCacheBackends = NULL;
-static PVDCACHEBACKEND aStaticCacheBackends[] =
-{
-    &g_VciCacheBackend
-};
-
-/**
- * internal: add several backends.
- */
-static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
-{
-    PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
-           (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
-    if (RT_UNLIKELY(!pTmp))
-        return VERR_NO_MEMORY;
-    g_apBackends = pTmp;
-    memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
-    g_cBackends += cBackends;
-    return VINF_SUCCESS;
-}
-
-/**
- * internal: add single backend.
- */
-DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
-{
-    return vdAddBackends(&pBackend, 1);
-}
-
-/**
- * internal: add several cache backends.
- */
-static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
-{
-    PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
-           (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
-    if (RT_UNLIKELY(!pTmp))
-        return VERR_NO_MEMORY;
-    g_apCacheBackends = pTmp;
-    memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
-    g_cCacheBackends += cBackends;
-    return VINF_SUCCESS;
-}
-
-/**
- * internal: add single cache backend.
- */
-DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
-{
-    return vdAddCacheBackends(&pBackend, 1);
-}
-
-/**
- * internal: issue error message.
- */
-static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
-                   const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pDisk->pInterfaceErrorCallbacks)
-        pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * internal: thread synchronization, start read.
- */
-DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
-        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
-    return rc;
-}
-
-/**
- * internal: thread synchronization, finish read.
- */
-DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
-        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
-    return rc;
-}
-
-/**
- * internal: thread synchronization, start write.
- */
-DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
-        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
-    return rc;
-}
-
-/**
- * internal: thread synchronization, finish write.
- */
-DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
-        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
-    return rc;
-}
-
-/**
- * internal: find image format backend.
- */
-static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
-{
-    int rc = VINF_SUCCESS;
-    PCVBOXHDDBACKEND pBackend = NULL;
-
-    if (!g_apBackends)
-        VDInit();
-
-    for (unsigned i = 0; i < g_cBackends; i++)
-    {
-        if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
-        {
-            pBackend = g_apBackends[i];
-            break;
-        }
-    }
-    *ppBackend = pBackend;
-    return rc;
-}
-
-/**
- * internal: find cache format backend.
- */
-static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
-{
-    int rc = VINF_SUCCESS;
-    PCVDCACHEBACKEND pBackend = NULL;
-
-    if (!g_apCacheBackends)
-        VDInit();
-
-    for (unsigned i = 0; i < g_cCacheBackends; i++)
-    {
-        if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
-        {
-            pBackend = g_apCacheBackends[i];
-            break;
-        }
-    }
-    *ppBackend = pBackend;
-    return rc;
-}
-
-/**
- * internal: add image structure to the end of images list.
- */
-static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE 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 vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE 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--;
-}
-
-/**
- * internal: find image by index into the images list.
- */
-static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
-{
-    PVDIMAGE pImage = pDisk->pBase;
-    if (nImage == VD_LAST_IMAGE)
-        return pDisk->pLast;
-    while (pImage && nImage)
-    {
-        pImage = pImage->pNext;
-        nImage--;
-    }
-    return pImage;
-}
-
-/**
- * Internal: Tries to read the desired range from the given cache.
- *
- * @returns VBox status code.
- * @retval  VERR_VD_BLOCK_FREE if the block is not in the cache.
- *          pcbRead will be set to the number of bytes not in the cache.
- *          Everything thereafter might be in the cache.
- * @param   pCache   The cache to read from.
- * @param   uOffset  Offset of the virtual disk to read.
- * @param   pvBuf    Where to store the read data.
- * @param   cbRead   How much to read.
- * @param   pcbRead  Where to store the number of bytes actually read.
- *                   On success this indicates the number of bytes read from the cache.
- *                   If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
- *                   which are not in the cache.
- *                   In both cases everything beyond this value
- *                   might or might not be in the cache.
- */
-static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
-                             void *pvBuf, size_t cbRead, size_t *pcbRead)
-{
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
-                 pCache, uOffset, pvBuf, cbRead, pcbRead));
-
-    AssertPtr(pCache);
-    AssertPtr(pcbRead);
-
-    rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
-                                  cbRead, pcbRead);
-
-    LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
-    return rc;
-}
-
-/**
- * Internal: Writes data for the given block into the cache.
- *
- * @returns VBox status code.
- * @param   pCache     The cache to write to.
- * @param   uOffset    Offset of the virtual disk to write to teh cache.
- * @param   pcvBuf     The data to write.
- * @param   cbWrite    How much to write.
- * @param   pcbWritten How much data could be written, optional.
- */
-static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
-                              size_t cbWrite, size_t *pcbWritten)
-{
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
-                 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
-
-    AssertPtr(pCache);
-    AssertPtr(pcvBuf);
-    Assert(cbWrite > 0);
-
-    if (pcbWritten)
-        rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
-                                       cbWrite, pcbWritten);
-    else
-    {
-        size_t cbWritten = 0;
-
-        do
-        {
-            rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
-                                           cbWrite, &cbWritten);
-            uOffset += cbWritten;
-            pcvBuf   = (char *)pcvBuf + cbWritten;
-            cbWrite -= cbWritten;
-        } while (   cbWrite
-                 && RT_SUCCESS(rc));
-    }
-
-    LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
-                 rc, pcbWritten ? *pcbWritten : cbWrite));
-    return rc;
-}
-
-/**
- * Internal: Reads a given amount of data from the image chain of the disk.
- **/
-static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
-                            uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
-{
-    int rc = VINF_SUCCESS;
-    size_t cbThisRead = cbRead;
-
-    AssertPtr(pcbThisRead);
-
-    *pcbThisRead = 0;
-
-    /*
-     * Try to read from the given image.
-     * If the block is not allocated read from override chain if present.
-     */
-    rc = pImage->Backend->pfnRead(pImage->pBackendData,
-                                  uOffset, pvBuf, cbThisRead,
-                                  &cbThisRead);
-
-    if (rc == VERR_VD_BLOCK_FREE)
-    {
-        for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
-             pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
-             pCurrImage = pCurrImage->pPrev)
-        {
-            rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
-                                              uOffset, pvBuf, cbThisRead,
-                                              &cbThisRead);
-        }
-    }
-
-    if (RT_SUCCESS(rc))
-        *pcbThisRead = cbThisRead;
-
-    return rc;
-}
-
-/**
- * internal: read the specified amount of data in whatever blocks the backend
- * will give us.
- */
-static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
-                        uint64_t uOffset, void *pvBuf, size_t cbRead,
-                        bool fZeroFreeBlocks, bool fUpdateCache)
-{
-    int rc = VINF_SUCCESS;
-    size_t cbThisRead;
-    bool fAllFree = true;
-    size_t cbBufClear = 0;
-
-    /* Loop until all read. */
-    do
-    {
-        /* Search for image with allocated block. Do not attempt to read more
-         * than the previous reads marked as valid. Otherwise this would return
-         * stale data when different block sizes are used for the images. */
-        cbThisRead = cbRead;
-
-        if (   pDisk->pCache
-            && !pImageParentOverride)
-        {
-            rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
-                                   cbThisRead, &cbThisRead);
-
-            if (rc == VERR_VD_BLOCK_FREE)
-            {
-                rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
-                                      uOffset, pvBuf, cbThisRead, &cbThisRead);
-
-                /* If the read was successful, write the data back into the cache. */
-                if (   RT_SUCCESS(rc)
-                    && fUpdateCache)
-                {
-                    rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
-                                            cbThisRead, NULL);
-                }
-            }
-        }
-        else
-        {
-            /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
-             * don't want to be responsible for data corruption...
-             */
-            /*
-             * Try to read from the given image.
-             * If the block is not allocated read from override chain if present.
-             */
-            rc = pImage->Backend->pfnRead(pImage->pBackendData,
-                                          uOffset, pvBuf, cbThisRead,
-                                          &cbThisRead);
-
-            if (rc == VERR_VD_BLOCK_FREE)
-            {
-                for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
-                     pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
-                     pCurrImage = pCurrImage->pPrev)
-                {
-                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
-                                                      uOffset, pvBuf, cbThisRead,
-                                                      &cbThisRead);
-                }
-            }
-        }
-
-        /* No image in the chain contains the data for the block. */
-        if (rc == VERR_VD_BLOCK_FREE)
-        {
-            /* Fill the free space with 0 if we are told to do so
-             * or a previous read returned valid data. */
-            if (fZeroFreeBlocks || !fAllFree)
-                memset(pvBuf, '\0', cbThisRead);
-            else
-                cbBufClear += cbThisRead;
-
-            rc = VINF_SUCCESS;
-        }
-        else if (RT_SUCCESS(rc))
-        {
-            /* First not free block, fill the space before with 0. */
-            if (!fZeroFreeBlocks)
-            {
-                memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
-                cbBufClear = 0;
-                fAllFree = false;
-            }
-        }
-
-        cbRead -= cbThisRead;
-        uOffset += cbThisRead;
-        pvBuf = (char *)pvBuf + cbThisRead;
-    } while (cbRead != 0 && RT_SUCCESS(rc));
-
-    return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
-}
-
-DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
-                                  uint64_t uOffset, size_t cbTransfer,
-                                  PCRTSGSEG pcaSeg, unsigned cSeg,
-                                  void *pvAllocation,
-                                  PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
-{
-    PVDIOCTX pIoCtx = NULL;
-
-    pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
-    if (RT_LIKELY(pIoCtx))
-    {
-        pIoCtx->pDisk                 = pDisk;
-        pIoCtx->enmTxDir              = enmTxDir;
-        pIoCtx->cbTransferLeft        = cbTransfer;
-        pIoCtx->uOffset               = uOffset;
-        pIoCtx->cbTransfer            = cbTransfer;
-        pIoCtx->cDataTransfersPending = 0;
-        pIoCtx->cMetaTransfersPending = 0;
-        pIoCtx->fComplete             = false;
-        pIoCtx->fBlocked              = false;
-        pIoCtx->pvAllocation          = pvAllocation;
-        pIoCtx->pfnIoCtxTransfer      = pfnIoCtxTransfer;
-        pIoCtx->pfnIoCtxTransferNext  = NULL;
-        pIoCtx->rcReq                 = VINF_SUCCESS;
-
-        /* There is no S/G list for a flush request. */
-        if (enmTxDir != VDIOCTXTXDIR_FLUSH)
-            RTSgBufInit(&pIoCtx->SgBuf, pcaSeg, cSeg);
-        else
-            memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
-    }
-
-    return pIoCtx;
-}
-
-DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
-                                      uint64_t uOffset, size_t cbTransfer,
-                                      PCRTSGSEG paSeg, unsigned cSeg,
-                                      PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                                      void *pvUser1, void *pvUser2,
-                                      void *pvAllocation,
-                                      PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
-{
-    PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
-                                   paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
-
-    if (RT_LIKELY(pIoCtx))
-    {
-        pIoCtx->pIoCtxParent          = NULL;
-        pIoCtx->Type.Root.pfnComplete = pfnComplete;
-        pIoCtx->Type.Root.pvUser1     = pvUser1;
-        pIoCtx->Type.Root.pvUser2     = pvUser2;
-    }
-
-    LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
-    return pIoCtx;
-}
-
-DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
-                                       uint64_t uOffset, size_t cbTransfer,
-                                       PCRTSGSEG paSeg, unsigned cSeg,
-                                       PVDIOCTX pIoCtxParent, size_t cbTransferParent,
-                                       size_t cbWriteParent, void *pvAllocation,
-                                       PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
-{
-    PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
-                                   paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
-
-    AssertPtr(pIoCtxParent);
-    Assert(!pIoCtxParent->pIoCtxParent);
-
-    if (RT_LIKELY(pIoCtx))
-    {
-        pIoCtx->pIoCtxParent                   = pIoCtxParent;
-        pIoCtx->Type.Child.uOffsetSaved        = uOffset;
-        pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
-        pIoCtx->Type.Child.cbTransferParent    = cbTransferParent;
-        pIoCtx->Type.Child.cbWriteParent       = cbWriteParent;
-    }
-
-    LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
-    return pIoCtx;
-}
-
-DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
-{
-    PVDIOTASK pIoTask = NULL;
-
-    pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
-    if (pIoTask)
-    {
-        pIoTask->pIoStorage           = pIoStorage;
-        pIoTask->pfnComplete          = pfnComplete;
-        pIoTask->pvUser               = pvUser;
-        pIoTask->fMeta                = false;
-        pIoTask->Type.User.cbTransfer = cbTransfer;
-        pIoTask->Type.User.pIoCtx     = pIoCtx;
-    }
-
-    return pIoTask;
-}
-
-DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
-{
-    PVDIOTASK pIoTask = NULL;
-
-    pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
-    if (pIoTask)
-    {
-        pIoTask->pIoStorage          = pIoStorage;
-        pIoTask->pfnComplete         = pfnComplete;
-        pIoTask->pvUser              = pvUser;
-        pIoTask->fMeta               = true;
-        pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
-    }
-
-    return pIoTask;
-}
-
-DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
-{
-    LogFlow(("Freeing I/O context %#p\n", pIoCtx));
-    if (pIoCtx->pvAllocation)
-        RTMemFree(pIoCtx->pvAllocation);
-#ifdef DEBUG
-    memset(pIoCtx, 0xff, sizeof(VDIOCTX));
-#endif
-    RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
-}
-
-DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
-{
-    RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
-}
-
-DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
-{
-    AssertPtr(pIoCtx->pIoCtxParent);
-
-    RTSgBufReset(&pIoCtx->SgBuf);
-    pIoCtx->uOffset        = pIoCtx->Type.Child.uOffsetSaved;
-    pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
-}
-
-DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIMAGE pImage, PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
-{
-    PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
-
-    if (RT_LIKELY(pMetaXfer))
-    {
-        pMetaXfer->Core.Key     = uOffset;
-        pMetaXfer->Core.KeyLast = uOffset + cb - 1;
-        pMetaXfer->fFlags       = VDMETAXFER_TXDIR_NONE;
-        pMetaXfer->cbMeta       = cb;
-        pMetaXfer->pIoStorage   = pIoStorage;
-        pMetaXfer->cRefs        = 0;
-        RTListInit(&pMetaXfer->ListIoCtxWaiting);
-    }
-    return pMetaXfer;
-}
-
-DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
-{
-    PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
-
-    if (!pDeferred)
-        return VERR_NO_MEMORY;
-
-    LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
-
-    Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
-
-    RTListInit(&pDeferred->NodeDeferred);
-    pDeferred->pIoCtx = pIoCtx;
-    RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
-    pIoCtx->fBlocked = true;
-    return VINF_SUCCESS;
-}
-
-static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
-{
-    return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
-}
-
-static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
-{
-    return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
-}
-
-static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
-{
-    return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
-}
-
-
-static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
-{
-    return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
-}
-
-static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
-{
-    return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
-}
-
-static int vdIoCtxProcess(PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    PVBOXHDD pDisk = pIoCtx->pDisk;
-
-    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
-
-    RTCritSectEnter(&pDisk->CritSect);
-
-    if (   !pIoCtx->cbTransferLeft
-        && !pIoCtx->cMetaTransfersPending
-        && !pIoCtx->cDataTransfersPending
-        && !pIoCtx->pfnIoCtxTransfer)
-    {
-        rc = VINF_VD_ASYNC_IO_FINISHED;
-        goto out;
-    }
-
-    /*
-     * We complete the I/O context in case of an error
-     * if there is no I/O task pending.
-     */
-    if (   RT_FAILURE(pIoCtx->rcReq)
-        && !pIoCtx->cMetaTransfersPending
-        && !pIoCtx->cDataTransfersPending)
-    {
-        rc = VINF_VD_ASYNC_IO_FINISHED;
-        goto out;
-    }
-
-    /* Don't change anything if there is a metadata transfer pending or we are blocked. */
-    if (   pIoCtx->cMetaTransfersPending
-        || pIoCtx->fBlocked)
-    {
-        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-        goto out;
-    }
-
-    if (pIoCtx->pfnIoCtxTransfer)
-    {
-        /* Call the transfer function advancing to the next while there is no error. */
-        while (   pIoCtx->pfnIoCtxTransfer
-               && RT_SUCCESS(rc))
-        {
-            LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
-            rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
-
-            /* Advance to the next part of the transfer if the current one succeeded. */
-            if (RT_SUCCESS(rc))
-            {
-                pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
-                pIoCtx->pfnIoCtxTransferNext = NULL;
-            }
-        }
-    }
-
-    if (   RT_SUCCESS(rc)
-        && !pIoCtx->cbTransferLeft
-        && !pIoCtx->cMetaTransfersPending
-        && !pIoCtx->cDataTransfersPending)
-        rc = VINF_VD_ASYNC_IO_FINISHED;
-    else if (   RT_SUCCESS(rc)
-             || rc == VERR_VD_NOT_ENOUGH_METADATA
-             || rc == VERR_VD_IOCTX_HALT)
-        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-    else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-    {
-        ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
-        /*
-         * The I/O context completed if we have an error and there is no data
-         * or meta data transfer pending.
-         */
-        if (   !pIoCtx->cMetaTransfersPending
-            && !pIoCtx->cDataTransfersPending)
-            rc = VINF_VD_ASYNC_IO_FINISHED;
-        else
-            rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-    }
-
-out:
-    RTCritSectLeave(&pDisk->CritSect);
-
-    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
-                 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
-                 pIoCtx->fComplete));
-
-    return rc;
-}
-
-static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-
-    if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
-    {
-        rc = vdIoCtxDefer(pDisk, pIoCtx);
-        if (RT_SUCCESS(rc))
-            rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-    }
-
-    return rc;
-}
-
-static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
-{
-    Assert(pDisk->fLocked);
-    ASMAtomicXchgBool(&pDisk->fLocked, false);
-
-    /* Process any pending writes if the current request didn't caused another growing. */
-    RTCritSectEnter(&pDisk->CritSect);
-
-    if (!RTListIsEmpty(&pDisk->ListWriteLocked))
-    {
-        RTLISTNODE ListTmp;
-
-        RTListMove(&ListTmp, &pDisk->ListWriteLocked);
-        RTCritSectLeave(&pDisk->CritSect);
-
-        /* Process the list. */
-        do
-        {
-            int rc;
-            PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
-            PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
-
-            AssertPtr(pIoCtxWait);
-
-            RTListNodeRemove(&pDeferred->NodeDeferred);
-            RTMemFree(pDeferred);
-
-            Assert(!pIoCtxWait->pIoCtxParent);
-
-            pIoCtxWait->fBlocked = false;
-            LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
-
-            rc = vdIoCtxProcess(pIoCtxWait);
-            if (   rc == VINF_VD_ASYNC_IO_FINISHED
-                && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
-            {
-                LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
-                vdThreadFinishWrite(pDisk);
-                pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
-                                                  pIoCtxWait->Type.Root.pvUser2,
-                                                  pIoCtxWait->rcReq);
-                vdIoCtxFree(pDisk, pIoCtxWait);
-            }
-        } while (!RTListIsEmpty(&ListTmp));
-    }
-    else
-        RTCritSectLeave(&pDisk->CritSect);
-}
-
-/**
- * internal: read the specified amount of data in whatever blocks the backend
- * will give us - async version.
- */
-static int vdReadHelperAsync(PVDIOCTX pIoCtx)
-{
-    int rc;
-    size_t cbToRead     = pIoCtx->cbTransfer;
-    uint64_t uOffset    = pIoCtx->uOffset;
-    PVDIMAGE pCurrImage = NULL;
-    size_t cbThisRead;
-
-    /* Loop until all reads started or we have a backend which needs to read metadata. */
-    do
-    {
-        pCurrImage = pIoCtx->pImage;
-
-        /* Search for image with allocated block. Do not attempt to read more
-         * than the previous reads marked as valid. Otherwise this would return
-         * stale data when different block sizes are used for the images. */
-        cbThisRead = cbToRead;
-
-        /*
-         * Try to read from the given image.
-         * If the block is not allocated read from override chain if present.
-         */
-        rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
-                                               uOffset, cbThisRead,
-                                               pIoCtx, &cbThisRead);
-
-        if (rc == VERR_VD_BLOCK_FREE)
-        {
-            for (pCurrImage =  pCurrImage->pPrev;
-                 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
-                 pCurrImage = pCurrImage->pPrev)
-            {
-                rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
-                                                       uOffset, cbThisRead,
-                                                       pIoCtx, &cbThisRead);
-            }
-        }
-
-        /* The task state will be updated on success already, don't do it here!. */
-        if (rc == VERR_VD_BLOCK_FREE)
-        {
-            /* No image in the chain contains the data for the block. */
-            vdIoCtxSet(pIoCtx, '\0', cbThisRead);
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
-            rc = VINF_SUCCESS;
-        }
-        else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-            rc = VINF_SUCCESS;
-        else if (rc == VERR_VD_IOCTX_HALT)
-        {
-            uOffset  += cbThisRead;
-            cbToRead -= cbThisRead;
-            pIoCtx->fBlocked = true;
-        }
-
-        if (RT_FAILURE(rc))
-            break;
-
-        cbToRead -= cbThisRead;
-        uOffset  += cbThisRead;
-    } while (cbToRead != 0 && RT_SUCCESS(rc));
-
-    if (   rc == VERR_VD_NOT_ENOUGH_METADATA
-        || rc == VERR_VD_IOCTX_HALT)
-    {
-        /* Save the current state. */
-        pIoCtx->uOffset    = uOffset;
-        pIoCtx->cbTransfer = cbToRead;
-        pIoCtx->pImage     = pCurrImage;
-    }
-
-    return rc;
-}
-
-/**
- * internal: parent image read wrapper for compacting.
- */
-static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
-                        size_t cbRead)
-{
-    PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
-    return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
-                        pvBuf, cbRead, true /* fZeroFreeBlocks */,
-                        false /* fUpdateCache */);
-}
-
-/**
- * internal: mark the disk as not modified.
- */
-static void vdResetModifiedFlag(PVBOXHDD pDisk)
-{
-    if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
-    {
-        /* generate new last-modified uuid */
-        if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
-        {
-            RTUUID Uuid;
-
-            RTUuidCreate(&Uuid);
-            pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
-                                                          &Uuid);
-
-            if (pDisk->pCache)
-                pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
-                                                               &Uuid);
-        }
-
-        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
-    }
-}
-
-/**
- * internal: mark the disk as modified.
- */
-static void vdSetModifiedFlag(PVBOXHDD pDisk)
-{
-    pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
-    if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
-    {
-        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
-
-        /* First modify, so create a UUID and ensure it's written to disk. */
-        vdResetModifiedFlag(pDisk);
-
-        if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
-            pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
-    }
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation does not optimize
- * anything (except that it tries to read only that portions from parent
- * images that are really needed).
- */
-static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
-                                 PVDIMAGE pImageParentOverride,
-                                 uint64_t uOffset, size_t cbWrite,
-                                 size_t cbThisWrite, size_t cbPreRead,
-                                 size_t cbPostRead, const void *pvBuf,
-                                 void *pvTmp)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Read the data that goes before the write to fill the block. */
-    if (cbPreRead)
-    {
-        /*
-         * Updating the cache doesn't make sense here because
-         * this will be done after the complete block was written.
-         */
-        rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
-                          uOffset - cbPreRead, pvTmp, cbPreRead,
-                          true /* fZeroFreeBlocks*/,
-                          false /* fUpdateCache */);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    /* Copy the data to the right place in the buffer. */
-    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
-
-    /* Read the data that goes after the write to fill the block. */
-    if (cbPostRead)
-    {
-        /* If we have data to be written, use that instead of reading
-         * data from the image. */
-        size_t cbWriteCopy;
-        if (cbWrite > cbThisWrite)
-            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
-        else
-            cbWriteCopy = 0;
-        /* Figure out how much we cannot read from the image, because
-         * the last block to write might exceed the nominal size of the
-         * image for technical reasons. */
-        size_t cbFill;
-        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
-            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
-        else
-            cbFill = 0;
-        /* The rest must be read from the image. */
-        size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-
-        /* Now assemble the remaining data. */
-        if (cbWriteCopy)
-            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
-                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
-        if (cbReadImage)
-            rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
-                              uOffset + cbThisWrite + cbWriteCopy,
-                              (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
-                              cbReadImage, true /* fZeroFreeBlocks */,
-                              false /* fUpdateCache */);
-        if (RT_FAILURE(rc))
-            return rc;
-        /* Zero out the remainder of this block. Will never be visible, as this
-         * is beyond the limit of the image. */
-        if (cbFill)
-            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
-                   '\0', cbFill);
-    }
-
-    /* Write the full block to the virtual disk. */
-    rc = pImage->Backend->pfnWrite(pImage->pBackendData,
-                                   uOffset - cbPreRead, pvTmp,
-                                   cbPreRead + cbThisWrite + cbPostRead,
-                                   NULL, &cbPreRead, &cbPostRead, 0);
-    Assert(rc != VERR_VD_BLOCK_FREE);
-    Assert(cbPreRead == 0);
-    Assert(cbPostRead == 0);
-
-    return rc;
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation optimizes out writes
- * that do not change the data relative to the state as of the parent images.
- * All backends which support differential/growing images support this.
- */
-static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
-                                  PVDIMAGE pImageParentOverride,
-                                  uint64_t uOffset, size_t cbWrite,
-                                  size_t cbThisWrite, size_t cbPreRead,
-                                  size_t cbPostRead, const void *pvBuf,
-                                  void *pvTmp)
-{
-    size_t cbFill = 0;
-    size_t cbWriteCopy = 0;
-    size_t cbReadImage = 0;
-    int rc;
-
-    if (cbPostRead)
-    {
-        /* Figure out how much we cannot read from the image, because
-         * the last block to write might exceed the nominal size of the
-         * image for technical reasons. */
-        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
-            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
-
-        /* If we have data to be written, use that instead of reading
-         * data from the image. */
-        if (cbWrite > cbThisWrite)
-            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
-
-        /* The rest must be read from the image. */
-        cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-    }
-
-    /* Read the entire data of the block so that we can compare whether it will
-     * be modified by the write or not. */
-    rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
-                      cbPreRead + cbThisWrite + cbPostRead - cbFill,
-                      true /* fZeroFreeBlocks */,
-                      false /* fUpdateCache */);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* Check if the write would modify anything in this block. */
-    if (   !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
-        && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
-                                    (char *)pvBuf + cbThisWrite, cbWriteCopy)))
-    {
-        /* Block is completely unchanged, so no need to write anything. */
-        return VINF_SUCCESS;
-    }
-
-    /* Copy the data to the right place in the buffer. */
-    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
-
-    /* Handle the data that goes after the write to fill the block. */
-    if (cbPostRead)
-    {
-        /* Now assemble the remaining data. */
-        if (cbWriteCopy)
-            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
-                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
-        /* Zero out the remainder of this block. Will never be visible, as this
-         * is beyond the limit of the image. */
-        if (cbFill)
-            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
-                   '\0', cbFill);
-    }
-
-    /* Write the full block to the virtual disk. */
-    rc = pImage->Backend->pfnWrite(pImage->pBackendData,
-                                   uOffset - cbPreRead, pvTmp,
-                                   cbPreRead + cbThisWrite + cbPostRead,
-                                   NULL, &cbPreRead, &cbPostRead, 0);
-    Assert(rc != VERR_VD_BLOCK_FREE);
-    Assert(cbPreRead == 0);
-    Assert(cbPostRead == 0);
-
-    return rc;
-}
-
-/**
- * internal: write buffer to the image, taking care of block boundaries and
- * write optimizations.
- */
-static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
-                         PVDIMAGE pImageParentOverride, uint64_t uOffset,
-                         const void *pvBuf, size_t cbWrite,
-                         bool fUpdateCache)
-{
-    int rc;
-    unsigned fWrite;
-    size_t cbThisWrite;
-    size_t cbPreRead, cbPostRead;
-    uint64_t uOffsetCur = uOffset;
-    size_t cbWriteCur = cbWrite;
-    const void *pcvBufCur = pvBuf;
-
-    /* Loop until all written. */
-    do
-    {
-        /* Try to write the possibly partial block to the last opened image.
-         * This works when the block is already allocated in this image or
-         * if it is a full-block write (and allocation isn't suppressed below).
-         * For image formats which don't support zero blocks, it's beneficial
-         * to avoid unnecessarily allocating unchanged blocks. This prevents
-         * unwanted expanding of images. VMDK is an example. */
-        cbThisWrite = cbWriteCur;
-        fWrite =   (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
-                 ? 0 : VD_WRITE_NO_ALLOC;
-        rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
-                                       cbThisWrite, &cbThisWrite, &cbPreRead,
-                                       &cbPostRead, fWrite);
-        if (rc == VERR_VD_BLOCK_FREE)
-        {
-            void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
-            AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
-
-            if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
-            {
-                /* Optimized write, suppress writing to a so far unallocated
-                 * block if the data is in fact not changed. */
-                rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
-                                            uOffsetCur, cbWriteCur,
-                                            cbThisWrite, cbPreRead, cbPostRead,
-                                            pcvBufCur, pvTmp);
-            }
-            else
-            {
-                /* Normal write, not optimized in any way. The block will
-                 * be written no matter what. This will usually (unless the
-                 * backend has some further optimization enabled) cause the
-                 * block to be allocated. */
-                rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
-                                           uOffsetCur, cbWriteCur,
-                                           cbThisWrite, cbPreRead, cbPostRead,
-                                           pcvBufCur, pvTmp);
-            }
-            RTMemTmpFree(pvTmp);
-            if (RT_FAILURE(rc))
-                break;
-        }
-
-        cbWriteCur -= cbThisWrite;
-        uOffsetCur += cbThisWrite;
-        pcvBufCur = (char *)pcvBufCur + cbThisWrite;
-    } while (cbWriteCur != 0 && RT_SUCCESS(rc));
-
-    /* Update the cache on success */
-    if (   RT_SUCCESS(rc)
-        && pDisk->pCache
-        && fUpdateCache)
-        rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
-
-    return rc;
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation does not optimize
- * anything (except that it tries to read only that portions from parent
- * images that are really needed) - async version.
- */
-static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-
-#if 0
-
-    /* Read the data that goes before the write to fill the block. */
-    if (cbPreRead)
-    {
-        rc = vdReadHelperAsync(pIoCtxDst);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    /* Copy the data to the right place in the buffer. */
-    vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
-
-    /* Read the data that goes after the write to fill the block. */
-    if (cbPostRead)
-    {
-        /* If we have data to be written, use that instead of reading
-         * data from the image. */
-        size_t cbWriteCopy;
-        if (cbWrite > cbThisWrite)
-            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
-        else
-            cbWriteCopy = 0;
-        /* Figure out how much we cannot read from the image, because
-         * the last block to write might exceed the nominal size of the
-         * image for technical reasons. */
-        size_t cbFill;
-        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
-            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
-        else
-            cbFill = 0;
-        /* The rest must be read from the image. */
-        size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-
-        /* Now assemble the remaining data. */
-        if (cbWriteCopy)
-        {
-            vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
-            ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
-        }
-
-        if (cbReadImage)
-            rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
-                                   uOffset + cbThisWrite + cbWriteCopy,
-                                   cbReadImage);
-        if (RT_FAILURE(rc))
-            return rc;
-        /* Zero out the remainder of this block. Will never be visible, as this
-         * is beyond the limit of the image. */
-        if (cbFill)
-        {
-            vdIoCtxSet(pIoCtxDst, '\0', cbFill);
-            ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
-        }
-    }
-
-    if (   !pIoCtxDst->cbTransferLeft
-        && !pIoCtxDst->cMetaTransfersPending
-        && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
-    {
-        /* Write the full block to the virtual disk. */
-        vdIoCtxChildReset(pIoCtxDst);
-        rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
-                                            uOffset - cbPreRead,
-                                            cbPreRead + cbThisWrite + cbPostRead,
-                                            pIoCtxDst,
-                                            NULL, &cbPreRead, &cbPostRead, 0);
-        Assert(rc != VERR_VD_BLOCK_FREE);
-        Assert(cbPreRead == 0);
-        Assert(cbPostRead == 0);
-    }
-    else
-    {
-        LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
-                 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
-                 pIoCtxDst->fComplete));
-        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-    }
-
-    return rc;
-#endif
-    return VERR_NOT_IMPLEMENTED;
-}
-
-static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    PVDIMAGE pImage = pIoCtx->pImage;
-    size_t cbThisWrite    = 0;
-    size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
-    size_t cbPostRead     = pIoCtx->Type.Child.cbPostRead;
-    size_t cbWriteCopy    = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
-    size_t cbFill         = pIoCtx->Type.Child.Write.Optimized.cbFill;
-    size_t cbReadImage    = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
-    PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
-
-    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
-
-    AssertPtr(pIoCtxParent);
-    Assert(!pIoCtxParent->pIoCtxParent);
-    Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
-
-    vdIoCtxChildReset(pIoCtx);
-    cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
-    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
-
-    /* Check if the write would modify anything in this block. */
-    if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
-    {
-        RTSGBUF SgBufSrcTmp;
-
-        RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
-        RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
-        RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
-
-        if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
-        {
-            /* Block is completely unchanged, so no need to write anything. */
-            LogFlowFunc(("Block didn't changed\n"));
-            ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
-            RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
-            return VINF_VD_ASYNC_IO_FINISHED;
-        }
-    }
-
-    /* Copy the data to the right place in the buffer. */
-    RTSgBufReset(&pIoCtx->SgBuf);
-    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
-    vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
-
-    /* Handle the data that goes after the write to fill the block. */
-    if (cbPostRead)
-    {
-        /* Now assemble the remaining data. */
-        if (cbWriteCopy)
-        {
-            /*
-             * The S/G buffer of the parent needs to be cloned because
-             * it is not allowed to modify the state.
-             */
-            RTSGBUF SgBufParentTmp;
-
-            RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
-            RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
-        }
-
-        /* Zero out the remainder of this block. Will never be visible, as this
-         * is beyond the limit of the image. */
-        if (cbFill)
-        {
-            RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
-            vdIoCtxSet(pIoCtx, '\0', cbFill);
-        }
-    }
-
-    /* Write the full block to the virtual disk. */
-    RTSgBufReset(&pIoCtx->SgBuf);
-    rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
-                                        pIoCtx->uOffset - cbPreRead,
-                                        cbPreRead + cbThisWrite + cbPostRead,
-                                        pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
-    Assert(rc != VERR_VD_BLOCK_FREE);
-    Assert(cbPreRead == 0);
-    Assert(cbPostRead == 0);
-    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        rc = VINF_SUCCESS;
-    else if (rc == VERR_VD_IOCTX_HALT)
-    {
-        pIoCtx->fBlocked = true;
-        rc = VINF_SUCCESS;
-    }
-
-    return rc;
-}
-
-static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
-
-    if (pIoCtx->cbTransferLeft)
-        rc = vdReadHelperAsync(pIoCtx);
-
-    if (   RT_SUCCESS(rc)
-        && (   pIoCtx->cbTransferLeft
-            || pIoCtx->cMetaTransfersPending))
-        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-     else
-        pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
-
-    return rc;
-}
-
-/**
- * internal: write a complete block (only used for diff images), taking the
- * remaining data from parent images. This implementation optimizes out writes
- * that do not change the data relative to the state as of the parent images.
- * All backends which support differential/growing images support this - async version.
- */
-static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
-{
-    PVBOXHDD pDisk = pIoCtx->pDisk;
-    uint64_t uOffset   = pIoCtx->Type.Child.uOffsetSaved;
-    size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
-    size_t cbPreRead   = pIoCtx->Type.Child.cbPreRead;
-    size_t cbPostRead  = pIoCtx->Type.Child.cbPostRead;
-    size_t cbWrite     = pIoCtx->Type.Child.cbWriteParent;
-    size_t cbFill = 0;
-    size_t cbWriteCopy = 0;
-    size_t cbReadImage = 0;
-
-    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
-
-    AssertPtr(pIoCtx->pIoCtxParent);
-    Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
-
-    if (cbPostRead)
-    {
-        /* Figure out how much we cannot read from the image, because
-         * the last block to write might exceed the nominal size of the
-         * image for technical reasons. */
-        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
-            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
-
-        /* If we have data to be written, use that instead of reading
-         * data from the image. */
-        if (cbWrite > cbThisWrite)
-            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
-
-        /* The rest must be read from the image. */
-        cbReadImage = cbPostRead - cbWriteCopy - cbFill;
-    }
-
-    pIoCtx->Type.Child.Write.Optimized.cbFill      = cbFill;
-    pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
-    pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
-
-    /* Read the entire data of the block so that we can compare whether it will
-     * be modified by the write or not. */
-    pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
-    pIoCtx->cbTransfer     = pIoCtx->cbTransferLeft;
-    pIoCtx->uOffset       -= cbPreRead;
-
-    /* Next step */
-    pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
-    return VINF_SUCCESS;
-}
-
-/**
- * internal: write buffer to the image, taking care of block boundaries and
- * write optimizations - async version.
- */
-static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
-{
-    int rc;
-    size_t cbWrite   = pIoCtx->cbTransfer;
-    uint64_t uOffset = pIoCtx->uOffset;
-    PVDIMAGE pImage  = pIoCtx->pImage;
-    PVBOXHDD pDisk   = pIoCtx->pDisk;
-    unsigned fWrite;
-    size_t cbThisWrite;
-    size_t cbPreRead, cbPostRead;
-
-    /* Loop until all written. */
-    do
-    {
-        /* Try to write the possibly partial block to the last opened image.
-         * This works when the block is already allocated in this image or
-         * if it is a full-block write (and allocation isn't suppressed below).
-         * For image formats which don't support zero blocks, it's beneficial
-         * to avoid unnecessarily allocating unchanged blocks. This prevents
-         * unwanted expanding of images. VMDK is an example. */
-        cbThisWrite = cbWrite;
-        fWrite =   (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
-                 ? 0 : VD_WRITE_NO_ALLOC;
-        rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
-                                            cbThisWrite, pIoCtx,
-                                            &cbThisWrite, &cbPreRead,
-                                            &cbPostRead, fWrite);
-        if (rc == VERR_VD_BLOCK_FREE)
-        {
-            /* Lock the disk .*/
-            rc = vdIoCtxLockDisk(pDisk, pIoCtx);
-            if (RT_SUCCESS(rc))
-            {
-                /*
-                 * Allocate segment and buffer in one go.
-                 * A bit hackish but avoids the need to allocate memory twice.
-                 */
-                PRTSGSEG pTmp = (PRTSGSEG)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG));
-                AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
-
-                pTmp->pvSeg = pTmp + 1;
-                pTmp->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
-
-                PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
-                                                         uOffset, pTmp->cbSeg,
-                                                         pTmp, 1,
-                                                         pIoCtx, cbThisWrite,
-                                                         cbWrite,
-                                                         pTmp,
-                                                           (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
-                                                         ? vdWriteHelperStandardAsync
-                                                         : vdWriteHelperOptimizedAsync);
-                if (!VALID_PTR(pIoCtxWrite))
-                {
-                    RTMemTmpFree(pTmp);
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-
-                LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
-                             pIoCtx, pIoCtxWrite));
-
-                pIoCtxWrite->pImage                = pImage;
-                pIoCtxWrite->Type.Child.cbPreRead  = cbPreRead;
-                pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
-
-                /* Process the write request */
-                rc = vdIoCtxProcess(pIoCtxWrite);
-
-                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-                {
-                    vdIoCtxFree(pDisk, pIoCtxWrite);
-                    break;
-                }
-                else if (   rc == VINF_VD_ASYNC_IO_FINISHED
-                         && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
-                {
-                    LogFlow(("Child write request completed\n"));
-                    Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
-                    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
-                    ASMAtomicWriteBool(&pDisk->fLocked, false);
-                    vdIoCtxFree(pDisk, pIoCtxWrite);
-
-                    rc = VINF_SUCCESS;
-                }
-                else
-                {
-                    LogFlow(("Child write pending\n"));
-                    pIoCtx->fBlocked = true;
-                    rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-                    cbWrite -= cbThisWrite;
-                    uOffset += cbThisWrite;
-                    break;
-                }
-            }
-            else
-            {
-                rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-                break;
-            }
-        }
-
-        if (rc == VERR_VD_IOCTX_HALT)
-        {
-            cbWrite -= cbThisWrite;
-            uOffset += cbThisWrite;
-            pIoCtx->fBlocked = true;
-            break;
-        }
-        else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
-            break;
-
-        cbWrite -= cbThisWrite;
-        uOffset += cbThisWrite;
-    } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
-
-    if (   rc == VERR_VD_ASYNC_IO_IN_PROGRESS
-        || rc == VERR_VD_NOT_ENOUGH_METADATA
-        || rc == VERR_VD_IOCTX_HALT)
-    {
-        /*
-         * Tell the caller that we don't need to go back here because all
-         * writes are initiated.
-         */
-        if (!cbWrite)
-            rc = VINF_SUCCESS;
-
-        pIoCtx->uOffset    = uOffset;
-        pIoCtx->cbTransfer = cbWrite;
-    }
-
-    return rc;
-}
-
-/**
- * Flush helper async version.
- */
-static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    PVBOXHDD pDisk = pIoCtx->pDisk;
-    PVDIMAGE pImage = pIoCtx->pImage;
-
-    rc = vdIoCtxLockDisk(pDisk, pIoCtx);
-    if (RT_SUCCESS(rc))
-    {
-        vdResetModifiedFlag(pDisk);
-        rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
-        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-            rc = VINF_SUCCESS;
-    }
-
-    return rc;
-}
-
-/**
- * internal: scans plugin directory and loads the backends have been found.
- */
-static int vdLoadDynamicBackends()
-{
-#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
-    int rc = VINF_SUCCESS;
-    PRTDIR pPluginDir = NULL;
-
-    /* Enumerate plugin backends. */
-    char szPath[RTPATH_MAX];
-    rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* To get all entries with VBoxHDD as prefix. */
-    char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
-    if (!pszPluginFilter)
-        return VERR_NO_STR_MEMORY;
-
-    PRTDIRENTRYEX pPluginDirEntry = NULL;
-    size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
-    /* The plugins are in the same directory as the other shared libs. */
-    rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
-    if (RT_FAILURE(rc))
-    {
-        /* On Windows the above immediately signals that there are no
-         * files matching, while on other platforms enumerating the
-         * files below fails. Either way: no plugins. */
-        goto out;
-    }
-
-    pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
-    if (!pPluginDirEntry)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
-    {
-        RTLDRMOD hPlugin = NIL_RTLDRMOD;
-        PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
-        PVBOXHDDBACKEND pBackend = NULL;
-        char *pszPluginPath = NULL;
-
-        if (rc == VERR_BUFFER_OVERFLOW)
-        {
-            /* allocate new buffer. */
-            RTMemFree(pPluginDirEntry);
-            pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
-            if (!pPluginDirEntry)
-            {
-                rc = VERR_NO_MEMORY;
-                break;
-            }
-            /* Retry. */
-            rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
-            if (RT_FAILURE(rc))
-                break;
-        }
-        else if (RT_FAILURE(rc))
-            break;
-
-        /* We got the new entry. */
-        if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
-            continue;
-
-        /* Prepend the path to the libraries. */
-        pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
-        if (!pszPluginPath)
-        {
-            rc = VERR_NO_STR_MEMORY;
-            break;
-        }
-
-        rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
-        if (RT_SUCCESS(rc))
-        {
-            rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
-            if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
-            {
-                LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
-                if (RT_SUCCESS(rc))
-                    rc = VERR_SYMBOL_NOT_FOUND;
-            }
-
-            if (RT_SUCCESS(rc))
-            {
-                /* Get the function table. */
-                rc = pfnHDDFormatLoad(&pBackend);
-                if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
-                {
-                    pBackend->hPlugin = hPlugin;
-                    vdAddBackend(pBackend);
-                }
-                else
-                    LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
-            }
-            else
-                LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
-
-            if (RT_FAILURE(rc))
-                RTLdrClose(hPlugin);
-        }
-        RTStrFree(pszPluginPath);
-    }
-out:
-    if (rc == VERR_NO_MORE_FILES)
-        rc = VINF_SUCCESS;
-    RTStrFree(pszPluginFilter);
-    if (pPluginDirEntry)
-        RTMemFree(pPluginDirEntry);
-    if (pPluginDir)
-        RTDirClose(pPluginDir);
-    return rc;
-#else
-    return VINF_SUCCESS;
-#endif
-}
-
-/**
- * internal: scans plugin directory and loads the cache backends have been found.
- */
-static int vdLoadDynamicCacheBackends()
-{
-#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
-    int rc = VINF_SUCCESS;
-    PRTDIR pPluginDir = NULL;
-
-    /* Enumerate plugin backends. */
-    char szPath[RTPATH_MAX];
-    rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* To get all entries with VBoxHDD as prefix. */
-    char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
-    if (!pszPluginFilter)
-    {
-        rc = VERR_NO_STR_MEMORY;
-        return rc;
-    }
-
-    PRTDIRENTRYEX pPluginDirEntry = NULL;
-    size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
-    /* The plugins are in the same directory as the other shared libs. */
-    rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
-    if (RT_FAILURE(rc))
-    {
-        /* On Windows the above immediately signals that there are no
-         * files matching, while on other platforms enumerating the
-         * files below fails. Either way: no plugins. */
-        goto out;
-    }
-
-    pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
-    if (!pPluginDirEntry)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
-    {
-        RTLDRMOD hPlugin = NIL_RTLDRMOD;
-        PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
-        PVDCACHEBACKEND pBackend = NULL;
-        char *pszPluginPath = NULL;
-
-        if (rc == VERR_BUFFER_OVERFLOW)
-        {
-            /* allocate new buffer. */
-            RTMemFree(pPluginDirEntry);
-            pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
-            if (!pPluginDirEntry)
-            {
-                rc = VERR_NO_MEMORY;
-                break;
-            }
-            /* Retry. */
-            rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
-            if (RT_FAILURE(rc))
-                break;
-        }
-        else if (RT_FAILURE(rc))
-            break;
-
-        /* We got the new entry. */
-        if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
-            continue;
-
-        /* Prepend the path to the libraries. */
-        pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
-        if (!pszPluginPath)
-        {
-            rc = VERR_NO_STR_MEMORY;
-            break;
-        }
-
-        rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
-        if (RT_SUCCESS(rc))
-        {
-            rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
-            if (RT_FAILURE(rc) || !pfnVDCacheLoad)
-            {
-                LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
-                         VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
-                if (RT_SUCCESS(rc))
-                    rc = VERR_SYMBOL_NOT_FOUND;
-            }
-
-            if (RT_SUCCESS(rc))
-            {
-                /* Get the function table. */
-                rc = pfnVDCacheLoad(&pBackend);
-                if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
-                {
-                    pBackend->hPlugin = hPlugin;
-                    vdAddCacheBackend(pBackend);
-                }
-                else
-                    LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
-            }
-            else
-                LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
-
-            if (RT_FAILURE(rc))
-                RTLdrClose(hPlugin);
-        }
-        RTStrFree(pszPluginPath);
-    }
-out:
-    if (rc == VERR_NO_MORE_FILES)
-        rc = VINF_SUCCESS;
-    RTStrFree(pszPluginFilter);
-    if (pPluginDirEntry)
-        RTMemFree(pPluginDirEntry);
-    if (pPluginDir)
-        RTDirClose(pPluginDir);
-    return rc;
-#else
-    return VINF_SUCCESS;
-#endif
-}
-
-/**
- * VD async I/O interface open callback.
- */
-static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
-                            uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
-                            void **ppStorage)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
-
-    if (!pStorage)
-        return VERR_NO_MEMORY;
-
-    pStorage->pfnCompleted = pfnCompleted;
-
-    /* Open the file. */
-    int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
-    if (RT_SUCCESS(rc))
-    {
-        *ppStorage = pStorage;
-        return VINF_SUCCESS;
-    }
-
-    RTMemFree(pStorage);
-    return rc;
-}
-
-/**
- * VD async I/O interface close callback.
- */
-static int vdIOCloseFallback(void *pvUser, void *pvStorage)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    RTFileClose(pStorage->File);
-    RTMemFree(pStorage);
-    return VINF_SUCCESS;
-}
-
-static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
-{
-    return RTFileDelete(pcszFilename);
-}
-
-static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
-{
-    return RTFileMove(pcszSrc, pcszDst, fMove);
-}
-
-static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
-{
-    return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
-}
-
-static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
-{
-    RTFSOBJINFO info;
-    int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
-    if (RT_SUCCESS(rc))
-        *pModificationTime = info.ModificationTime;
-    return rc;
-}
-
-/**
- * VD async I/O interface callback for retrieving the file size.
- */
-static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    return RTFileGetSize(pStorage->File, pcbSize);
-}
-
-/**
- * VD async I/O interface callback for setting the file size.
- */
-static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    return RTFileSetSize(pStorage->File, cbSize);
-}
-
-/**
- * VD async I/O interface callback for a synchronous write to the file.
- */
-static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
-                              const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
-}
-
-/**
- * VD async I/O interface callback for a synchronous read from the file.
- */
-static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
-                             void *pvBuf, size_t cbRead, size_t *pcbRead)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
-}
-
-/**
- * VD async I/O interface callback for a synchronous flush of the file data.
- */
-static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
-{
-    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
-
-    return RTFileFlush(pStorage->File);
-}
-
-/**
- * VD async I/O interface callback for a asynchronous read from the file.
- */
-static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
-                                 PCRTSGSEG paSegments, size_t cSegments,
-                                 size_t cbRead, void *pvCompletion,
-                                 void **ppTask)
-{
-    return VERR_NOT_IMPLEMENTED;
-}
-
-/**
- * VD async I/O interface callback for a asynchronous write to the file.
- */
-static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
-                                  PCRTSGSEG paSegments, size_t cSegments,
-                                  size_t cbWrite, void *pvCompletion,
-                                  void **ppTask)
-{
-    return VERR_NOT_IMPLEMENTED;
-}
-
-/**
- * VD async I/O interface callback for a asynchronous flush of the file data.
- */
-static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
-                                  void *pvCompletion, void **ppTask)
-{
-    return VERR_NOT_IMPLEMENTED;
-}
-
-/**
- * Internal - Continues an I/O context after
- * it was halted because of an active transfer.
- */
-static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
-{
-    PVBOXHDD pDisk = pIoCtx->pDisk;
-    int rc = VINF_SUCCESS;
-
-    if (RT_FAILURE(rcReq))
-        ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
-
-    if (!pIoCtx->fBlocked)
-    {
-        /* Continue the transfer */
-        rc = vdIoCtxProcess(pIoCtx);
-
-        if (   rc == VINF_VD_ASYNC_IO_FINISHED
-            && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
-        {
-            LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
-            if (pIoCtx->pIoCtxParent)
-            {
-                PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
-
-                LogFlowFunc(("I/O context transfered %u bytes for the parent pIoCtxParent=%p\n",
-                             pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
-
-                /* Update the parent state. */
-                Assert(!pIoCtxParent->pIoCtxParent);
-                Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
-                Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
-                ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
-
-                if (RT_FAILURE(pIoCtx->rcReq))
-                    ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
-
-                /*
-                 * A completed child write means that we finished growing the image.
-                 * We have to process any pending writes now.
-                 */
-                Assert(pDisk->fLocked);
-                ASMAtomicWriteBool(&pDisk->fLocked, false);
-
-                /* Unblock the parent */
-                pIoCtxParent->fBlocked = false;
-
-                rc = vdIoCtxProcess(pIoCtxParent);
-
-                if (   rc == VINF_VD_ASYNC_IO_FINISHED
-                    && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
-                {
-                    LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
-                    pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
-                                                        pIoCtxParent->Type.Root.pvUser2,
-                                                        pIoCtxParent->rcReq);
-                    vdThreadFinishWrite(pDisk);
-                    vdIoCtxFree(pDisk, pIoCtxParent);
-                }
-
-                /* Process any pending writes if the current request didn't caused another growing. */
-                RTCritSectEnter(&pDisk->CritSect);
-
-                if (!RTListIsEmpty(&pDisk->ListWriteLocked) && !pDisk->fLocked)
-                {
-                    RTLISTNODE ListTmp;
-
-                    LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
-                                 pDisk->ListWriteLocked.pPrev));
-
-                    RTListMove(&ListTmp, &pDisk->ListWriteLocked);
-
-                    LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
-                                 pDisk->ListWriteLocked.pPrev));
-
-                    RTCritSectLeave(&pDisk->CritSect);
-
-                    /* Process the list. */
-                    do
-                    {
-                        PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
-                        PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
-
-                        AssertPtr(pIoCtxWait);
-
-                        RTListNodeRemove(&pDeferred->NodeDeferred);
-                        RTMemFree(pDeferred);
-
-                        Assert(!pIoCtxWait->pIoCtxParent);
-
-                        pIoCtxWait->fBlocked = false;
-                        LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
-
-                        rc = vdIoCtxProcess(pIoCtxWait);
-                        if (   rc == VINF_VD_ASYNC_IO_FINISHED
-                            && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
-                        {
-                            LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
-                            vdThreadFinishWrite(pDisk);
-                            pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
-                                                              pIoCtxWait->Type.Root.pvUser2,
-                                                              pIoCtxWait->rcReq);
-                            vdIoCtxFree(pDisk, pIoCtxWait);
-                        }
-                    } while (!RTListIsEmpty(&ListTmp));
-                }
-                else
-                    RTCritSectLeave(&pDisk->CritSect);
-            }
-            else
-            {
-                if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
-                {
-                    vdIoCtxUnlockDisk(pDisk, pIoCtx);
-                    vdThreadFinishWrite(pDisk);
-                }
-                else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
-                    vdThreadFinishWrite(pDisk);
-                else
-                {
-                    Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
-                    vdThreadFinishRead(pDisk);
-                }
-
-                LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
-                pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
-                                              pIoCtx->Type.Root.pvUser2,
-                                              pIoCtx->rcReq);
-            }
-
-            vdIoCtxFree(pDisk, pIoCtx);
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal - Called when user transfer completed.
- */
-static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
-                               PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
-                               size_t cbTransfer, int rcReq)
-{
-    int rc = VINF_SUCCESS;
-    bool fIoCtxContinue = true;
-    PVBOXHDD pDisk = pIoCtx->pDisk;
-
-    LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
-                 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
-
-    Assert(pIoCtx->cbTransferLeft >= cbTransfer);
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
-    ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
-
-    if (pfnComplete)
-    {
-        RTCritSectEnter(&pDisk->CritSect);
-        rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
-        RTCritSectLeave(&pDisk->CritSect);
-    }
-
-    if (RT_SUCCESS(rc))
-        rc = vdIoCtxContinue(pIoCtx, rcReq);
-    else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        rc = VINF_SUCCESS;
-
-    return rc;
-}
-
-/**
- * Internal - Called when a meta transfer completed.
- */
-static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
-                               PVDMETAXFER pMetaXfer, int rcReq)
-{
-    PVBOXHDD pDisk = pIoStorage->pImage->pDisk;
-    RTLISTNODE ListIoCtxWaiting;
-    bool fFlush;
-
-    LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
-                 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
-
-    RTCritSectEnter(&pDisk->CritSect);
-    fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
-    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
-
-    if (!fFlush)
-    {
-        RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
-
-        if (RT_FAILURE(rcReq))
-        {
-            /* Remove from the AVL tree. */
-            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
-            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
-            Assert(fRemoved);
-            RTMemFree(pMetaXfer);
-        }
-        else
-        {
-            /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
-            pMetaXfer->cRefs++;
-        }
-    }
-    else
-        RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
-    RTCritSectLeave(&pDisk->CritSect);
-
-    /* Go through the waiting list and continue the I/O contexts. */
-    while (!RTListIsEmpty(&ListIoCtxWaiting))
-    {
-        int rc = VINF_SUCCESS;
-        bool fContinue = true;
-        PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
-        PVDIOCTX pIoCtx = pDeferred->pIoCtx;
-        RTListNodeRemove(&pDeferred->NodeDeferred);
-
-        RTMemFree(pDeferred);
-        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
-
-        if (pfnComplete)
-        {
-            RTCritSectEnter(&pDisk->CritSect);
-            rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
-            RTCritSectLeave(&pDisk->CritSect);
-        }
-
-        LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
-
-        if (RT_SUCCESS(rc))
-        {
-            rc = vdIoCtxContinue(pIoCtx, rcReq);
-            AssertRC(rc);
-        }
-        else
-            Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
-    }
-
-    /* Remove if not used anymore. */
-    if (RT_SUCCESS(rcReq) && !fFlush)
-    {
-        RTCritSectEnter(&pDisk->CritSect);
-        pMetaXfer->cRefs--;
-        if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
-        {
-            /* Remove from the AVL tree. */
-            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
-            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
-            Assert(fRemoved);
-            RTMemFree(pMetaXfer);
-        }
-        RTCritSectLeave(&pDisk->CritSect);
-    }
-    else if (fFlush)
-        RTMemFree(pMetaXfer);
-
-    return VINF_SUCCESS;
-}
-
-static int vdIOIntReqCompleted(void *pvUser, int rcReq)
-{
-    int rc = VINF_SUCCESS;
-    PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
-    PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
-
-    LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
-
-    if (!pIoTask->fMeta)
-        rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
-                                 pIoTask->pfnComplete, pIoTask->pvUser,
-                                 pIoTask->Type.User.cbTransfer, rcReq);
-    else
-        rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
-                                 pIoTask->Type.Meta.pMetaXfer, rcReq);
-
-    vdIoTaskFree(pIoStorage->pImage->pDisk, pIoTask);
-
-    return rc;
-}
-
-/**
- * VD I/O interface callback for opening a file.
- */
-static int vdIOIntOpen(void *pvUser, const char *pszLocation,
-                       unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
-{
-    int rc = VINF_SUCCESS;
-    PVDIMAGE pImage         = (PVDIMAGE)pvUser;
-    PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
-
-    if (!pIoStorage)
-        return VERR_NO_MEMORY;
-
-    pIoStorage->pImage = pImage;
-
-    /* Create the AVl tree. */
-    pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
-    if (pIoStorage->pTreeMetaXfers)
-    {
-        rc = pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                    pszLocation, uOpenFlags,
-                                                    vdIOIntReqCompleted,
-                                                    &pIoStorage->pStorage);
-        if (RT_SUCCESS(rc))
-        {
-            *ppIoStorage = pIoStorage;
-            return VINF_SUCCESS;
-        }
-
-        RTMemFree(pIoStorage->pTreeMetaXfers);
-    }
-    else
-        rc = VERR_NO_MEMORY;
-
-    RTMemFree(pIoStorage);
-    return rc;
-}
-
-static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
-{
-    AssertMsgFailed(("Tree should be empty at this point!\n"));
-    return VINF_SUCCESS;
-}
-
-static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    int rc = pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                     pIoStorage->pStorage);
-    AssertRC(rc);
-
-    RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
-    RTMemFree(pIoStorage->pTreeMetaXfers);
-    RTMemFree(pIoStorage);
-    return VINF_SUCCESS;
-}
-
-static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                    pcszFilename);
-}
-
-static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
-                       unsigned fMove)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pcszSrc, pcszDst, fMove);
-}
-
-static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
-                               int64_t *pcbFreeSpace)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
-                                                          pcszFilename,
-                                                          pcbFreeSpace);
-}
-
-static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
-                                      PRTTIMESPEC pModificationTime)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
-                                                                 pcszFilename,
-                                                                 pModificationTime);
-}
-
-static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
-                          uint64_t *pcbSize)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                    pIoStorage->pStorage,
-                                                    pcbSize);
-}
-
-static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
-                          uint64_t cbSize)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pIoStorage->pStorage,
-                                                     cbSize);
-}
-
-static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                            uint64_t uOffset, const void *pvBuf,
-                            size_t cbWrite, size_t *pcbWritten)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pIoStorage->pStorage,
-                                                       uOffset, pvBuf, cbWrite,
-                                                       pcbWritten);
-}
-
-static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                           uint64_t uOffset, void *pvBuf, size_t cbRead,
-                           size_t *pcbRead)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pIoStorage->pStorage,
-                                                      uOffset, pvBuf, cbRead,
-                                                      pcbRead);
-}
-
-static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pIoStorage->pStorage);
-}
-
-static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                uint64_t uOffset, PVDIOCTX pIoCtx,
-                                size_t cbRead)
-{
-    int rc = VINF_SUCCESS;
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-
-    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
-                 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    /* Build the S/G array and spawn a new I/O task */
-    while (cbRead)
-    {
-        RTSGSEG  aSeg[VD_IO_TASK_SEGMENTS_MAX];
-        unsigned cSegments  = VD_IO_TASK_SEGMENTS_MAX;
-        size_t   cbTaskRead = 0;
-
-        cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
-
-        AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
-
-        LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
-
-#ifdef RT_STRICT
-        for (unsigned i = 0; i < cSegments; i++)
-                AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
-                          ("Segment %u is invalid\n", i));
-#endif
-
-        PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
-
-        if (!pIoTask)
-            return VERR_NO_MEMORY;
-
-        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
-
-        void *pvTask;
-        rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
-                                                         pIoStorage->pStorage,
-                                                         uOffset, aSeg, cSegments,
-                                                         cbTaskRead, pIoTask,
-                                                         &pvTask);
-        if (RT_SUCCESS(rc))
-        {
-            AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
-            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
-            vdIoTaskFree(pDisk, pIoTask);
-        }
-        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
-        {
-            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
-            break;
-        }
-
-        uOffset += cbTaskRead;
-        cbRead  -= cbTaskRead;
-    }
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                 uint64_t uOffset, PVDIOCTX pIoCtx,
-                                 size_t cbWrite,
-                                 PFNVDXFERCOMPLETED pfnComplete,
-                                 void *pvCompleteUser)
-{
-    int rc = VINF_SUCCESS;
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-
-    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
-                 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    /* Build the S/G array and spawn a new I/O task */
-    while (cbWrite)
-    {
-        RTSGSEG  aSeg[VD_IO_TASK_SEGMENTS_MAX];
-        unsigned cSegments   = VD_IO_TASK_SEGMENTS_MAX;
-        size_t   cbTaskWrite = 0;
-
-        cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
-
-        AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
-
-        LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
-
-#ifdef DEBUG
-        for (unsigned i = 0; i < cSegments; i++)
-                AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
-                          ("Segment %u is invalid\n", i));
-#endif
-
-        PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
-
-        if (!pIoTask)
-            return VERR_NO_MEMORY;
-
-        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
-
-        void *pvTask;
-        rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
-                                                          pIoStorage->pStorage,
-                                                          uOffset, aSeg, cSegments,
-                                                          cbTaskWrite, pIoTask,
-                                                          &pvTask);
-        if (RT_SUCCESS(rc))
-        {
-            AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
-            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
-            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
-            vdIoTaskFree(pDisk, pIoTask);
-        }
-        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
-        {
-            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
-            break;
-        }
-
-        uOffset += cbTaskWrite;
-        cbWrite -= cbTaskWrite;
-    }
-
-    return rc;
-}
-
-static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                uint64_t uOffset, void *pvBuf,
-                                size_t cbRead, PVDIOCTX pIoCtx,
-                                PPVDMETAXFER ppMetaXfer,
-                                PFNVDXFERCOMPLETED pfnComplete,
-                                void *pvCompleteUser)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    int rc = VINF_SUCCESS;
-    RTSGSEG Seg;
-    PVDIOTASK pIoTask;
-    PVDMETAXFER pMetaXfer = NULL;
-    void *pvTask = NULL;
-
-    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
-                 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
-    if (!pMetaXfer)
-    {
-#ifdef RT_STRICT
-        pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
-        AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
-                  ("Overlapping meta transfers!\n"));
-#endif
-
-        /* Allocate a new meta transfer. */
-        pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbRead);
-        if (!pMetaXfer)
-            return VERR_NO_MEMORY;
-
-        pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
-        if (!pIoTask)
-        {
-            RTMemFree(pMetaXfer);
-            return VERR_NO_MEMORY;
-        }
-
-        Seg.cbSeg = cbRead;
-        Seg.pvSeg = pMetaXfer->abData;
-
-        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
-        rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
-                                                         pIoStorage->pStorage,
-                                                         uOffset, &Seg, 1,
-                                                         cbRead, pIoTask,
-                                                         &pvTask);
-
-        if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        {
-            bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
-            Assert(fInserted);
-        }
-        else
-            RTMemFree(pMetaXfer);
-
-        if (RT_SUCCESS(rc))
-        {
-            VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
-            vdIoTaskFree(pDisk, pIoTask);
-        }
-        else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
-            rc = VERR_VD_NOT_ENOUGH_METADATA;
-    }
-
-    Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
-
-    if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-    {
-        /* If it is pending add the request to the list. */
-        if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
-        {
-            PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
-            AssertPtr(pDeferred);
-
-            RTListInit(&pDeferred->NodeDeferred);
-            pDeferred->pIoCtx = pIoCtx;
-
-            ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
-            RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
-            rc = VERR_VD_NOT_ENOUGH_METADATA;
-        }
-        else
-        {
-            /* Transfer the data. */
-            pMetaXfer->cRefs++;
-            Assert(pMetaXfer->cbMeta >= cbRead);
-            Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
-            memcpy(pvBuf, pMetaXfer->abData, cbRead);
-            *ppMetaXfer = pMetaXfer;
-        }
-    }
-
-    return rc;
-}
-
-static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                 uint64_t uOffset, void *pvBuf,
-                                 size_t cbWrite, PVDIOCTX pIoCtx,
-                                 PFNVDXFERCOMPLETED pfnComplete,
-                                 void *pvCompleteUser)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    int rc = VINF_SUCCESS;
-    RTSGSEG Seg;
-    PVDIOTASK pIoTask;
-    PVDMETAXFER pMetaXfer = NULL;
-    bool fInTree = false;
-    void *pvTask = NULL;
-
-    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
-                 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
-    if (!pMetaXfer)
-    {
-        /* Allocate a new meta transfer. */
-        pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbWrite);
-        if (!pMetaXfer)
-            return VERR_NO_MEMORY;
-    }
-    else
-    {
-        Assert(pMetaXfer->cbMeta >= cbWrite);
-        Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
-        fInTree = true;
-    }
-
-    Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
-
-    pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
-    if (!pIoTask)
-    {
-        RTMemFree(pMetaXfer);
-        return VERR_NO_MEMORY;
-    }
-
-    memcpy(pMetaXfer->abData, pvBuf, cbWrite);
-    Seg.cbSeg = cbWrite;
-    Seg.pvSeg = pMetaXfer->abData;
-
-    ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
-
-    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
-    rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
-                                                      pIoStorage->pStorage,
-                                                      uOffset, &Seg, 1,
-                                                      cbWrite, pIoTask,
-                                                      &pvTask);
-    if (RT_SUCCESS(rc))
-    {
-        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
-        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
-        vdIoTaskFree(pDisk, pIoTask);
-        if (fInTree && !pMetaXfer->cRefs)
-        {
-            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
-            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
-            AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
-            RTMemFree(pMetaXfer);
-            pMetaXfer = NULL;
-        }
-    }
-    else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-    {
-        PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
-        AssertPtr(pDeferred);
-
-        RTListInit(&pDeferred->NodeDeferred);
-        pDeferred->pIoCtx = pIoCtx;
-
-        if (!fInTree)
-        {
-            bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
-            Assert(fInserted);
-        }
-
-        RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
-    }
-    else
-    {
-        RTMemFree(pMetaXfer);
-        pMetaXfer = NULL;
-    }
-
-    return rc;
-}
-
-static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    Assert(   VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
-           || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
-    Assert(pMetaXfer->cRefs > 0);
-
-    pMetaXfer->cRefs--;
-    if (   !pMetaXfer->cRefs
-        && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
-        && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
-    {
-        /* Free the meta data entry. */
-        LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
-        bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
-        AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
-
-        RTMemFree(pMetaXfer);
-    }
-}
-
-static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
-                             PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
-                             void *pvCompleteUser)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    int rc = VINF_SUCCESS;
-    PVDIOTASK pIoTask;
-    PVDMETAXFER pMetaXfer = NULL;
-    void *pvTask = NULL;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
-                 pvUser, pIoStorage, pIoCtx));
-
-    /* Allocate a new meta transfer. */
-    pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, 0, 0);
-    if (!pMetaXfer)
-        return VERR_NO_MEMORY;
-
-    pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
-    if (!pIoTask)
-    {
-        RTMemFree(pMetaXfer);
-        return VERR_NO_MEMORY;
-    }
-
-    ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
-
-    PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
-    AssertPtr(pDeferred);
-
-    RTListInit(&pDeferred->NodeDeferred);
-    pDeferred->pIoCtx = pIoCtx;
-
-    RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
-    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
-    rc = pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                      pIoStorage->pStorage,
-                                                      pIoTask, &pvTask);
-    if (RT_SUCCESS(rc))
-    {
-        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
-        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
-        vdIoTaskFree(pDisk, pIoTask);
-        RTMemFree(pDeferred);
-        RTMemFree(pMetaXfer);
-    }
-    else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
-        RTMemFree(pMetaXfer);
-
-    return rc;
-}
-
-static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
-                                 void *pvBuf, size_t cbBuf)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    size_t cbCopied = 0;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
-    Assert(cbCopied == cbBuf);
-
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
-
-    return cbCopied;
-}
-
-static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
-                                   void *pvBuf, size_t cbBuf)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    size_t cbCopied = 0;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
-    Assert(cbCopied == cbBuf);
-
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
-
-    return cbCopied;
-}
-
-static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    size_t cbSet = 0;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    cbSet = vdIoCtxSet(pIoCtx, ch, cb);
-    Assert(cbSet == cb);
-
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
-
-    return cbSet;
-}
-
-static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
-                                         PRTSGSEG paSeg, unsigned *pcSeg,
-                                         size_t cbData)
-{
-    PVDIMAGE pImage = (PVDIMAGE)pvUser;
-    PVBOXHDD pDisk  = pImage->pDisk;
-    size_t cbCreated = 0;
-
-    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
-
-    cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
-    Assert(!paSeg || cbData == cbCreated);
-
-    return cbCreated;
-}
-
-static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
-                                  size_t cbCompleted)
-{
-    /* Continue */
-    pIoCtx->fBlocked = false;
-    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
-
-    /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
-     * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
-    if (!pIoCtx->cbTransferLeft)
-        pIoCtx->pfnIoCtxTransfer = NULL;
-
-    vdIoCtxContinue(pIoCtx, rcReq);
-}
-
-/**
- * VD I/O interface callback for opening a file (limited version for VDGetFormat).
- */
-static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
-                              uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
-{
-    int rc = VINF_SUCCESS;
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
-
-    if (!pIoStorage)
-        return VERR_NO_MEMORY;
-
-    rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
-                                        NULL, &pIoStorage->pStorage);
-    if (RT_SUCCESS(rc))
-        *ppIoStorage = pIoStorage;
-    else
-        RTMemFree(pIoStorage);
-
-    return rc;
-}
-
-static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
-    AssertRC(rc);
-
-    RTMemFree(pIoStorage);
-    return VINF_SUCCESS;
-}
-
-static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
-}
-
-static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
-                              const char *pcszDst, unsigned fMove)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
-}
-
-static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
-                                      int64_t *pcbFreeSpace)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
-}
-
-static int vdIOIntGetModificationTimeLimited(void *pvUser,
-                                             const char *pcszFilename,
-                                             PRTTIMESPEC pModificationTime)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
-}
-
-static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                 uint64_t *pcbSize)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
-}
-
-static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                 uint64_t cbSize)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
-}
-
-static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                   uint64_t uOffset, const void *pvBuf,
-                                   size_t cbWrite, size_t *pcbWritten)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
-}
-
-static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
-                                  uint64_t uOffset, void *pvBuf, size_t cbRead,
-                                  size_t *pcbRead)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
-}
-
-static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
-{
-    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
-    return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
-}
-
-/**
- * internal: send output to the log (unconditionally).
- */
-int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
-{
-    NOREF(pvUser);
-    RTLogPrintfV(pszFormat, args);
-    return VINF_SUCCESS;
-}
-
-DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
-                                                         pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-/**
- * Initializes HDD backends.
- *
- * @returns VBox status code.
- */
-VBOXDDU_DECL(int) VDInit(void)
-{
-    int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
-    if (RT_SUCCESS(rc))
-    {
-        rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
-        if (RT_SUCCESS(rc))
-        {
-            rc = vdLoadDynamicBackends();
-            if (RT_SUCCESS(rc))
-                rc = vdLoadDynamicCacheBackends();
-        }
-    }
-    LogRel(("VDInit finished\n"));
-    return rc;
-}
-
-/**
- * Destroys loaded HDD backends.
- *
- * @returns VBox status code.
- */
-VBOXDDU_DECL(int) VDShutdown(void)
-{
-    PVBOXHDDBACKEND *pBackends = g_apBackends;
-    PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
-    unsigned cBackends = g_cBackends;
-
-    if (!pBackends)
-        return VERR_INTERNAL_ERROR;
-
-    g_cBackends = 0;
-    g_apBackends = NULL;
-
-#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
-    for (unsigned i = 0; i < cBackends; i++)
-        if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
-            RTLdrClose(pBackends[i]->hPlugin);
-#endif
-
-    /* Clear the supported cache backends. */
-    cBackends = g_cCacheBackends;
-    g_cCacheBackends = 0;
-    g_apCacheBackends = NULL;
-
-#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
-    for (unsigned i = 0; i < cBackends; i++)
-        if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
-            RTLdrClose(pCacheBackends[i]->hPlugin);
-#endif
-
-    if (pCacheBackends)
-        RTMemFree(pCacheBackends);
-    RTMemFree(pBackends);
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Lists all HDD backends and their capabilities in a caller-provided buffer.
- *
- * @returns VBox status code.
- *          VERR_BUFFER_OVERFLOW if not enough space is passed.
- * @param   cEntriesAlloc   Number of list entries available.
- * @param   pEntries        Pointer to array for the entries.
- * @param   pcEntriesUsed   Number of entries returned.
- */
-VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
-                                unsigned *pcEntriesUsed)
-{
-    int rc = VINF_SUCCESS;
-    PRTDIR pPluginDir = NULL;
-    unsigned cEntries = 0;
-
-    LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
-    /* Check arguments. */
-    AssertMsgReturn(cEntriesAlloc,
-                    ("cEntriesAlloc=%u\n", cEntriesAlloc),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(VALID_PTR(pEntries),
-                    ("pEntries=%#p\n", pEntries),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(VALID_PTR(pcEntriesUsed),
-                    ("pcEntriesUsed=%#p\n", pcEntriesUsed),
-                    VERR_INVALID_PARAMETER);
-    if (!g_apBackends)
-        VDInit();
-
-    if (cEntriesAlloc < g_cBackends)
-    {
-        *pcEntriesUsed = g_cBackends;
-        return VERR_BUFFER_OVERFLOW;
-    }
-
-    for (unsigned i = 0; i < g_cBackends; i++)
-    {
-        pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
-        pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
-        pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
-        pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
-        pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
-        pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
-    }
-
-    LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
-    *pcEntriesUsed = g_cBackends;
-    return rc;
-}
-
-/**
- * Lists the capabilities of a backend identified by its name.
- *
- * @returns VBox status code.
- * @param   pszBackend      The backend name.
- * @param   pEntries        Pointer to an entry.
- */
-VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
-{
-    LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
-    /* Check arguments. */
-    AssertMsgReturn(VALID_PTR(pszBackend),
-                    ("pszBackend=%#p\n", pszBackend),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(VALID_PTR(pEntry),
-                    ("pEntry=%#p\n", pEntry),
-                    VERR_INVALID_PARAMETER);
-    if (!g_apBackends)
-        VDInit();
-
-    /* Go through loaded backends. */
-    for (unsigned i = 0; i < g_cBackends; i++)
-    {
-        if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
-        {
-            pEntry->pszBackend = g_apBackends[i]->pszBackendName;
-            pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
-            pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
-            pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
-            return VINF_SUCCESS;
-        }
-    }
-
-    return VERR_NOT_FOUND;
-}
-
-/**
- * Allocates and initializes an empty HDD container.
- * No image files are opened.
- *
- * @returns VBox status code.
- * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
- * @param   enmType         Type of the image container.
- * @param   ppDisk          Where to store the reference to HDD container.
- */
-VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
-{
-    int rc = VINF_SUCCESS;
-    PVBOXHDD pDisk = NULL;
-
-    LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
-    do
-    {
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(ppDisk),
-                           ("ppDisk=%#p\n", ppDisk),
-                           rc = VERR_INVALID_PARAMETER);
-
-        pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
-        if (pDisk)
-        {
-            pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
-            pDisk->enmType      = enmType;
-            pDisk->cImages      = 0;
-            pDisk->pBase        = NULL;
-            pDisk->pLast        = NULL;
-            pDisk->cbSize       = 0;
-            pDisk->PCHSGeometry.cCylinders = 0;
-            pDisk->PCHSGeometry.cHeads     = 0;
-            pDisk->PCHSGeometry.cSectors   = 0;
-            pDisk->LCHSGeometry.cCylinders = 0;
-            pDisk->LCHSGeometry.cHeads     = 0;
-            pDisk->LCHSGeometry.cSectors   = 0;
-            pDisk->pVDIfsDisk  = pVDIfsDisk;
-            pDisk->pInterfaceError = NULL;
-            pDisk->pInterfaceErrorCallbacks = NULL;
-            pDisk->pInterfaceThreadSync = NULL;
-            pDisk->pInterfaceThreadSyncCallbacks = NULL;
-            pDisk->fLocked = false;
-            RTListInit(&pDisk->ListWriteLocked);
-
-            /* Create the I/O ctx cache */
-            rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
-                                  NULL, NULL, NULL, 0);
-            if (RT_FAILURE(rc))
-            {
-                RTMemFree(pDisk);
-                break;
-            }
-
-            /* Create the I/O task cache */
-            rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
-                                  NULL, NULL, NULL, 0);
-            if (RT_FAILURE(rc))
-            {
-                RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
-                RTMemFree(pDisk);
-                break;
-            }
-
-            /* Create critical section. */
-            rc = RTCritSectInit(&pDisk->CritSect);
-            if (RT_FAILURE(rc))
-            {
-                RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
-                RTMemCacheDestroy(pDisk->hMemCacheIoTask);
-                RTMemFree(pDisk);
-                break;
-            }
-
-            pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
-            if (pDisk->pInterfaceError)
-                pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
-
-            pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
-            if (pDisk->pInterfaceThreadSync)
-                pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
-
-            /* Create fallback I/O callback table */
-            pDisk->VDIIOCallbacks.cbSize                 = sizeof(VDINTERFACEIO);
-            pDisk->VDIIOCallbacks.enmInterface           = VDINTERFACETYPE_IO;
-            pDisk->VDIIOCallbacks.pfnOpen                = vdIOOpenFallback;
-            pDisk->VDIIOCallbacks.pfnClose               = vdIOCloseFallback;
-            pDisk->VDIIOCallbacks.pfnDelete              = vdIODeleteFallback;
-            pDisk->VDIIOCallbacks.pfnMove                = vdIOMoveFallback;
-            pDisk->VDIIOCallbacks.pfnGetFreeSpace        = vdIOGetFreeSpaceFallback;
-            pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
-            pDisk->VDIIOCallbacks.pfnGetSize             = vdIOGetSizeFallback;
-            pDisk->VDIIOCallbacks.pfnSetSize             = vdIOSetSizeFallback;
-            pDisk->VDIIOCallbacks.pfnReadSync            = vdIOReadSyncFallback;
-            pDisk->VDIIOCallbacks.pfnWriteSync           = vdIOWriteSyncFallback;
-            pDisk->VDIIOCallbacks.pfnFlushSync           = vdIOFlushSyncFallback;
-            pDisk->VDIIOCallbacks.pfnReadAsync           = vdIOReadAsyncFallback;
-            pDisk->VDIIOCallbacks.pfnWriteAsync          = vdIOWriteAsyncFallback;
-            pDisk->VDIIOCallbacks.pfnFlushAsync          = vdIOFlushAsyncFallback;
-
-            /*
-             * Create the internal I/O callback table.
-             * The interface is per-image but no need to duplicate the
-             * callback table every time.
-             */
-            pDisk->VDIIOIntCallbacks.cbSize                 = sizeof(VDINTERFACEIOINT);
-            pDisk->VDIIOIntCallbacks.enmInterface           = VDINTERFACETYPE_IOINT;
-            pDisk->VDIIOIntCallbacks.pfnOpen                = vdIOIntOpen;
-            pDisk->VDIIOIntCallbacks.pfnClose               = vdIOIntClose;
-            pDisk->VDIIOIntCallbacks.pfnDelete              = vdIOIntDelete;
-            pDisk->VDIIOIntCallbacks.pfnMove                = vdIOIntMove;
-            pDisk->VDIIOIntCallbacks.pfnGetFreeSpace        = vdIOIntGetFreeSpace;
-            pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
-            pDisk->VDIIOIntCallbacks.pfnGetSize             = vdIOIntGetSize;
-            pDisk->VDIIOIntCallbacks.pfnSetSize             = vdIOIntSetSize;
-            pDisk->VDIIOIntCallbacks.pfnReadSync            = vdIOIntReadSync;
-            pDisk->VDIIOIntCallbacks.pfnWriteSync           = vdIOIntWriteSync;
-            pDisk->VDIIOIntCallbacks.pfnFlushSync           = vdIOIntFlushSync;
-            pDisk->VDIIOIntCallbacks.pfnReadUserAsync       = vdIOIntReadUserAsync;
-            pDisk->VDIIOIntCallbacks.pfnWriteUserAsync      = vdIOIntWriteUserAsync;
-            pDisk->VDIIOIntCallbacks.pfnReadMetaAsync       = vdIOIntReadMetaAsync;
-            pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync      = vdIOIntWriteMetaAsync;
-            pDisk->VDIIOIntCallbacks.pfnMetaXferRelease     = vdIOIntMetaXferRelease;
-            pDisk->VDIIOIntCallbacks.pfnFlushAsync          = vdIOIntFlushAsync;
-            pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom       = vdIOIntIoCtxCopyFrom;
-            pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo         = vdIOIntIoCtxCopyTo;
-            pDisk->VDIIOIntCallbacks.pfnIoCtxSet            = vdIOIntIoCtxSet;
-            pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
-            pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted      = vdIOIntIoCtxCompleted;
-
-            *ppDisk = pDisk;
-        }
-        else
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-    } while (0);
-
-    LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
-    return rc;
-}
-
-/**
- * Destroys HDD container.
- * If container has opened image files they will be closed.
- *
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
-{
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreak(pDisk);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-        VDCloseAll(pDisk);
-        RTCritSectDelete(&pDisk->CritSect);
-        RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
-        RTMemCacheDestroy(pDisk->hMemCacheIoTask);
-        RTMemFree(pDisk);
-    } while (0);
-    LogFlowFunc(("returns\n"));
-}
-
-/**
- * Try to get the backend name which can use this image.
- *
- * @returns VBox status code.
- *          VINF_SUCCESS if a plugin was found.
- *                       ppszFormat contains the string which can be used as backend name.
- *          VERR_NOT_SUPPORTED if no backend was found.
- * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pszFilename     Name of the image file for which the backend is queried.
- * @param   ppszFormat      Receives pointer of the UTF-8 string which contains the format name.
- *                          The returned pointer must be freed using RTStrFree().
- */
-VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                              const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
-{
-    int rc = VERR_NOT_SUPPORTED;
-    VDINTERFACEIOINT VDIIOIntCallbacks;
-    VDINTERFACE      VDIIOInt;
-    VDINTERFACEIO    VDIIOCallbacksFallback;
-    PVDINTERFACE     pInterfaceIO;
-    PVDINTERFACEIO   pInterfaceIOCallbacks;
-
-    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
-    /* Check arguments. */
-    AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
-                    ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(VALID_PTR(ppszFormat),
-                    ("ppszFormat=%#p\n", ppszFormat),
-                    VERR_INVALID_PARAMETER);
-    AssertMsgReturn(VALID_PTR(ppszFormat),
-                    ("penmType=%#p\n", penmType),
-                    VERR_INVALID_PARAMETER);
-
-    if (!g_apBackends)
-        VDInit();
-
-    pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
-    if (!pInterfaceIO)
-    {
-        /*
-         * Caller doesn't provide an I/O interface, create our own using the
-         * native file API.
-         */
-        VDIIOCallbacksFallback.cbSize                    = sizeof(VDINTERFACEIO);
-        VDIIOCallbacksFallback.enmInterface              = VDINTERFACETYPE_IO;
-        VDIIOCallbacksFallback.pfnOpen                   = vdIOOpenFallback;
-        VDIIOCallbacksFallback.pfnClose                  = vdIOCloseFallback;
-        VDIIOCallbacksFallback.pfnDelete                 = vdIODeleteFallback;
-        VDIIOCallbacksFallback.pfnMove                   = vdIOMoveFallback;
-        VDIIOCallbacksFallback.pfnGetFreeSpace           = vdIOGetFreeSpaceFallback;
-        VDIIOCallbacksFallback.pfnGetModificationTime    = vdIOGetModificationTimeFallback;
-        VDIIOCallbacksFallback.pfnGetSize                = vdIOGetSizeFallback;
-        VDIIOCallbacksFallback.pfnSetSize                = vdIOSetSizeFallback;
-        VDIIOCallbacksFallback.pfnReadSync               = vdIOReadSyncFallback;
-        VDIIOCallbacksFallback.pfnWriteSync              = vdIOWriteSyncFallback;
-        VDIIOCallbacksFallback.pfnFlushSync              = vdIOFlushSyncFallback;
-        pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
-    }
-    else
-        pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
-
-    /* Set up the internal I/O interface. */
-    AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
-                 VERR_INVALID_PARAMETER);
-    VDIIOIntCallbacks.cbSize                    = sizeof(VDINTERFACEIOINT);
-    VDIIOIntCallbacks.enmInterface              = VDINTERFACETYPE_IOINT;
-    VDIIOIntCallbacks.pfnOpen                   = vdIOIntOpenLimited;
-    VDIIOIntCallbacks.pfnClose                  = vdIOIntCloseLimited;
-    VDIIOIntCallbacks.pfnDelete                 = vdIOIntDeleteLimited;
-    VDIIOIntCallbacks.pfnMove                   = vdIOIntMoveLimited;
-    VDIIOIntCallbacks.pfnGetFreeSpace           = vdIOIntGetFreeSpaceLimited;
-    VDIIOIntCallbacks.pfnGetModificationTime    = vdIOIntGetModificationTimeLimited;
-    VDIIOIntCallbacks.pfnGetSize                = vdIOIntGetSizeLimited;
-    VDIIOIntCallbacks.pfnSetSize                = vdIOIntSetSizeLimited;
-    VDIIOIntCallbacks.pfnReadSync               = vdIOIntReadSyncLimited;
-    VDIIOIntCallbacks.pfnWriteSync              = vdIOIntWriteSyncLimited;
-    VDIIOIntCallbacks.pfnFlushSync              = vdIOIntFlushSyncLimited;
-    VDIIOIntCallbacks.pfnReadUserAsync          = NULL;
-    VDIIOIntCallbacks.pfnWriteUserAsync         = NULL;
-    VDIIOIntCallbacks.pfnReadMetaAsync          = NULL;
-    VDIIOIntCallbacks.pfnWriteMetaAsync         = NULL;
-    VDIIOIntCallbacks.pfnFlushAsync             = NULL;
-    rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                        &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
-    AssertRC(rc);
-
-    /* Find the backend supporting this file format. */
-    for (unsigned i = 0; i < g_cBackends; i++)
-    {
-        if (g_apBackends[i]->pfnCheckIfValid)
-        {
-            rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
-                                                  pVDIfsImage, penmType);
-            if (    RT_SUCCESS(rc)
-                /* The correct backend has been found, but there is a small
-                 * incompatibility so that the file cannot be used. Stop here
-                 * and signal success - the actual open will of course fail,
-                 * but that will create a really sensible error message. */
-                ||  (   rc != VERR_VD_GEN_INVALID_HEADER
-                     && rc != VERR_VD_VDI_INVALID_HEADER
-                     && rc != VERR_VD_VMDK_INVALID_HEADER
-                     && rc != VERR_VD_ISCSI_INVALID_HEADER
-                     && rc != VERR_VD_VHD_INVALID_HEADER
-                     && rc != VERR_VD_RAW_INVALID_HEADER
-                     && rc != VERR_VD_PARALLELS_INVALID_HEADER
-                     && rc != VERR_VD_DMG_INVALID_HEADER))
-            {
-                /* Copy the name into the new string. */
-                char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
-                if (!pszFormat)
-                {
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-                *ppszFormat = pszFormat;
-                rc = VINF_SUCCESS;
-                break;
-            }
-            rc = VERR_NOT_SUPPORTED;
-        }
-    }
-
-    /* Try the cache backends. */
-    if (rc == VERR_NOT_SUPPORTED)
-    {
-        for (unsigned i = 0; i < g_cCacheBackends; i++)
-        {
-            if (g_apCacheBackends[i]->pfnProbe)
-            {
-                rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
-                                                    pVDIfsImage);
-                if (    RT_SUCCESS(rc)
-                    ||  (rc != VERR_VD_GEN_INVALID_HEADER))
-                {
-                    /* Copy the name into the new string. */
-                    char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
-                    if (!pszFormat)
-                    {
-                        rc = VERR_NO_MEMORY;
-                        break;
-                    }
-                    *ppszFormat = pszFormat;
-                    rc = VINF_SUCCESS;
-                    break;
-                }
-                rc = VERR_NOT_SUPPORTED;
-            }
-        }
-    }
-
-    LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
-    return rc;
-}
-
-/**
- * Opens an image file.
- *
- * The first opened image file in 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 another 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 is opened in read-only mode if a read/write open is not possible.
- * Use VDIsReadOnly to check open mode.
- *
- * @returns VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use.
- * @param   pszFilename     Name of the image file to open.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- */
-VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
-                         const char *pszFilename, unsigned uOpenFlags,
-                         PVDINTERFACE pVDIfsImage)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-    PVDIMAGE pImage = NULL;
-
-    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
-                 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
-                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        /* Set up image descriptor. */
-        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
-        if (!pImage)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pImage->pszFilename = RTStrDup(pszFilename);
-        if (!pImage->pszFilename)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        pImage->pDisk       = pDisk;
-        pImage->pVDIfsImage = pVDIfsImage;
-
-        rc = vdFindBackend(pszBackend, &pImage->Backend);
-        if (RT_FAILURE(rc))
-            break;
-        if (!pImage->Backend)
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: unknown backend name '%s'"), pszBackend);
-            break;
-        }
-
-        /* Set up the I/O interface. */
-        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
-        if (pImage->pInterfaceIO)
-            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
-        else
-        {
-            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
-                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
-            pImage->pInterfaceIO = &pImage->VDIIO;
-            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
-        }
-
-        /* Set up the internal I/O interface. */
-        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
-                        rc = VERR_INVALID_PARAMETER);
-        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
-        AssertRC(rc);
-
-        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
-        rc = pImage->Backend->pfnOpen(pImage->pszFilename,
-                                      uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
-                                      pDisk->pVDIfsDisk,
-                                      pImage->pVDIfsImage,
-                                      pDisk->enmType,
-                                      &pImage->pBackendData);
-        /* If the open in read-write mode failed, retry in read-only mode. */
-        if (RT_FAILURE(rc))
-        {
-            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                &&  (   rc == VERR_ACCESS_DENIED
-                     || rc == VERR_PERMISSION_DENIED
-                     || rc == VERR_WRITE_PROTECT
-                     || rc == VERR_SHARING_VIOLATION
-                     || rc == VERR_FILE_LOCK_FAILED))
-                rc = pImage->Backend->pfnOpen(pImage->pszFilename,
-                                                (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
-                                               | VD_OPEN_FLAGS_READONLY,
-                                               pDisk->pVDIfsDisk,
-                                               pImage->pVDIfsImage,
-                                               pDisk->enmType,
-                                               &pImage->pBackendData);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdError(pDisk, rc, RT_SRC_POS,
-                             N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
-                break;
-            }
-        }
-
-        /* Lock disk for writing, as we modify pDisk information below. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        /* Check image type. As the image itself has only partial knowledge
-         * whether it's a base image or not, this info is derived here. The
-         * base image can be fixed or normal, all others must be normal or
-         * diff images. Some image formats don't distinguish between normal
-         * and diff images, so this must be corrected here. */
-        unsigned uImageFlags;
-        uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
-        if (RT_FAILURE(rc))
-            uImageFlags = VD_IMAGE_FLAGS_NONE;
-        if (    RT_SUCCESS(rc)
-            &&  !(uOpenFlags & VD_OPEN_FLAGS_INFO))
-        {
-            if (    pDisk->cImages == 0
-                &&  (uImageFlags & VD_IMAGE_FLAGS_DIFF))
-            {
-                rc = VERR_VD_INVALID_TYPE;
-                break;
-            }
-            else if (pDisk->cImages != 0)
-            {
-                if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-                {
-                    rc = VERR_VD_INVALID_TYPE;
-                    break;
-                }
-                else
-                    uImageFlags |= VD_IMAGE_FLAGS_DIFF;
-            }
-        }
-
-        /* Ensure we always get correct diff information, even if the backend
-         * doesn't actually have a stored flag for this. It must not return
-         * bogus information for the parent UUID if it is not a diff image. */
-        RTUUID parentUuid;
-        RTUuidClear(&parentUuid);
-        rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
-        if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
-            uImageFlags |= VD_IMAGE_FLAGS_DIFF;
-
-        pImage->uImageFlags = uImageFlags;
-
-        /* Force sane optimization settings. It's not worth avoiding writes
-         * to fixed size images. The overhead would have almost no payback. */
-        if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-            pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
-
-        /** @todo optionally check UUIDs */
-
-        /* Cache disk information. */
-        pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
-
-        /* Cache PCHS geometry. */
-        rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                  &pDisk->PCHSGeometry);
-        if (RT_FAILURE(rc2))
-        {
-            pDisk->PCHSGeometry.cCylinders = 0;
-            pDisk->PCHSGeometry.cHeads = 0;
-            pDisk->PCHSGeometry.cSectors = 0;
-        }
-        else
-        {
-            /* Make sure the PCHS geometry is properly clipped. */
-            pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
-            pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
-            pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
-        }
-
-        /* Cache LCHS geometry. */
-        rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                  &pDisk->LCHSGeometry);
-        if (RT_FAILURE(rc2))
-        {
-            pDisk->LCHSGeometry.cCylinders = 0;
-            pDisk->LCHSGeometry.cHeads = 0;
-            pDisk->LCHSGeometry.cSectors = 0;
-        }
-        else
-        {
-            /* Make sure the LCHS geometry is properly clipped. */
-            pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
-            pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
-        }
-
-        if (pDisk->cImages != 0)
-        {
-            /* Switch previous image to read-only mode. */
-            unsigned uOpenFlagsPrevImg;
-            uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
-            if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
-            {
-                uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
-                rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
-            }
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Image successfully opened, make it the last image. */
-            vdAddImageToList(pDisk, pImage);
-            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
-        }
-        else
-        {
-            /* Error detected, but image opened. Close image. */
-            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
-            AssertRC(rc2);
-            pImage->pBackendData = NULL;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (pImage)
-        {
-            if (pImage->pszFilename)
-                RTStrFree(pImage->pszFilename);
-            RTMemFree(pImage);
-        }
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Opens a cache image.
- *
- * @return  VBox status code.
- * @param   pDisk           Pointer to the HDD container which should use the cache image.
- * @param   pszBackend      Name of the cache file backend to use (case insensitive).
- * @param   pszFilename     Name of the cache image to open.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
- */
-VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
-                              const char *pszFilename, unsigned uOpenFlags,
-                              PVDINTERFACE pVDIfsCache)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-    PVDCACHE pCache = NULL;
-
-    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
-                 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
-                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        /* Set up image descriptor. */
-        pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
-        if (!pCache)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pCache->pszFilename = RTStrDup(pszFilename);
-        if (!pCache->pszFilename)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        pCache->pDisk       = pDisk;
-        pCache->pVDIfsCache = pVDIfsCache;
-
-        rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
-        if (RT_FAILURE(rc))
-            break;
-        if (!pCache->Backend)
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: unknown backend name '%s'"), pszBackend);
-            break;
-        }
-
-        /* Set up the I/O interface. */
-        pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
-        if (pCache->pInterfaceIO)
-            pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
-        else
-        {
-            rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
-                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
-            pCache->pInterfaceIO = &pCache->VDIIO;
-            pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
-        }
-
-        /* Set up the internal I/O interface. */
-        AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
-                        rc = VERR_INVALID_PARAMETER);
-        rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                            &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
-        AssertRC(rc);
-
-        pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
-        rc = pCache->Backend->pfnOpen(pCache->pszFilename,
-                                      uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
-                                      pDisk->pVDIfsDisk,
-                                      pCache->pVDIfsCache,
-                                      &pCache->pBackendData);
-        /* If the open in read-write mode failed, retry in read-only mode. */
-        if (RT_FAILURE(rc))
-        {
-            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                &&  (   rc == VERR_ACCESS_DENIED
-                     || rc == VERR_PERMISSION_DENIED
-                     || rc == VERR_WRITE_PROTECT
-                     || rc == VERR_SHARING_VIOLATION
-                     || rc == VERR_FILE_LOCK_FAILED))
-                rc = pCache->Backend->pfnOpen(pCache->pszFilename,
-                                                (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
-                                               | VD_OPEN_FLAGS_READONLY,
-                                               pDisk->pVDIfsDisk,
-                                               pCache->pVDIfsCache,
-                                               &pCache->pBackendData);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdError(pDisk, rc, RT_SRC_POS,
-                             N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
-                break;
-            }
-        }
-
-        /* Lock disk for writing, as we modify pDisk information below. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        /*
-         * Check that the modification UUID of the cache and last image
-         * match. If not the image was modified in-between without the cache.
-         * The cache might contain stale data.
-         */
-        RTUUID UuidImage, UuidCache;
-
-        rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
-                                                     &UuidCache);
-        if (RT_SUCCESS(rc))
-        {
-            rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
-                                                               &UuidImage);
-            if (RT_SUCCESS(rc))
-            {
-                if (RTUuidCompare(&UuidImage, &UuidCache))
-                    rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
-            }
-        }
-
-        /*
-         * We assume that the user knows what he is doing if one of the images
-         * doesn't support the modification uuid.
-         */
-        if (rc == VERR_NOT_SUPPORTED)
-            rc = VINF_SUCCESS;
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Cache successfully opened, make it the current one. */
-            if (!pDisk->pCache)
-                pDisk->pCache = pCache;
-            else
-                rc = VERR_VD_CACHE_ALREADY_EXISTS;
-        }
-
-        if (RT_FAILURE(rc))
-        {
-            /* Error detected, but image opened. Close image. */
-            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
-            AssertRC(rc2);
-            pCache->pBackendData = NULL;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (pCache)
-        {
-            if (pCache->pszFilename)
-                RTStrFree(pCache->pszFilename);
-            RTMemFree(pCache);
-        }
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Creates and opens a new base image file.
- *
- * @returns VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use.
- * @param   pszFilename     Name of the image file to create.
- * @param   cbSize          Image size in bytes.
- * @param   uImageFlags     Flags specifying special image features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pPCHSGeometry   Pointer to physical disk geometry <= (16383,16,63). Not NULL.
- * @param   pLCHSGeometry   Pointer to logical disk geometry <= (x,255,63). Not NULL.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
-                               const char *pszFilename, uint64_t cbSize,
-                               unsigned uImageFlags, const char *pszComment,
-                               PCVDGEOMETRY pPCHSGeometry,
-                               PCVDGEOMETRY pLCHSGeometry,
-                               PCRTUUID pUuid, unsigned uOpenFlags,
-                               PVDINTERFACE pVDIfsImage,
-                               PVDINTERFACE pVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false, fLockRead = false;
-    PVDIMAGE pImage = NULL;
-    RTUUID uuid;
-
-    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
-                 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
-                 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
-                 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
-                 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
-                 uOpenFlags, pVDIfsImage, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
-                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbSize,
-                           ("cbSize=%llu\n", cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(   ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
-                           || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
-                           ("uImageFlags=%#x\n", uImageFlags),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The PCHS geometry fields may be 0 to leave it for later. */
-        AssertMsgBreakStmt(   VALID_PTR(pPCHSGeometry)
-                           && pPCHSGeometry->cHeads <= 16
-                           && pPCHSGeometry->cSectors <= 63,
-                           ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
-                            pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
-                            pPCHSGeometry->cSectors),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
-        AssertMsgBreakStmt(   VALID_PTR(pLCHSGeometry)
-                           && pLCHSGeometry->cHeads <= 255
-                           && pLCHSGeometry->cSectors <= 63,
-                           ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
-                            pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
-                            pLCHSGeometry->cSectors),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The UUID may be NULL. */
-        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
-                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        /* Check state. Needs a temporary read lock. Holding the write lock
-         * all the time would be blocking other activities for too long. */
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-        AssertMsgBreakStmt(pDisk->cImages == 0,
-                           ("Create base image cannot be done with other images open\n"),
-                           rc = VERR_VD_INVALID_STATE);
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = false;
-
-        /* Set up image descriptor. */
-        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
-        if (!pImage)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pImage->pszFilename = RTStrDup(pszFilename);
-        if (!pImage->pszFilename)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pImage->pDisk       = pDisk;
-        pImage->pVDIfsImage = pVDIfsImage;
-
-        /* Set up the I/O interface. */
-        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
-        if (pImage->pInterfaceIO)
-            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
-        else
-        {
-            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
-                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
-            pImage->pInterfaceIO = &pImage->VDIIO;
-            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
-        }
-
-        /* Set up the internal I/O interface. */
-        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
-                        rc = VERR_INVALID_PARAMETER);
-        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
-        AssertRC(rc);
-
-        rc = vdFindBackend(pszBackend, &pImage->Backend);
-        if (RT_FAILURE(rc))
-            break;
-        if (!pImage->Backend)
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: unknown backend name '%s'"), pszBackend);
-            break;
-        }
-        if (!(pImage->Backend->uBackendCaps & (  VD_CAP_CREATE_FIXED
-                                               | VD_CAP_CREATE_DYNAMIC)))
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: backend '%s' cannot create base images"), pszBackend);
-            break;
-        }
-
-        /* Create UUID if the caller didn't specify one. */
-        if (!pUuid)
-        {
-            rc = RTUuidCreate(&uuid);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdError(pDisk, rc, RT_SRC_POS,
-                             N_("VD: cannot generate UUID for image '%s'"),
-                             pszFilename);
-                break;
-            }
-            pUuid = &uuid;
-        }
-
-        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
-        uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
-        rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
-                                        uImageFlags, pszComment, pPCHSGeometry,
-                                        pLCHSGeometry, pUuid,
-                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
-                                        0, 99,
-                                        pDisk->pVDIfsDisk,
-                                        pImage->pVDIfsImage,
-                                        pVDIfsOperation,
-                                        &pImage->pBackendData);
-
-        if (RT_SUCCESS(rc))
-        {
-            pImage->uImageFlags = uImageFlags;
-
-            /* Force sane optimization settings. It's not worth avoiding writes
-             * to fixed size images. The overhead would have almost no payback. */
-            if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-                pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
-
-            /* Lock disk for writing, as we modify pDisk information below. */
-            rc2 = vdThreadStartWrite(pDisk);
-            AssertRC(rc2);
-            fLockWrite = true;
-
-            /** @todo optionally check UUIDs */
-
-            /* Re-check state, as the lock wasn't held and another image
-             * creation call could have been done by another thread. */
-            AssertMsgStmt(pDisk->cImages == 0,
-                          ("Create base image cannot be done with other images open\n"),
-                          rc = VERR_VD_INVALID_STATE);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Cache disk information. */
-            pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
-
-            /* Cache PCHS geometry. */
-            rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                      &pDisk->PCHSGeometry);
-            if (RT_FAILURE(rc2))
-            {
-                pDisk->PCHSGeometry.cCylinders = 0;
-                pDisk->PCHSGeometry.cHeads = 0;
-                pDisk->PCHSGeometry.cSectors = 0;
-            }
-            else
-            {
-                /* Make sure the CHS geometry is properly clipped. */
-                pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
-                pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
-                pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
-            }
-
-            /* Cache LCHS geometry. */
-            rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                      &pDisk->LCHSGeometry);
-            if (RT_FAILURE(rc2))
-            {
-                pDisk->LCHSGeometry.cCylinders = 0;
-                pDisk->LCHSGeometry.cHeads = 0;
-                pDisk->LCHSGeometry.cSectors = 0;
-            }
-            else
-            {
-                /* Make sure the CHS geometry is properly clipped. */
-                pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
-                pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
-            }
-
-            /* Image successfully opened, make it the last image. */
-            vdAddImageToList(pDisk, pImage);
-            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
-        }
-        else
-        {
-            /* Error detected, image may or may not be opened. Close and delete
-             * image if it was opened. */
-            if (pImage->pBackendData)
-            {
-                rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
-                AssertRC(rc2);
-                pImage->pBackendData = NULL;
-            }
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (pImage)
-        {
-            if (pImage->pszFilename)
-                RTStrFree(pImage->pszFilename);
-            RTMemFree(pImage);
-        }
-    }
-
-    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
-        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Creates and opens a new differencing image file in HDD container.
- * See comments for VDOpen function about differencing images.
- *
- * @returns VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   pszBackend      Name of the image file backend to use.
- * @param   pszFilename     Name of the differencing image file to create.
- * @param   uImageFlags     Flags specifying special image features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   pParentUuid     New parent UUID of the image. If NULL, the UUID is queried automatically.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsImage     Pointer to the per-image VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
-                               const char *pszFilename, unsigned uImageFlags,
-                               const char *pszComment, PCRTUUID pUuid,
-                               PCRTUUID pParentUuid, unsigned uOpenFlags,
-                               PVDINTERFACE pVDIfsImage,
-                               PVDINTERFACE pVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false, fLockRead = false;
-    PVDIMAGE pImage = NULL;
-    RTUUID uuid;
-
-    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
-                 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
-                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
-                           ("uImageFlags=%#x\n", uImageFlags),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The UUID may be NULL. */
-        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
-                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The parent UUID may be NULL. */
-        AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
-                           ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        /* Check state. Needs a temporary read lock. Holding the write lock
-         * all the time would be blocking other activities for too long. */
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-        AssertMsgBreakStmt(pDisk->cImages != 0,
-                           ("Create diff image cannot be done without other images open\n"),
-                           rc = VERR_VD_INVALID_STATE);
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = false;
-
-        /* Set up image descriptor. */
-        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
-        if (!pImage)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pImage->pszFilename = RTStrDup(pszFilename);
-        if (!pImage->pszFilename)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        rc = vdFindBackend(pszBackend, &pImage->Backend);
-        if (RT_FAILURE(rc))
-            break;
-        if (!pImage->Backend)
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: unknown backend name '%s'"), pszBackend);
-            break;
-        }
-        if (   !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
-            || !(pImage->Backend->uBackendCaps & (  VD_CAP_CREATE_FIXED
-                                                  | VD_CAP_CREATE_DYNAMIC)))
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: backend '%s' cannot create diff images"), pszBackend);
-            break;
-        }
-
-        pImage->pDisk       = pDisk;
-        pImage->pVDIfsImage = pVDIfsImage;
-
-        /* Set up the I/O interface. */
-        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
-        if (pImage->pInterfaceIO)
-            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
-        else
-        {
-            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
-                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
-            pImage->pInterfaceIO = &pImage->VDIIO;
-            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
-        }
-
-        /* Set up the internal I/O interface. */
-        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
-                        rc = VERR_INVALID_PARAMETER);
-        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
-        AssertRC(rc);
-
-        /* Create UUID if the caller didn't specify one. */
-        if (!pUuid)
-        {
-            rc = RTUuidCreate(&uuid);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdError(pDisk, rc, RT_SRC_POS,
-                             N_("VD: cannot generate UUID for image '%s'"),
-                             pszFilename);
-                break;
-            }
-            pUuid = &uuid;
-        }
-
-        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
-        uImageFlags |= VD_IMAGE_FLAGS_DIFF;
-        rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
-                                        uImageFlags | VD_IMAGE_FLAGS_DIFF,
-                                        pszComment, &pDisk->PCHSGeometry,
-                                        &pDisk->LCHSGeometry, pUuid,
-                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
-                                        0, 99,
-                                        pDisk->pVDIfsDisk,
-                                        pImage->pVDIfsImage,
-                                        pVDIfsOperation,
-                                        &pImage->pBackendData);
-
-        if (RT_SUCCESS(rc))
-        {
-            pImage->uImageFlags = uImageFlags;
-
-            /* Lock disk for writing, as we modify pDisk information below. */
-            rc2 = vdThreadStartWrite(pDisk);
-            AssertRC(rc2);
-            fLockWrite = true;
-
-            /* Switch previous image to read-only mode. */
-            unsigned uOpenFlagsPrevImg;
-            uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
-            if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
-            {
-                uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
-                rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
-            }
-
-            /** @todo optionally check UUIDs */
-
-            /* Re-check state, as the lock wasn't held and another image
-             * creation call could have been done by another thread. */
-            AssertMsgStmt(pDisk->cImages != 0,
-                          ("Create diff image cannot be done without other images open\n"),
-                          rc = VERR_VD_INVALID_STATE);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            RTUUID Uuid;
-            RTTIMESPEC ts;
-
-            if (pParentUuid && !RTUuidIsNull(pParentUuid))
-            {
-                Uuid = *pParentUuid;
-                pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
-            }
-            else
-            {
-                rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
-                                                        &Uuid);
-                if (RT_SUCCESS(rc2))
-                    pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
-            }
-            rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
-                                                                &Uuid);
-            if (RT_SUCCESS(rc2))
-                pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
-                                                              &Uuid);
-            if (pDisk->pLast->Backend->pfnGetTimeStamp)
-                rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
-                                                             &ts);
-            else
-                rc2 = VERR_NOT_IMPLEMENTED;
-            if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
-                pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
-
-            if (pImage->Backend->pfnSetParentFilename)
-                rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Image successfully opened, make it the last image. */
-            vdAddImageToList(pDisk, pImage);
-            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
-        }
-        else
-        {
-            /* Error detected, but image opened. Close and delete image. */
-            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
-            AssertRC(rc2);
-            pImage->pBackendData = NULL;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (pImage)
-        {
-            if (pImage->pszFilename)
-                RTStrFree(pImage->pszFilename);
-            RTMemFree(pImage);
-        }
-    }
-
-    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
-        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * Creates and opens new cache image file in HDD container.
- *
- * @return  VBox status code.
- * @param   pDisk           Name of the cache file backend to use (case insensitive).
- * @param   pszFilename     Name of the differencing cache file to create.
- * @param   cbSize          Maximum size of the cache.
- * @param   uImageFlags     Flags specifying special cache features.
- * @param   pszComment      Pointer to image comment. NULL is ok.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
-                                const char *pszFilename, uint64_t cbSize,
-                                unsigned uImageFlags, const char *pszComment,
-                                PCRTUUID pUuid, unsigned uOpenFlags,
-                                PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false, fLockRead = false;
-    PVDCACHE pCache = NULL;
-    RTUUID uuid;
-
-    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSIze=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
-                 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
-                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbSize,
-                           ("cbSize=%llu\n", cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
-                           ("uImageFlags=%#x\n", uImageFlags),
-                           rc = VERR_INVALID_PARAMETER);
-        /* The UUID may be NULL. */
-        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
-                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        /* Check state. Needs a temporary read lock. Holding the write lock
-         * all the time would be blocking other activities for too long. */
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-        AssertMsgBreakStmt(pDisk->cImages != 0,
-                           ("Create diff image cannot be done without other images open\n"),
-                           rc = VERR_VD_INVALID_STATE);
-        AssertMsgBreakStmt(!pDisk->pCache,
-                           ("Create cache image cannot be done with a cache already attached\n"),
-                           rc = VERR_VD_CACHE_ALREADY_EXISTS);
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = false;
-
-        /* Set up image descriptor. */
-        pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
-        if (!pCache)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-        pCache->pszFilename = RTStrDup(pszFilename);
-        if (!pCache->pszFilename)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
-        if (RT_FAILURE(rc))
-            break;
-        if (!pCache->Backend)
-        {
-            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
-                         N_("VD: unknown backend name '%s'"), pszBackend);
-            break;
-        }
-
-        pCache->pDisk       = pDisk;
-        pCache->pVDIfsCache = pVDIfsCache;
-
-        /* Set up the I/O interface. */
-        pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
-        if (pCache->pInterfaceIO)
-            pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
-        else
-        {
-            rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
-                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
-            pCache->pInterfaceIO = &pCache->VDIIO;
-            pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
-        }
-
-        /* Set up the internal I/O interface. */
-        AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
-                        rc = VERR_INVALID_PARAMETER);
-        rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
-                            &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
-        AssertRC(rc);
-
-        /* Create UUID if the caller didn't specify one. */
-        if (!pUuid)
-        {
-            rc = RTUuidCreate(&uuid);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdError(pDisk, rc, RT_SRC_POS,
-                             N_("VD: cannot generate UUID for image '%s'"),
-                             pszFilename);
-                break;
-            }
-            pUuid = &uuid;
-        }
-
-        pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
-        rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
-                                        uImageFlags,
-                                        pszComment, pUuid,
-                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
-                                        0, 99,
-                                        pDisk->pVDIfsDisk,
-                                        pCache->pVDIfsCache,
-                                        pVDIfsOperation,
-                                        &pCache->pBackendData);
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Lock disk for writing, as we modify pDisk information below. */
-            rc2 = vdThreadStartWrite(pDisk);
-            AssertRC(rc2);
-            fLockWrite = true;
-
-            /* Re-check state, as the lock wasn't held and another image
-             * creation call could have been done by another thread. */
-            AssertMsgStmt(!pDisk->pCache,
-                          ("Create cache image cannot be done with another cache open\n"),
-                          rc = VERR_VD_CACHE_ALREADY_EXISTS);
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            RTUUID UuidModification;
-
-            /* Set same modification Uuid as the last image. */
-            rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
-                                                               &UuidModification);
-            if (RT_SUCCESS(rc))
-            {
-                rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
-                                                             &UuidModification);
-            }
-
-            if (rc == VERR_NOT_SUPPORTED)
-                rc = VINF_SUCCESS;
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            /* Cache successfully created. */
-            pDisk->pCache = pCache;
-        }
-        else
-        {
-            /* Error detected, but image opened. Close and delete image. */
-            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
-            AssertRC(rc2);
-            pCache->pBackendData = NULL;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_FAILURE(rc))
-    {
-        if (pCache)
-        {
-            if (pCache->pszFilename)
-                RTStrFree(pCache->pszFilename);
-            RTMemFree(pCache);
-        }
-    }
-
-    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
-        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Merges two images (not necessarily with direct parent/child relationship).
- * As a side effect the source image and potentially the other images which
- * are also merged to the destination are deleted from both the disk and the
- * images in the HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImageFrom      Name of the image file to merge from.
- * @param   nImageTo        Name of the image file to merge to.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
-                          unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false, fLockRead = false;
-    void *pvBuf = NULL;
-
-    LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
-                 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* For simplicity reasons lock for writing as the image reopen below
-         * might need it. After all the reopen is usually needed. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-        PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
-        PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
-        if (!pImageFrom || !pImageTo)
-        {
-            rc = VERR_VD_IMAGE_NOT_FOUND;
-            break;
-        }
-        AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
-
-        /* Make sure destination image is writable. */
-        unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
-            rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
-                                                    uOpenFlags);
-            if (RT_FAILURE(rc))
-                break;
-        }
-
-        /* Get size of destination image. */
-        uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = false;
-
-        /* Allocate tmp buffer. */
-        pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
-        if (!pvBuf)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        /* Merging is done directly on the images itself. This potentially
-         * causes trouble if the disk is full in the middle of operation. */
-        if (nImageFrom < nImageTo)
-        {
-            /* Merge parent state into child. This means writing all not
-             * allocated blocks in the destination image which are allocated in
-             * the images to be merged. */
-            uint64_t uOffset = 0;
-            uint64_t cbRemaining = cbSize;
-            do
-            {
-                size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
-
-                /* Need to hold the write lock during a read-write operation. */
-                rc2 = vdThreadStartWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = true;
-
-                rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
-                                                uOffset, pvBuf, cbThisRead,
-                                                &cbThisRead);
-                if (rc == VERR_VD_BLOCK_FREE)
-                {
-                    /* Search for image with allocated block. Do not attempt to
-                     * read more than the previous reads marked as valid.
-                     * Otherwise this would return stale data when different
-                     * block sizes are used for the images. */
-                    for (PVDIMAGE pCurrImage = pImageTo->pPrev;
-                         pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
-                         pCurrImage = pCurrImage->pPrev)
-                    {
-                        rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
-                                                          uOffset, pvBuf,
-                                                          cbThisRead,
-                                                          &cbThisRead);
-                    }
-
-                    if (rc != VERR_VD_BLOCK_FREE)
-                    {
-                        if (RT_FAILURE(rc))
-                            break;
-                        /* Updating the cache is required because this might be a live merge. */
-                        rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
-                                           uOffset, pvBuf, cbThisRead,
-                                           true /* fUpdateCache */);
-                        if (RT_FAILURE(rc))
-                            break;
-                    }
-                    else
-                        rc = VINF_SUCCESS;
-                }
-                else if (RT_FAILURE(rc))
-                    break;
-
-                rc2 = vdThreadFinishWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = false;
-
-                uOffset += cbThisRead;
-                cbRemaining -= cbThisRead;
-
-                if (pCbProgress && pCbProgress->pfnProgress)
-                {
-                    /** @todo r=klaus: this can update the progress to the same
-                     * percentage over and over again if the image format makes
-                     * relatively small increments. */
-                    rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                                  uOffset * 99 / cbSize);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-            } while (uOffset < cbSize);
-        }
-        else
-        {
-            /*
-             * We may need to update the parent uuid of the child coming after
-             * the last image to be merged. We have to reopen it read/write.
-             *
-             * This is done before we do the actual merge to prevent an
-             * inconsistent chain if the mode change fails for some reason.
-             */
-            if (pImageFrom->pNext)
-            {
-                PVDIMAGE pImageChild = pImageFrom->pNext;
-
-                /* Take the write lock. */
-                rc2 = vdThreadStartWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = true;
-
-                /* We need to open the image in read/write mode. */
-                uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
-
-                if (uOpenFlags  & VD_OPEN_FLAGS_READONLY)
-                {
-                    uOpenFlags  &= ~VD_OPEN_FLAGS_READONLY;
-                    rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
-                                                               uOpenFlags);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-
-                rc2 = vdThreadFinishWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = false;
-            }
-
-            /* If the merge is from the last image we have to relay all writes
-             * to the merge destination as well, so that concurrent writes
-             * (in case of a live merge) are handled correctly. */
-            if (!pImageFrom->pNext)
-            {
-                /* Take the write lock. */
-                rc2 = vdThreadStartWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = true;
-
-                pDisk->pImageRelay = pImageTo;
-
-                rc2 = vdThreadFinishWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = false;
-            }
-
-            /* Merge child state into parent. This means writing all blocks
-             * which are allocated in the image up to the source image to the
-             * destination image. */
-            uint64_t uOffset = 0;
-            uint64_t cbRemaining = cbSize;
-            do
-            {
-                size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
-                rc = VERR_VD_BLOCK_FREE;
-
-                /* Need to hold the write lock during a read-write operation. */
-                rc2 = vdThreadStartWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = true;
-
-                /* Search for image with allocated block. Do not attempt to
-                 * read more than the previous reads marked as valid. Otherwise
-                 * this would return stale data when different block sizes are
-                 * used for the images. */
-                for (PVDIMAGE pCurrImage = pImageFrom;
-                     pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
-                     pCurrImage = pCurrImage->pPrev)
-                {
-                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
-                                                      uOffset, pvBuf,
-                                                      cbThisRead, &cbThisRead);
-                }
-
-                if (rc != VERR_VD_BLOCK_FREE)
-                {
-                    if (RT_FAILURE(rc))
-                        break;
-                    rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
-                                       cbThisRead, true /* fUpdateCache */);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-                else
-                    rc = VINF_SUCCESS;
-
-                rc2 = vdThreadFinishWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = false;
-
-                uOffset += cbThisRead;
-                cbRemaining -= cbThisRead;
-
-                if (pCbProgress && pCbProgress->pfnProgress)
-                {
-                    /** @todo r=klaus: this can update the progress to the same
-                     * percentage over and over again if the image format makes
-                     * relatively small increments. */
-                    rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                                  uOffset * 99 / cbSize);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-            } while (uOffset < cbSize);
-
-            /* In case we set up a "write proxy" image above we must clear
-             * this again now to prevent stray writes. Failure or not. */
-            if (!pImageFrom->pNext)
-            {
-                /* Take the write lock. */
-                rc2 = vdThreadStartWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = true;
-
-                pDisk->pImageRelay = NULL;
-
-                rc2 = vdThreadFinishWrite(pDisk);
-                AssertRC(rc2);
-                fLockWrite = false;
-            }
-        }
-
-        /*
-         * Leave in case of an error to avoid corrupted data in the image chain
-         * (includes cancelling the operation by the user).
-         */
-        if (RT_FAILURE(rc))
-            break;
-
-        /* Need to hold the write lock while finishing the merge. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        /* Update parent UUID so that image chain is consistent. */
-        RTUUID Uuid;
-        PVDIMAGE pImageChild = NULL;
-        if (nImageFrom < nImageTo)
-        {
-            if (pImageFrom->pPrev)
-            {
-                rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
-                                                            &Uuid);
-                AssertRC(rc);
-            }
-            else
-                RTUuidClear(&Uuid);
-            rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
-                                                     &Uuid);
-            AssertRC(rc);
-        }
-        else
-        {
-            /* Update the parent uuid of the child of the last merged image. */
-            if (pImageFrom->pNext)
-            {
-                rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
-                                                   &Uuid);
-                AssertRC(rc);
-
-                rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
-                                                           &Uuid);
-                AssertRC(rc);
-
-                pImageChild = pImageFrom->pNext;
-            }
-        }
-
-        /* Delete the no longer needed images. */
-        PVDIMAGE pImg = pImageFrom, pTmp;
-        while (pImg != pImageTo)
-        {
-            if (nImageFrom < nImageTo)
-                pTmp = pImg->pNext;
-            else
-                pTmp = pImg->pPrev;
-            vdRemoveImageFromList(pDisk, pImg);
-            pImg->Backend->pfnClose(pImg->pBackendData, true);
-            RTMemFree(pImg->pszFilename);
-            RTMemFree(pImg);
-            pImg = pTmp;
-        }
-
-        /* Make sure destination image is back to read only if necessary. */
-        if (pImageTo != pDisk->pLast)
-        {
-            uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
-            uOpenFlags |= VD_OPEN_FLAGS_READONLY;
-            rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
-                                                    uOpenFlags);
-            if (RT_FAILURE(rc))
-                break;
-        }
-
-        /*
-         * Make sure the child is readonly
-         * for the child -> parent merge direction
-         * if necessary.
-        */
-        if (   nImageFrom > nImageTo
-            && pImageChild
-            && pImageChild != pDisk->pLast)
-        {
-            uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
-            uOpenFlags |= VD_OPEN_FLAGS_READONLY;
-            rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
-                                                       uOpenFlags);
-            if (RT_FAILURE(rc))
-                break;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-
-    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
-        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Copies an image from one HDD container to another.
- * The copy is opened in the target HDD container.
- * It is possible to convert between different image formats, because the
- * backend for the destination may be different from the source.
- * If both the source and destination reference the same HDD container,
- * then the image is moved (by copying/deleting or renaming) to the new location.
- * The source container is unchanged if the move operation fails, otherwise
- * the image at the new location is opened in the same way as the old one was.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDiskFrom       Pointer to source HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pDiskTo         Pointer to destination HDD container.
- * @param   pszBackend      Name of the image file backend to use.
- * @param   pszFilename     New name of the image (may be NULL if pDiskFrom == pDiskTo).
- * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
- * @param   cbSize          New image size (0 means leave unchanged).
- * @param   uImageFlags     Flags specifying special destination image features.
- * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
- *                          This parameter is used if and only if a true copy is created.
- *                          In all rename/move cases the UUIDs are copied over.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- *                          Only used if the destination image is created.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
- *                          destination image.
- * @param   pDstVDIfsOperation Pointer to the per-image VD interface list,
- *                          for the destination image.
- */
-VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
-                         const char *pszBackend, const char *pszFilename,
-                         bool fMoveByRename, uint64_t cbSize,
-                         unsigned uImageFlags, PCRTUUID pDstUuid,
-                         unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
-                         PVDINTERFACE pDstVDIfsImage,
-                         PVDINTERFACE pDstVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
-    void *pvBuf = NULL;
-    PVDIMAGE pImageTo = NULL;
-
-    LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
-                 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
-                                                 VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pDstCbProgress = NULL;
-    if (pDstIfProgress)
-        pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
-
-    do {
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
-                  ("u32Signature=%08x\n", pDiskFrom->u32Signature));
-
-        rc2 = vdThreadStartRead(pDiskFrom);
-        AssertRC(rc2);
-        fLockReadFrom = true;
-        PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
-        AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
-        AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
-                  ("u32Signature=%08x\n", pDiskTo->u32Signature));
-
-        /* Move the image. */
-        if (pDiskFrom == pDiskTo)
-        {
-            /* Rename only works when backends are the same, are file based
-             * and the rename method is implemented. */
-            if (    fMoveByRename
-                &&  !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
-                &&  pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
-                &&  pImageFrom->Backend->pfnRename)
-            {
-                rc2 = vdThreadFinishRead(pDiskFrom);
-                AssertRC(rc2);
-                fLockReadFrom = false;
-
-                rc2 = vdThreadStartWrite(pDiskFrom);
-                AssertRC(rc2);
-                fLockWriteFrom = true;
-                rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
-                break;
-            }
-
-            /** @todo Moving (including shrinking/growing) of the image is
-             * requested, but the rename attempt failed or it wasn't possible.
-             * Must now copy image to temp location. */
-            AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
-        }
-
-        /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
-        AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-
-        uint64_t cbSizeFrom;
-        cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
-        if (cbSizeFrom == 0)
-        {
-            rc = VERR_VD_VALUE_NOT_FOUND;
-            break;
-        }
-
-        VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
-        VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
-        pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
-        pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
-
-        RTUUID ImageUuid, ImageModificationUuid;
-        if (pDiskFrom != pDiskTo)
-        {
-            if (pDstUuid)
-                ImageUuid = *pDstUuid;
-            else
-                RTUuidCreate(&ImageUuid);
-        }
-        else
-        {
-            rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
-            if (RT_FAILURE(rc))
-                RTUuidCreate(&ImageUuid);
-        }
-        rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
-        if (RT_FAILURE(rc))
-            RTUuidClear(&ImageModificationUuid);
-
-        char szComment[1024];
-        rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
-        if (RT_FAILURE(rc))
-            szComment[0] = '\0';
-        else
-            szComment[sizeof(szComment) - 1] = '\0';
-
-        rc2 = vdThreadFinishRead(pDiskFrom);
-        AssertRC(rc2);
-        fLockReadFrom = false;
-
-        rc2 = vdThreadStartRead(pDiskTo);
-        AssertRC(rc2);
-        unsigned cImagesTo = pDiskTo->cImages;
-        rc2 = vdThreadFinishRead(pDiskTo);
-        AssertRC(rc2);
-
-        if (pszFilename)
-        {
-            if (cbSize == 0)
-                cbSize = cbSizeFrom;
-
-            /* Create destination image with the properties of source image. */
-            /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
-             * calls to the backend. Unifies the code and reduces the API
-             * dependencies. Would also make the synchronization explicit. */
-            if (cImagesTo > 0)
-            {
-                rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
-                                  uImageFlags, szComment, &ImageUuid,
-                                  NULL /* pParentUuid */,
-                                  uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                  pDstVDIfsImage, NULL);
-
-                rc2 = vdThreadStartWrite(pDiskTo);
-                AssertRC(rc2);
-                fLockWriteTo = true;
-            } else {
-                /** @todo hack to force creation of a fixed image for
-                 * the RAW backend, which can't handle anything else. */
-                if (!RTStrICmp(pszBackend, "RAW"))
-                    uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-
-                /* Fix broken PCHS geometry. Can happen for two reasons: either
-                 * the backend mixes up PCHS and LCHS, or the application used
-                 * to create the source image has put garbage in it. */
-                /** @todo double-check if the VHD backend correctly handles
-                 * PCHS and LCHS geometry. also reconsider our current paranoia
-                 * level when it comes to geometry settings here and in the
-                 * backends. */
-                if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
-                {
-                    Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
-                    PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
-                    PCHSGeometryFrom.cHeads = 16;
-                    PCHSGeometryFrom.cSectors = 63;
-                }
-
-                rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
-                                  uImageFlags, szComment,
-                                  &PCHSGeometryFrom, &LCHSGeometryFrom,
-                                  NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                  pDstVDIfsImage, NULL);
-
-                rc2 = vdThreadStartWrite(pDiskTo);
-                AssertRC(rc2);
-                fLockWriteTo = true;
-
-                if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
-                     pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
-            }
-            if (RT_FAILURE(rc))
-                break;
-
-            pImageTo = pDiskTo->pLast;
-            AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-            cbSize = RT_MIN(cbSize, cbSizeFrom);
-        }
-        else
-        {
-            pImageTo = pDiskTo->pLast;
-            AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-            uint64_t cbSizeTo;
-            cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
-            if (cbSizeTo == 0)
-            {
-                rc = VERR_VD_VALUE_NOT_FOUND;
-                break;
-            }
-
-            if (cbSize == 0)
-                cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
-        }
-
-        rc2 = vdThreadFinishWrite(pDiskTo);
-        AssertRC(rc2);
-        fLockWriteTo = false;
-
-        /* Allocate tmp buffer. */
-        pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
-        if (!pvBuf)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        /* Whether we can take the optimized copy path (false) or not.
-         * Don't optimize if the image existed or if it is a child image. */
-        bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
-
-        /* Copy the data. */
-        uint64_t uOffset = 0;
-        uint64_t cbRemaining = cbSize;
-
-        do
-        {
-            size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
-
-            /* Note that we don't attempt to synchronize cross-disk accesses.
-             * It wouldn't be very difficult to do, just the lock order would
-             * need to be defined somehow to prevent deadlocks. Postpone such
-             * magic as there is no use case for this. */
-
-            rc2 = vdThreadStartRead(pDiskFrom);
-            AssertRC(rc2);
-            fLockReadFrom = true;
-
-            /*
-             * Updating the cache doesn't make any sense
-             * as we are looping once through the image.
-             */
-            rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
-                              cbThisRead, fRegularRead,
-                              false /* fUpdateCache */);
-            if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
-                break;
-
-            rc2 = vdThreadFinishRead(pDiskFrom);
-            AssertRC(rc2);
-            fLockReadFrom = false;
-
-            if (rc != VERR_VD_BLOCK_FREE)
-            {
-                rc2 = vdThreadStartWrite(pDiskTo);
-                AssertRC(rc2);
-                fLockWriteTo = true;
-
-                rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
-                                   cbThisRead, false /* fUpdateCache */);
-                if (RT_FAILURE(rc))
-                    break;
-
-                rc2 = vdThreadFinishWrite(pDiskTo);
-                AssertRC(rc2);
-                fLockWriteTo = false;
-            }
-            else /* Don't propagate the error to the outside */
-                rc = VINF_SUCCESS;
-
-            uOffset += cbThisRead;
-            cbRemaining -= cbThisRead;
-
-            if (pCbProgress && pCbProgress->pfnProgress)
-            {
-                /** @todo r=klaus: this can update the progress to the same
-                 * percentage over and over again if the image format makes
-                 * relatively small increments. */
-                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                              uOffset * 99 / cbSize);
-                if (RT_FAILURE(rc))
-                    break;
-            }
-            if (pDstCbProgress && pDstCbProgress->pfnProgress)
-            {
-                /** @todo r=klaus: this can update the progress to the same
-                 * percentage over and over again if the image format makes
-                 * relatively small increments. */
-                rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
-                                                 uOffset * 99 / cbSize);
-                if (RT_FAILURE(rc))
-                    break;
-            }
-        } while (uOffset < cbSize);
-
-        if (RT_SUCCESS(rc))
-        {
-            rc2 = vdThreadStartWrite(pDiskTo);
-            AssertRC(rc2);
-            fLockWriteTo = true;
-
-            /* Only set modification UUID if it is non-null, since the source
-             * backend might not provide a valid modification UUID. */
-            if (!RTUuidIsNull(&ImageModificationUuid))
-                pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
-
-            /* Set the requested open flags if they differ from the value
-             * required for creating the image and copying the contents. */
-            if (   pImageTo && pszFilename
-                && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
-                rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
-                                                        uOpenFlags);
-        }
-    } while (0);
-
-    if (RT_FAILURE(rc) && pImageTo && pszFilename)
-    {
-        /* Take the write lock only if it is not taken. Not worth making the
-         * above code even more complicated. */
-        if (RT_UNLIKELY(!fLockWriteTo))
-        {
-            rc2 = vdThreadStartWrite(pDiskTo);
-            AssertRC(rc2);
-            fLockWriteTo = true;
-        }
-        /* Error detected, but new image created. Remove image from list. */
-        vdRemoveImageFromList(pDiskTo, pImageTo);
-
-        /* Close and delete image. */
-        rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
-        AssertRC(rc2);
-        pImageTo->pBackendData = NULL;
-
-        /* Free remaining resources. */
-        if (pImageTo->pszFilename)
-            RTStrFree(pImageTo->pszFilename);
-
-        RTMemFree(pImageTo);
-    }
-
-    if (RT_UNLIKELY(fLockWriteTo))
-    {
-        rc2 = vdThreadFinishWrite(pDiskTo);
-        AssertRC(rc2);
-    }
-    if (RT_UNLIKELY(fLockWriteFrom))
-    {
-        rc2 = vdThreadFinishWrite(pDiskFrom);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockReadFrom))
-    {
-        rc2 = vdThreadFinishRead(pDiskFrom);
-        AssertRC(rc2);
-    }
-
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-
-    if (RT_SUCCESS(rc))
-    {
-        if (pCbProgress && pCbProgress->pfnProgress)
-            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-        if (pDstCbProgress && pDstCbProgress->pfnProgress)
-            pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Optimizes the storage consumption of an image. Typically the unused blocks
- * have to be wiped with zeroes to achieve a substantial reduced storage use.
- * Another optimization done is reordering the image blocks, which can provide
- * a significant performance boost, as reads and writes tend to use less random
- * file offsets.
- *
- * @return  VBox status code.
- * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
- * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
- *                             the code for this isn't implemented yet.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
-                            PVDINTERFACE pVDIfsOperation)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false, fLockWrite = false;
-    void *pvBuf = NULL;
-    void *pvTmp = NULL;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
-                 pDisk, nImage, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do {
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
-                  ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        /* If there is no compact callback for not file based backends then
-         * the backend doesn't need compaction. No need to make much fuss about
-         * this. For file based ones signal this as not yet supported. */
-        if (!pImage->Backend->pfnCompact)
-        {
-            if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
-                rc = VERR_NOT_SUPPORTED;
-            else
-                rc = VINF_SUCCESS;
-            break;
-        }
-
-        /* Insert interface for reading parent state into per-operation list,
-         * if there is a parent image. */
-        VDINTERFACE IfOpParent;
-        VDINTERFACEPARENTSTATE ParentCb;
-        VDPARENTSTATEDESC ParentUser;
-        if (pImage->pPrev)
-        {
-            ParentCb.cbSize = sizeof(ParentCb);
-            ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
-            ParentCb.pfnParentRead = vdParentRead;
-            ParentUser.pDisk = pDisk;
-            ParentUser.pImage = pImage->pPrev;
-            rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
-                                &ParentCb, &ParentUser, &pVDIfsOperation);
-            AssertRC(rc);
-        }
-
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = false;
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        rc = pImage->Backend->pfnCompact(pImage->pBackendData,
-                                         0, 99,
-                                         pDisk->pVDIfsDisk,
-                                         pImage->pVDIfsImage,
-                                         pVDIfsOperation);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-    if (pvTmp)
-        RTMemTmpFree(pvTmp);
-
-    if (RT_SUCCESS(rc))
-    {
-        if (pCbProgress && pCbProgress->pfnProgress)
-            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Resizes the the given disk image to the given size.
- *
- * @return  VBox status
- * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
- * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
- *
- * @param   pDisk           Pointer to the HDD container.
- * @param   cbSize          New size of the image.
- * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
- * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
- * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
- */
-VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
-                           PCVDGEOMETRY pPCHSGeometry,
-                           PCVDGEOMETRY pLCHSGeometry,
-                           PVDINTERFACE pVDIfsOperation)
-{
-    /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false, fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
-                 pDisk, cbSize, pVDIfsOperation));
-
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-
-    do {
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
-                  ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        /* Not supported if the disk has child images attached. */
-        AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
-                           rc = VERR_NOT_SUPPORTED);
-
-        PVDIMAGE pImage = pDisk->pBase;
-
-        /* If there is no compact callback for not file based backends then
-         * the backend doesn't need compaction. No need to make much fuss about
-         * this. For file based ones signal this as not yet supported. */
-        if (!pImage->Backend->pfnResize)
-        {
-            if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
-                rc = VERR_NOT_SUPPORTED;
-            else
-                rc = VINF_SUCCESS;
-            break;
-        }
-
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = false;
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        VDGEOMETRY PCHSGeometryOld;
-        VDGEOMETRY LCHSGeometryOld;
-        PCVDGEOMETRY pPCHSGeometryNew;
-        PCVDGEOMETRY pLCHSGeometryNew;
-
-        if (pPCHSGeometry->cCylinders == 0)
-        {
-            /* Auto-detect marker, calculate new value ourself. */
-            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
-            if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
-                PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
-            else if (rc == VERR_VD_GEOMETRY_NOT_SET)
-                rc = VINF_SUCCESS;
-
-            pPCHSGeometryNew = &PCHSGeometryOld;
-        }
-        else
-            pPCHSGeometryNew = pPCHSGeometry;
-
-        if (pLCHSGeometry->cCylinders == 0)
-        {
-            /* Auto-detect marker, calculate new value ourself. */
-            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
-            if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
-                LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
-            else if (rc == VERR_VD_GEOMETRY_NOT_SET)
-                rc = VINF_SUCCESS;
-
-            pLCHSGeometryNew = &LCHSGeometryOld;
-        }
-        else
-            pLCHSGeometryNew = pLCHSGeometry;
-
-        if (RT_SUCCESS(rc))
-            rc = pImage->Backend->pfnResize(pImage->pBackendData,
-                                            cbSize,
-                                            pPCHSGeometryNew,
-                                            pLCHSGeometryNew,
-                                            0, 99,
-                                            pDisk->pVDIfsDisk,
-                                            pImage->pVDIfsImage,
-                                            pVDIfsOperation);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-    else if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        if (pCbProgress && pCbProgress->pfnProgress)
-            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Closes the last opened image file in HDD container.
- * If previous image file was opened in read-only mode (the normal case) and
- * the last opened image is in read-write mode then the previous image will be
- * reopened in read/write mode.
- *
- * @returns VBox status code.
- * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   fDelete         If true, delete the image from the host disk.
- */
-VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Not worth splitting this up into a read lock phase and write
-         * lock phase, as closing an image is a relatively fast operation
-         * dominated by the part which needs the write lock. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = pDisk->pLast;
-        if (!pImage)
-        {
-            rc = VERR_VD_NOT_OPENED;
-            break;
-        }
-        unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
-        /* Remove image from list of opened images. */
-        vdRemoveImageFromList(pDisk, pImage);
-        /* Close (and optionally delete) image. */
-        rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
-        /* Free remaining resources related to the image. */
-        RTStrFree(pImage->pszFilename);
-        RTMemFree(pImage);
-
-        pImage = pDisk->pLast;
-        if (!pImage)
-            break;
-
-        /* If disk was previously in read/write mode, make sure it will stay
-         * like this (if possible) after closing this image. Set the open flags
-         * accordingly. */
-        if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
-            uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
-            rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
-        }
-
-        /* Cache disk information. */
-        pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
-
-        /* Cache PCHS geometry. */
-        rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                 &pDisk->PCHSGeometry);
-        if (RT_FAILURE(rc2))
-        {
-            pDisk->PCHSGeometry.cCylinders = 0;
-            pDisk->PCHSGeometry.cHeads = 0;
-            pDisk->PCHSGeometry.cSectors = 0;
-        }
-        else
-        {
-            /* Make sure the PCHS geometry is properly clipped. */
-            pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
-            pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
-            pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
-        }
-
-        /* Cache LCHS geometry. */
-        rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                  &pDisk->LCHSGeometry);
-        if (RT_FAILURE(rc2))
-        {
-            pDisk->LCHSGeometry.cCylinders = 0;
-            pDisk->LCHSGeometry.cHeads = 0;
-            pDisk->LCHSGeometry.cSectors = 0;
-        }
-        else
-        {
-            /* Make sure the LCHS geometry is properly clipped. */
-            pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
-            pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Closes the currently opened cache image file in HDD container.
- *
- * @return  VBox status code.
- * @return  VERR_VD_NOT_OPENED if no cache is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   fDelete         If true, delete the image from the host disk.
- */
-VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-    PVDCACHE pCache = NULL;
-
-    LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
-
-        pCache = pDisk->pCache;
-        pDisk->pCache = NULL;
-
-        pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
-        if (pCache->pszFilename)
-            RTStrFree(pCache->pszFilename);
-        RTMemFree(pCache);
-    } while (0);
-
-    if (RT_LIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Closes all opened image files in HDD container.
- *
- * @returns VBox status code.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Lock the entire operation. */
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDCACHE pCache = pDisk->pCache;
-        if (pCache)
-        {
-            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
-            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
-                rc = rc2;
-
-            if (pCache->pszFilename)
-                RTStrFree(pCache->pszFilename);
-            RTMemFree(pCache);
-        }
-
-        PVDIMAGE pImage = pDisk->pLast;
-        while (VALID_PTR(pImage))
-        {
-            PVDIMAGE pPrev = pImage->pPrev;
-            /* Remove image from list of opened images. */
-            vdRemoveImageFromList(pDisk, pImage);
-            /* Close image. */
-            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
-            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
-                rc = rc2;
-            /* Free remaining resources related to the image. */
-            RTStrFree(pImage->pszFilename);
-            RTMemFree(pImage);
-            pImage = pPrev;
-        }
-        Assert(!VALID_PTR(pDisk->pLast));
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Read data from virtual HDD.
- *
- * @returns VBox status code.
- * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   uOffset         Offset of first reading byte from start of disk.
- * @param   pvBuf           Pointer to buffer for reading data.
- * @param   cbRead          Number of bytes to read.
- */
-VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
-                         size_t cbRead)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
-                 pDisk, uOffset, pvBuf, cbRead));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pvBuf),
-                           ("pvBuf=%#p\n", pvBuf),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbRead,
-                           ("cbRead=%zu\n", cbRead),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
-                           ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
-                            uOffset, cbRead, pDisk->cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
-
-        rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
-                          true /* fZeroFreeBlocks */,
-                          true /* fUpdateCache */);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Write data to virtual HDD.
- *
- * @returns VBox status code.
- * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   uOffset         Offset of the first byte being
- *                          written from start of disk.
- * @param   pvBuf           Pointer to buffer for writing data.
- * @param   cbWrite         Number of bytes to write.
- */
-VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
-                          size_t cbWrite)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
-                 pDisk, uOffset, pvBuf, cbWrite));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pvBuf),
-                           ("pvBuf=%#p\n", pvBuf),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbWrite,
-                           ("cbWrite=%zu\n", cbWrite),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
-                           ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
-                            uOffset, cbWrite, pDisk->cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
-
-        vdSetModifiedFlag(pDisk);
-        rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
-                           true /* fUpdateCache */);
-        if (RT_FAILURE(rc))
-            break;
-
-        /* If there is a merge (in the direction towards a parent) running
-         * concurrently then we have to also "relay" the write to this parent,
-         * as the merge position might be already past the position where
-         * this write is going. The "context" of the write can come from the
-         * natural chain, since merging either already did or will take care
-         * of the "other" content which is might be needed to fill the block
-         * to a full allocation size. The cache doesn't need to be touched
-         * as this write is covered by the previous one. */
-        if (RT_UNLIKELY(pDisk->pImageRelay))
-            rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
-                               pvBuf, cbWrite, false /* fUpdateCache */);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Make sure the on disk representation of a virtual HDD is up to date.
- *
- * @returns VBox status code.
- * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
-
-        vdResetModifiedFlag(pDisk);
-        rc = pImage->Backend->pfnFlush(pImage->pBackendData);
-
-        if (   RT_SUCCESS(rc)
-            && pDisk->pCache)
-            rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get number of opened images in HDD container.
- *
- * @returns Number of opened images for HDD container. 0 if no images have been opened.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
-{
-    unsigned cImages;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, cImages = 0);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        cImages = pDisk->cImages;
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %u\n", cImages));
-    return cImages;
-}
-
-/**
- * Get read/write mode of HDD container.
- *
- * @returns Virtual disk ReadOnly status.
- * @returns true if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
-{
-    bool fReadOnly;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, fReadOnly = false);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, fReadOnly = true);
-
-        unsigned uOpenFlags;
-        uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
-        fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %d\n", fReadOnly));
-    return fReadOnly;
-}
-
-/**
- * Get total capacity of an image in HDD container.
- *
- * @returns Virtual disk size in bytes.
- * @returns 0 if no image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- */
-VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
-{
-    uint64_t cbSize;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, cbSize = 0);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, cbSize = 0);
-        cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %llu\n", cbSize));
-    return cbSize;
-}
-
-/**
- * Get total file size of an image in HDD container.
- *
- * @returns Virtual disk size in bytes.
- * @returns 0 if no image is opened in HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- */
-VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
-{
-    uint64_t cbSize;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, cbSize = 0);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, cbSize = 0);
-        cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %llu\n", cbSize));
-    return cbSize;
-}
-
-/**
- * Get virtual disk PCHS geometry stored in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pPCHSGeometry   Where to store PCHS geometry. Not NULL.
- */
-VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PVDGEOMETRY pPCHSGeometry)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
-                 pDisk, nImage, pPCHSGeometry));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
-                           ("pPCHSGeometry=%#p\n", pPCHSGeometry),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        if (pImage == pDisk->pLast)
-        {
-            /* Use cached information if possible. */
-            if (pDisk->PCHSGeometry.cCylinders != 0)
-                *pPCHSGeometry = pDisk->PCHSGeometry;
-            else
-                rc = VERR_VD_GEOMETRY_NOT_SET;
-        }
-        else
-            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                     pPCHSGeometry);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
-                 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
-                 pDisk->PCHSGeometry.cSectors));
-    return rc;
-}
-
-/**
- * Store virtual disk PCHS geometry in HDD container.
- *
- * Note that in case of unrecoverable error all images in HDD container will be closed.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pPCHSGeometry   Where to load PCHS geometry from. Not NULL.
- */
-VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PCVDGEOMETRY pPCHSGeometry)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
-                 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
-                 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(   VALID_PTR(pPCHSGeometry)
-                           && pPCHSGeometry->cHeads <= 16
-                           && pPCHSGeometry->cSectors <= 63,
-                           ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
-                            pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
-                            pPCHSGeometry->cSectors),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        if (pImage == pDisk->pLast)
-        {
-            if (    pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
-                ||  pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
-                ||  pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
-            {
-                /* Only update geometry if it is changed. Avoids similar checks
-                 * in every backend. Most of the time the new geometry is set
-                 * to the previous values, so no need to go through the hassle
-                 * of updating an image which could be opened in read-only mode
-                 * right now. */
-                rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
-                                                         pPCHSGeometry);
-
-                /* Cache new geometry values in any case. */
-                rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                          &pDisk->PCHSGeometry);
-                if (RT_FAILURE(rc2))
-                {
-                    pDisk->PCHSGeometry.cCylinders = 0;
-                    pDisk->PCHSGeometry.cHeads = 0;
-                    pDisk->PCHSGeometry.cSectors = 0;
-                }
-                else
-                {
-                    /* Make sure the CHS geometry is properly clipped. */
-                    pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
-                    pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
-                }
-            }
-        }
-        else
-        {
-            VDGEOMETRY PCHS;
-            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
-                                                     &PCHS);
-            if (    RT_FAILURE(rc)
-                ||  pPCHSGeometry->cCylinders != PCHS.cCylinders
-                ||  pPCHSGeometry->cHeads != PCHS.cHeads
-                ||  pPCHSGeometry->cSectors != PCHS.cSectors)
-            {
-                /* Only update geometry if it is changed. Avoids similar checks
-                 * in every backend. Most of the time the new geometry is set
-                 * to the previous values, so no need to go through the hassle
-                 * of updating an image which could be opened in read-only mode
-                 * right now. */
-                rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
-                                                         pPCHSGeometry);
-            }
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get virtual disk LCHS geometry stored in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pLCHSGeometry   Where to store LCHS geometry. Not NULL.
- */
-VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PVDGEOMETRY pLCHSGeometry)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
-                 pDisk, nImage, pLCHSGeometry));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
-                           ("pLCHSGeometry=%#p\n", pLCHSGeometry),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        if (pImage == pDisk->pLast)
-        {
-            /* Use cached information if possible. */
-            if (pDisk->LCHSGeometry.cCylinders != 0)
-                *pLCHSGeometry = pDisk->LCHSGeometry;
-            else
-                rc = VERR_VD_GEOMETRY_NOT_SET;
-        }
-        else
-            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                     pLCHSGeometry);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
-                 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
-                 pDisk->LCHSGeometry.cSectors));
-    return rc;
-}
-
-/**
- * Store virtual disk LCHS geometry in HDD container.
- *
- * Note that in case of unrecoverable error all images in HDD container will be closed.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pLCHSGeometry   Where to load LCHS geometry from. Not NULL.
- */
-VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
-                                    PCVDGEOMETRY pLCHSGeometry)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
-                 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
-                 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(   VALID_PTR(pLCHSGeometry)
-                           && pLCHSGeometry->cHeads <= 255
-                           && pLCHSGeometry->cSectors <= 63,
-                           ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
-                            pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
-                            pLCHSGeometry->cSectors),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        if (pImage == pDisk->pLast)
-        {
-            if (    pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
-                ||  pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
-                ||  pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
-            {
-                /* Only update geometry if it is changed. Avoids similar checks
-                 * in every backend. Most of the time the new geometry is set
-                 * to the previous values, so no need to go through the hassle
-                 * of updating an image which could be opened in read-only mode
-                 * right now. */
-                rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
-                                                         pLCHSGeometry);
-
-                /* Cache new geometry values in any case. */
-                rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                          &pDisk->LCHSGeometry);
-                if (RT_FAILURE(rc2))
-                {
-                    pDisk->LCHSGeometry.cCylinders = 0;
-                    pDisk->LCHSGeometry.cHeads = 0;
-                    pDisk->LCHSGeometry.cSectors = 0;
-                }
-                else
-                {
-                    /* Make sure the CHS geometry is properly clipped. */
-                    pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
-                    pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
-                }
-            }
-        }
-        else
-        {
-            VDGEOMETRY LCHS;
-            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
-                                                     &LCHS);
-            if (    RT_FAILURE(rc)
-                ||  pLCHSGeometry->cCylinders != LCHS.cCylinders
-                ||  pLCHSGeometry->cHeads != LCHS.cHeads
-                ||  pLCHSGeometry->cSectors != LCHS.cSectors)
-            {
-                /* Only update geometry if it is changed. Avoids similar checks
-                 * in every backend. Most of the time the new geometry is set
-                 * to the previous values, so no need to go through the hassle
-                 * of updating an image which could be opened in read-only mode
-                 * right now. */
-                rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
-                                                         pLCHSGeometry);
-            }
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get version of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puVersion       Where to store the image version.
- */
-VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
-                               unsigned *puVersion)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
-                 pDisk, nImage, puVersion));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(puVersion),
-                           ("puVersion=%#p\n", puVersion),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
-    return rc;
-}
-
-/**
- * List the capabilities of image backend in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to the HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pbackendInfo    Where to store the backend information.
- */
-VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
-                                      PVDBACKENDINFO pBackendInfo)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
-                 pDisk, nImage, pBackendInfo));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
-                           ("pBackendInfo=%#p\n", pBackendInfo),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
-        pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
-        pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
-        pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get flags of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puImageFlags    Where to store the image flags.
- */
-VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
-                                  unsigned *puImageFlags)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
-                 pDisk, nImage, puImageFlags));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(puImageFlags),
-                           ("puImageFlags=%#p\n", puImageFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        *puImageFlags = pImage->uImageFlags;
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
-    return rc;
-}
-
-/**
- * Get open flags of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   puOpenFlags     Where to store the image open flags.
- */
-VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
-                                 unsigned *puOpenFlags)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
-                 pDisk, nImage, puOpenFlags));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
-                           ("puOpenFlags=%#p\n", puOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
-    return rc;
-}
-
-/**
- * Set open flags of image in HDD container.
- * This operation may cause file locking changes and/or files being reopened.
- * Note that in case of unrecoverable error all images in HDD container will be closed.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
- */
-VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
-                                 unsigned uOpenFlags)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
-                           ("uOpenFlags=%#x\n", uOpenFlags),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
-                                              uOpenFlags);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get base filename of image in HDD container. Some image formats use
- * other filenames as well, so don't use this for anything but informational
- * purposes.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszFilename     Where to store the image file name.
- * @param   cbFilename      Size of buffer pszFilename points to.
- */
-VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
-                                char *pszFilename, unsigned cbFilename)
-{
-    int rc;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
-                 pDisk, nImage, pszFilename, cbFilename));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
-                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbFilename,
-                           ("cbFilename=%u\n", cbFilename),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        size_t cb = strlen(pImage->pszFilename);
-        if (cb <= cbFilename)
-        {
-            strcpy(pszFilename, pImage->pszFilename);
-            rc = VINF_SUCCESS;
-        }
-        else
-        {
-            strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
-            pszFilename[cbFilename - 1] = '\0';
-            rc = VERR_BUFFER_OVERFLOW;
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
-    return rc;
-}
-
-/**
- * Get the comment line of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszComment      Where to store the comment string of image. NULL is ok.
- * @param   cbComment       The size of pszComment buffer. 0 is ok.
- */
-VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
-                               char *pszComment, unsigned cbComment)
-{
-    int rc;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
-                 pDisk, nImage, pszComment, cbComment));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszComment),
-                           ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cbComment,
-                           ("cbComment=%u\n", cbComment),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
-                                            cbComment);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
-    return rc;
-}
-
-/**
- * Changes the comment line of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
- */
-VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
-                               const char *pszComment)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
-                 pDisk, nImage, pszComment, pszComment));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
-                           ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * Get UUID of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           Where to store the image creation UUID.
- */
-VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
-{
-    int rc;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pUuid),
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
-    return rc;
-}
-
-/**
- * Set the image's UUID. Should not be used by normal applications.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
-                 pDisk, nImage, pUuid, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        RTUUID Uuid;
-        if (!pUuid)
-        {
-            RTUuidCreate(&Uuid);
-            pUuid = &Uuid;
-        }
-        rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get last modification UUID of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           Where to store the image modification UUID.
- */
-VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pUuid),
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
-                                                     pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
-    return rc;
-}
-
-/**
- * Set the image's last modification UUID. Should not be used by normal applications.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New modification UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
-                 pDisk, nImage, pUuid, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        RTUUID Uuid;
-        if (!pUuid)
-        {
-            RTUuidCreate(&Uuid);
-            pUuid = &Uuid;
-        }
-        rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
-                                                     pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Get parent UUID of image in HDD container.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           Where to store the parent image UUID.
- */
-VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
-                                  PRTUUID pUuid)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pUuid),
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
-    return rc;
-}
-
-/**
- * Set the image's parent UUID. Should not be used by normal applications.
- *
- * @returns VBox status code.
- * @param   pDisk           Pointer to HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pUuid           New parent UUID of the image. If NULL, a new UUID is created.
- */
-VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
-                                  PCRTUUID pUuid)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
-                 pDisk, nImage, pUuid, pUuid));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
-                           ("pUuid=%#p\n", pUuid),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        RTUUID Uuid;
-        if (!pUuid)
-        {
-            RTUuidCreate(&Uuid);
-            pUuid = &Uuid;
-        }
-        rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/**
- * Debug helper - dumps all opened images in HDD container into the log file.
- *
- * @param   pDisk           Pointer to HDD container.
- */
-VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
-{
-    int rc2;
-    bool fLockRead = false;
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreak(pDisk);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
-            pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
-        for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
-        {
-            vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
-                             pImage->pszFilename, pImage->Backend->pszBackendName);
-            pImage->Backend->pfnDump(pImage->pBackendData);
-        }
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-}
-
-/**
- * Query if asynchronous operations are supported for this disk.
- *
- * @returns VBox status code.
- * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
- * @param   pDisk           Pointer to the HDD container.
- * @param   nImage          Image number, counts from 0. 0 is always base image of container.
- * @param   pfAIOSupported  Where to store if async IO is supported.
- */
-VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
-{
-    int rc = VINF_SUCCESS;
-    int rc2;
-    bool fLockRead = false;
-
-    LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
-                           ("pfAIOSupported=%#p\n", pfAIOSupported),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
-
-        if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
-            *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
-        else
-            *pfAIOSupported = false;
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
-    return rc;
-}
-
-
-VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
-                              PCRTSGSEG paSeg, unsigned cSeg,
-                              PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                              void *pvUser1, void *pvUser2)
-{
-    int rc = VERR_VD_BLOCK_FREE;
-    int rc2;
-    bool fLockRead = false;
-    PVDIOCTX pIoCtx = NULL;
-
-    LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
-                 pDisk, uOffset, paSeg, cSeg, cbRead, pvUser1, pvUser2));
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(cbRead,
-                           ("cbRead=%zu\n", cbRead),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(paSeg),
-                           ("paSeg=%#p\n", paSeg),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cSeg,
-                           ("cSeg=%zu\n", cSeg),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartRead(pDisk);
-        AssertRC(rc2);
-        fLockRead = true;
-
-        AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
-                           ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
-                            uOffset, cbRead, pDisk->cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-
-        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
-                                  cbRead, paSeg, cSeg,
-                                  pfnComplete, pvUser1, pvUser2,
-                                  NULL, vdReadHelperAsync);
-        if (!pIoCtx)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        pIoCtx->pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
-
-        rc = vdIoCtxProcess(pIoCtx);
-        if (rc == VINF_VD_ASYNC_IO_FINISHED)
-        {
-            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
-                vdIoCtxFree(pDisk, pIoCtx);
-            else
-                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
-        }
-        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
-            vdIoCtxFree(pDisk, pIoCtx);
-
-    } while (0);
-
-    if (RT_UNLIKELY(fLockRead) && (   rc == VINF_VD_ASYNC_IO_FINISHED
-                                   || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-    {
-        rc2 = vdThreadFinishRead(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
-                               PCRTSGSEG paSeg, unsigned cSeg,
-                               PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                               void *pvUser1, void *pvUser2)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-    PVDIOCTX pIoCtx = NULL;
-
-    LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
-                 pDisk, uOffset, paSeg, cSeg, cbWrite, pvUser1, pvUser2));
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        /* Check arguments. */
-        AssertMsgBreakStmt(cbWrite,
-                           ("cbWrite=%zu\n", cbWrite),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(VALID_PTR(paSeg),
-                           ("paSeg=%#p\n", paSeg),
-                           rc = VERR_INVALID_PARAMETER);
-        AssertMsgBreakStmt(cSeg,
-                           ("cSeg=%zu\n", cSeg),
-                           rc = VERR_INVALID_PARAMETER);
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
-                           ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
-                            uOffset, cbWrite, pDisk->cbSize),
-                           rc = VERR_INVALID_PARAMETER);
-
-        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
-                                  cbWrite, paSeg, cSeg,
-                                  pfnComplete, pvUser1, pvUser2,
-                                  NULL, vdWriteHelperAsync);
-        if (!pIoCtx)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
-        pIoCtx->pImage = pImage;
-
-        rc = vdIoCtxProcess(pIoCtx);
-        if (rc == VINF_VD_ASYNC_IO_FINISHED)
-        {
-            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
-                vdIoCtxFree(pDisk, pIoCtx);
-            else
-                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
-        }
-        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
-            vdIoCtxFree(pDisk, pIoCtx);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
-                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
-                               void *pvUser1, void *pvUser2)
-{
-    int rc;
-    int rc2;
-    bool fLockWrite = false;
-    PVDIOCTX pIoCtx = NULL;
-
-    LogFlowFunc(("pDisk=%#p\n", pDisk));
-
-    do
-    {
-        /* sanity check */
-        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
-        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
-
-        rc2 = vdThreadStartWrite(pDisk);
-        AssertRC(rc2);
-        fLockWrite = true;
-
-        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
-                                  0, NULL, 0,
-                                  pfnComplete, pvUser1, pvUser2,
-                                  NULL, vdFlushHelperAsync);
-        if (!pIoCtx)
-        {
-            rc = VERR_NO_MEMORY;
-            break;
-        }
-
-        PVDIMAGE pImage = pDisk->pLast;
-        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
-        pIoCtx->pImage = pImage;
-
-        rc = vdIoCtxProcess(pIoCtx);
-        if (rc == VINF_VD_ASYNC_IO_FINISHED)
-        {
-            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
-                vdIoCtxFree(pDisk, pIoCtx);
-            else
-                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
-        }
-        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
-            vdIoCtxFree(pDisk, pIoCtx);
-    } while (0);
-
-    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
-                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-    {
-        rc2 = vdThreadFinishWrite(pDisk);
-        AssertRC(rc2);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
Index: unk/src/VBox/Devices/Storage/VCICacheCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VCICacheCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,2104 +1,0 @@
-/* $Id$ */
-/** @file
- * VCICacheCore - VirtualBox Cache Image, Core Code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_RAW /** @todo logging group */
-#include <VBox/VBoxHDD-CachePlugin.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/assert.h>
-#include <iprt/alloc.h>
-#include <iprt/file.h>
-#include <iprt/asm.h>
-
-/*******************************************************************************
-* On disk data structures                                                      *
-*******************************************************************************/
-
-/** @note All structures which are written to the disk are written in camel case
- * and packed. */
-
-/** Block size used internally, because we cache sectors the smallest unit we
- * have to care about is 512 bytes. */
-#define VCI_BLOCK_SIZE             512
-
-/** Convert block number/size to byte offset/size. */
-#define VCI_BLOCK2BYTE(u)          ((uint64_t)(u) << 9)
-
-/** Convert byte offset/size to block number/size. */
-#define VCI_BYTE2BLOCK(u)          ((u) >> 9)
-
-/**
- * The VCI header - at the beginning of the file.
- *
- * All entries a stored in little endian order.
- */
-#pragma pack(1)
-typedef struct VciHdr
-{
-    /** The signature to identify a cache image. */
-    uint32_t    u32Signature;
-    /** Version of the layout of metadata in the cache. */
-    uint32_t    u32Version;
-    /** Maximum size of the cache file in blocks.
-     *  This includes all metadata. */
-    uint64_t    cBlocksCache;
-    /** Flag indicating whether the cache was closed cleanly. */
-    uint8_t     fUncleanShutdown;
-    /** Cache type. */
-    uint32_t    u32CacheType;
-    /** Offset of the B+-Tree root in the image in blocks. */
-    uint64_t    offTreeRoot;
-    /** Offset of the block allocation bitmap in blocks. */
-    uint64_t    offBlkMap;
-    /** Size of the block allocation bitmap in blocks. */
-    uint32_t    cBlkMap;
-    /** UUID of the image. */
-    RTUUID      uuidImage;
-    /** Modification UUID for the cache. */
-    RTUUID      uuidModification;
-    /** Reserved for future use. */
-    uint8_t     abReserved[951];
-} VciHdr, *PVciHdr;
-#pragma pack()
-AssertCompileSize(VciHdr, 2 * VCI_BLOCK_SIZE);
-
-/** VCI signature to identify a valid image. */
-#define VCI_HDR_SIGNATURE          UINT32_C(0x56434900) /* VCI\0 */
-/** Current version we support. */
-#define VCI_HDR_VERSION            UINT32_C(0x00000001)
-
-/** Value for an unclean cache shutdown. */
-#define VCI_HDR_UNCLEAN_SHUTDOWN   UINT8_C(0x01)
-/** Value for a clean cache shutdown. */
-#define VCI_HDR_CLEAN_SHUTDOWN     UINT8_C(0x00)
-
-/** Cache type: Dynamic image growing to the maximum value. */
-#define VCI_HDR_CACHE_TYPE_DYNAMIC UINT32_C(0x00000001)
-/** Cache type: Fixed image, space is preallocated. */
-#define VCI_HDR_CACHE_TYPE_FIXED   UINT32_C(0x00000002)
-
-/**
- * On disk representation of an extent describing a range of cached data.
- *
- * All entries a stored in little endian order.
- */
-#pragma pack(1)
-typedef struct VciCacheExtent
-{
-    /** Block address of the previous extent in the LRU list. */
-    uint64_t    u64ExtentPrev;
-    /** Block address of the next extent in the LRU list. */
-    uint64_t    u64ExtentNext;
-    /** Flags (for compression, encryption etc.) - currently unused and should be always 0. */
-    uint8_t     u8Flags;
-    /** Reserved */
-    uint8_t     u8Reserved;
-    /** First block of cached data the extent represents. */
-    uint64_t    u64BlockOffset;
-    /** Number of blocks the extent represents. */
-    uint32_t    u32Blocks;
-    /** First block in the image where the data is stored. */
-    uint64_t    u64BlockAddr;
-} VciCacheExtent, *PVciCacheExtent;
-#pragma pack()
-AssertCompileSize(VciCacheExtent, 38);
-
-/**
- * On disk representation of an internal node.
- *
- * All entries a stored in little endian order.
- */
-#pragma pack(1)
-typedef struct VciTreeNodeInternal
-{
-    /** First block of cached data the internal node represents. */
-    uint64_t    u64BlockOffset;
-    /** Number of blocks the internal node represents. */
-    uint32_t    u32Blocks;
-    /** Block address in the image where the next node in the tree is stored. */
-    uint64_t    u64ChildAddr;
-} VciTreeNodeInternal, *PVciTreeNodeInternal;
-#pragma pack()
-AssertCompileSize(VciTreeNodeInternal, 20);
-
-/**
- * On-disk representation of a node in the B+-Tree.
- *
- * All entries a stored in little endian order.
- */
-#pragma pack(1)
-typedef struct VciTreeNode
-{
-    /** Type of the node (root, internal, leaf). */
-    uint8_t     u8Type;
-    /** Data in the node. */
-    uint8_t     au8Data[4095];
-} VciTreeNode, *PVciTreeNode;
-#pragma pack()
-AssertCompileSize(VciTreeNode, 8 * VCI_BLOCK_SIZE);
-
-/** Node type: Internal node containing links to other nodes (VciTreeNodeInternal). */
-#define VCI_TREE_NODE_TYPE_INTERNAL UINT8_C(0x01)
-/** Node type: Leaf of the tree (VciCacheExtent). */
-#define VCI_TREE_NODE_TYPE_LEAF     UINT8_C(0x02)
-
-/** Number of cache extents described by one node. */
-#define VCI_TREE_EXTENTS_PER_NODE        ((sizeof(VciTreeNode)-1) / sizeof(VciCacheExtent))
-/** Number of internal nodes managed by one tree node. */
-#define VCI_TREE_INTERNAL_NODES_PER_NODE ((sizeof(VciTreeNode)-1) / sizeof(VciTreeNodeInternal))
-
-/**
- * VCI block bitmap header.
- *
- * All entries a stored in little endian order.
- */
-#pragma pack(1)
-typedef struct VciBlkMap
-{
-    /** Magic of the block bitmap. */
-    uint32_t     u32Magic;
-    /** Version of the block bitmap. */
-    uint32_t     u32Version;
-    /** Number of blocks this block map manages. */
-    uint64_t     cBlocks;
-    /** Number of free blocks. */
-    uint64_t     cBlocksFree;
-    /** Number of blocks allocated for metadata. */
-    uint64_t     cBlocksAllocMeta;
-    /** Number of blocks allocated for actual cached data. */
-    uint64_t     cBlocksAllocData;
-    /** Reserved for future use. */
-    uint8_t      au8Reserved[472];
-} VciBlkMap, *PVciBlkMap;
-#pragma pack()
-AssertCompileSize(VciBlkMap, VCI_BLOCK_SIZE);
-
-/** The magic which identifies a block map. */
-#define VCI_BLKMAP_MAGIC   UINT32_C(0x56424c4b) /* VBLK */
-/** Current version. */
-#define VCI_BLKMAP_VERSION UINT32_C(0x00000001)
-
-/** Block bitmap entry */
-typedef uint8_t VciBlkMapEnt;
-
-/*******************************************************************************
-*   Constants And Macros, Structures and Typedefs                              *
-*******************************************************************************/
-
-/**
- * Block range descriptor.
- */
-typedef struct VCIBLKRANGEDESC
-{
-    /** Previous entry in the list. */
-    struct VCIBLKRANGEDESC    *pPrev;
-    /** Next entry in the list. */
-    struct VCIBLKRANGEDESC    *pNext;
-    /** Start address of the range. */
-    uint64_t                   offAddrStart;
-    /** Number of blocks in the range. */
-    uint64_t                   cBlocks;
-    /** Flag whether the range is free or allocated. */
-    bool                       fFree;
-} VCIBLKRANGEDESC, *PVCIBLKRANGEDESC;
-
-/**
- * Block map for the cache image - in memory structure.
- */
-typedef struct VCIBLKMAP
-{
-    /** Number of blocks the map manages. */
-    uint64_t     cBlocks;
-    /** Number of blocks allocated for metadata. */
-    uint64_t     cBlocksAllocMeta;
-    /** Number of blocks allocated for actual cached data. */
-    uint64_t     cBlocksAllocData;
-    /** Number of free blocks. */
-    uint64_t     cBlocksFree;
-
-    /** Pointer to the head of the block range list. */
-    PVCIBLKRANGEDESC pRangesHead;
-    /** Pointer to the tail of the block range list. */
-    PVCIBLKRANGEDESC pRangesTail;
-
-} VCIBLKMAP;
-/** Pointer to a block map. */
-typedef VCIBLKMAP *PVCIBLKMAP;
-
-/**
- * B+-Tree node header.
- */
-typedef struct VCITREENODE
-{
-    /** Type of the node (VCI_TREE_NODE_TYPE_*). */
-    uint8_t             u8Type;
-    /** Block address where the node is stored. */
-    uint64_t            u64BlockAddr;
-    /** Pointer to the parent. */
-    struct VCITREENODE *pParent;
-} VCITREENODE, *PVCITREENODE;
-
-/**
- * B+-Tree node pointer.
- */
-typedef struct VCITREENODEPTR
-{
-    /** Flag whether the node is in memory or still on the disk. */
-    bool         fInMemory;
-    /** Type dependent data. */
-    union
-    {
-        /** Pointer to a in memory node. */
-        PVCITREENODE pNode;
-        /** Start block address of the node. */
-        uint64_t     offAddrBlockNode;
-    } u;
-} VCITREENODEPTR, *PVCITREENODEPTR;
-
-/**
- * Internal node.
- */
-typedef struct VCINODEINTERNAL
-{
-    /** First block of cached data the internal node represents. */
-    uint64_t       u64BlockOffset;
-    /** Number of blocks the internal node represents. */
-    uint32_t       u32Blocks;
-    /** Pointer to the child node. */
-    VCITREENODEPTR PtrChild;
-} VCINODEINTERNAL, *PVCINODEINTERNAL;
-
-/**
- * A in memory internal B+-tree node.
- */
-typedef struct VCITREENODEINT
-{
-    /** Node core. */
-    VCITREENODE     Core;
-    /** Number of used nodes. */
-    unsigned        cUsedNodes;
-    /** Array of internal nodes. */
-    VCINODEINTERNAL aIntNodes[VCI_TREE_INTERNAL_NODES_PER_NODE];
-} VCITREENODEINT, *PVCITREENODEINT;
-
-/**
- * A in memory cache extent.
- */
-typedef struct VCICACHEEXTENT
-{
-    /** First block of cached data the extent represents. */
-    uint64_t    u64BlockOffset;
-    /** Number of blocks the extent represents. */
-    uint32_t    u32Blocks;
-    /** First block in the image where the data is stored. */
-    uint64_t    u64BlockAddr;
-} VCICACHEEXTENT, *PVCICACHEEXTENT;
-
-/**
- * A in memory leaf B+-tree node.
- */
-typedef struct VCITREENODELEAF
-{
-    /** Node core. */
-    VCITREENODE    Core;
-    /** Number of used nodes. */
-    unsigned       cUsedNodes;
-    /** The extents in the node. */
-    VCICACHEEXTENT aExtents[VCI_TREE_EXTENTS_PER_NODE];
-} VCITREENODELEAF, *PVCITREENODELEAF;
-
-/**
- * VCI image data structure.
- */
-typedef struct VCICACHE
-{
-    /** Image name. */
-    const char       *pszFilename;
-    /** Storage handle. */
-    PVDIOSTORAGE      pStorage;
-    /** I/O interface. */
-    PVDINTERFACE      pInterfaceIO;
-    /** Async I/O interface callbacks. */
-    PVDINTERFACEIOINT pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE      pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE      pVDIfsImage;
-
-    /** Error callback. */
-    PVDINTERFACE      pInterfaceError;
-    /** Opaque data for error callback. */
-    PVDINTERFACEERROR pInterfaceErrorCallbacks;
-
-    /** Open flags passed by VBoxHD layer. */
-    unsigned          uOpenFlags;
-    /** Image flags defined during creation or determined during open. */
-    unsigned          uImageFlags;
-    /** Total size of the image. */
-    uint64_t          cbSize;
-
-    /** Offset of the B+-Tree in the image in bytes. */
-    uint64_t          offTreeRoot;
-    /** Pointer to the root node of the B+-Tree. */
-    PVCITREENODE      pRoot;
-    /** Offset to the block allocation bitmap in bytes. */
-    uint64_t          offBlksBitmap;
-    /** Block map. */
-    PVCIBLKMAP        pBlkMap;
-} VCICACHE, *PVCICACHE;
-
-/** No block free in bitmap error code. */
-#define VERR_VCI_NO_BLOCKS_FREE (-65536)
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const char *const s_apszVciFileExtensions[] =
-{
-    "vci",
-    NULL
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) vciError(PVCICACHE pCache, int rc, RT_SRC_POS_DECL,
-                         const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pCache->pInterfaceError)
-        pCache->pInterfaceErrorCallbacks->pfnError(pCache->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) vciMessage(PVCICACHE pCache, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pCache->pInterfaceError)
-        rc = pCache->pInterfaceErrorCallbacks->pfnMessage(pCache->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) vciFileOpen(PVCICACHE pCache, const char *pszFilename,
-                            uint32_t fOpen)
-{
-    return pCache->pInterfaceIOCallbacks->pfnOpen(pCache->pInterfaceIO->pvUser,
-                                                  pszFilename, fOpen,
-                                                  &pCache->pStorage);
-}
-
-DECLINLINE(int) vciFileClose(PVCICACHE pCache)
-{
-    return pCache->pInterfaceIOCallbacks->pfnClose(pCache->pInterfaceIO->pvUser,
-                                                   pCache->pStorage);
-}
-
-DECLINLINE(int) vciFileDelete(PVCICACHE pCache, const char *pszFilename)
-{
-    return pCache->pInterfaceIOCallbacks->pfnDelete(pCache->pInterfaceIO->pvUser,
-                                                    pszFilename);
-}
-
-DECLINLINE(int) vciFileMove(PVCICACHE pCache, const char *pszSrc,
-                            const char *pszDst, unsigned fMove)
-{
-    return pCache->pInterfaceIOCallbacks->pfnMove(pCache->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) vciFileGetFreeSpace(PVCICACHE pCache, const char *pszFilename,
-                                    int64_t *pcbFree)
-{
-    return pCache->pInterfaceIOCallbacks->pfnGetFreeSpace(pCache->pInterfaceIO->pvUser,
-                                                          pszFilename, pcbFree);
-}
-
-DECLINLINE(int) vciFileGetSize(PVCICACHE pCache, uint64_t *pcbSize)
-{
-    return pCache->pInterfaceIOCallbacks->pfnGetSize(pCache->pInterfaceIO->pvUser,
-                                                     pCache->pStorage, pcbSize);
-}
-
-DECLINLINE(int) vciFileSetSize(PVCICACHE pCache, uint64_t cbSize)
-{
-    return pCache->pInterfaceIOCallbacks->pfnSetSize(pCache->pInterfaceIO->pvUser,
-                                                     pCache->pStorage, cbSize);
-}
-
-DECLINLINE(int) vciFileWriteSync(PVCICACHE pCache, uint64_t uOffset,
-                                 const void *pvBuffer, size_t cbBuffer)
-{
-    return pCache->pInterfaceIOCallbacks->pfnWriteSync(pCache->pInterfaceIO->pvUser,
-                                                       pCache->pStorage, uOffset,
-                                                       pvBuffer, cbBuffer, NULL);
-}
-
-DECLINLINE(int) vciFileReadSync(PVCICACHE pCache, uint64_t uOffset,
-                                void *pvBuffer, size_t cbBuffer)
-{
-    return pCache->pInterfaceIOCallbacks->pfnReadSync(pCache->pInterfaceIO->pvUser,
-                                                      pCache->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, NULL);
-}
-
-DECLINLINE(int) vciFileFlushSync(PVCICACHE pCache)
-{
-    return pCache->pInterfaceIOCallbacks->pfnFlushSync(pCache->pInterfaceIO->pvUser,
-                                                       pCache->pStorage);
-}
-
-DECLINLINE(int) vciFileReadUserAsync(PVCICACHE pCache, uint64_t uOffset,
-                                     PVDIOCTX pIoCtx, size_t cbRead)
-{
-    return pCache->pInterfaceIOCallbacks->pfnReadUserAsync(pCache->pInterfaceIO->pvUser,
-                                                           pCache->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-DECLINLINE(int) vciFileWriteUserAsync(PVCICACHE pCache, uint64_t uOffset,
-                                      PVDIOCTX pIoCtx, size_t cbWrite,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pCache->pInterfaceIOCallbacks->pfnWriteUserAsync(pCache->pInterfaceIO->pvUser,
-                                                            pCache->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) vciFileFlushAsync(PVCICACHE pCache, PVDIOCTX pIoCtx,
-                                  PFNVDXFERCOMPLETED pfnComplete,
-                                  void *pvCompleteUser)
-{
-    return pCache->pInterfaceIOCallbacks->pfnFlushAsync(pCache->pInterfaceIO->pvUser,
-                                                        pCache->pStorage,
-                                                        pIoCtx, pfnComplete,
-                                                        pvCompleteUser);
-}
-
-/**
- * Internal. Flush image data to disk.
- */
-static int vciFlushImage(PVCICACHE pCache)
-{
-    int rc = VINF_SUCCESS;
-
-    if (   pCache->pStorage
-        && !(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        rc = vciFileFlushSync(pCache);
-
-    return rc;
-}
-
-/**
- * Internal. Free all allocated space for representing an image except pCache,
- * and optionally delete the image from disk.
- */
-static int vciFreeImage(PVCICACHE pCache, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pCache)
-    {
-        if (pCache->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-                vciFlushImage(pCache);
-
-            vciFileClose(pCache);
-            pCache->pStorage = NULL;
-        }
-
-        if (fDelete && pCache->pszFilename)
-            vciFileDelete(pCache, pCache->pszFilename);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Creates a new block map which can manage the given number of blocks.
- *
- * The size of the bitmap is aligned to the VCI block size.
- *
- * @returns VBox status code.
- * @param   cBlocks      The number of blocks the bitmap can manage.
- * @param   ppBlkMap     Where to store the pointer to the block bitmap.
- * @param   pcbBlkMap    Where to store the size of the block bitmap in blocks
- *                       needed on the disk.
- */
-static int vciBlkMapCreate(uint64_t cBlocks, PVCIBLKMAP *ppBlkMap, uint32_t *pcBlkMap)
-{
-    int rc = VINF_SUCCESS;
-    uint32_t cbBlkMap = RT_ALIGN_Z(cBlocks / sizeof(VciBlkMapEnt) / 8, VCI_BLOCK_SIZE);
-    PVCIBLKMAP pBlkMap = (PVCIBLKMAP)RTMemAllocZ(sizeof(VCIBLKMAP));
-    PVCIBLKRANGEDESC pFree   = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
-
-    LogFlowFunc(("cBlocks=%u ppBlkMap=%#p pcBlkMap=%#p\n", cBlocks, ppBlkMap, pcBlkMap));
-
-    if (pBlkMap && pFree)
-    {
-        pBlkMap->cBlocks          = cBlocks;
-        pBlkMap->cBlocksAllocMeta = 0;
-        pBlkMap->cBlocksAllocData = 0;
-        pBlkMap->cBlocksFree      = cBlocks;
-
-        pFree->pPrev = NULL;
-        pFree->pNext = NULL;
-        pFree->offAddrStart = 0;
-        pFree->cBlocks      = cBlocks;
-        pFree->fFree        = true;
-
-        pBlkMap->pRangesHead = pFree;
-        pBlkMap->pRangesTail = pFree;
-
-        Assert(!((cbBlkMap + sizeof(VciBlkMap) % VCI_BLOCK_SIZE)));
-        *ppBlkMap = pBlkMap;
-        *pcBlkMap = (cbBlkMap + sizeof(VciBlkMap)) / VCI_BLOCK_SIZE;
-    }
-    else
-    {
-        if (pBlkMap)
-            RTMemFree(pBlkMap);
-        if (pFree)
-            RTMemFree(pFree);
-
-        rc = VERR_NO_MEMORY;
-    }
-
-    LogFlowFunc(("returns rc=%Rrc cBlkMap=%u\n", rc, *pcBlkMap));
-    return rc;
-}
-
-/**
- * Frees a block map.
- *
- * @returns nothing.
- * @param   pBlkMap         The block bitmap to destroy.
- */
-static void vciBlkMapDestroy(PVCIBLKMAP pBlkMap)
-{
-    LogFlowFunc(("pBlkMap=%#p\n", pBlkMap));
-
-    PVCIBLKRANGEDESC pRangeCur = pBlkMap->pRangesHead;
-
-    while (pRangeCur)
-    {
-        PVCIBLKRANGEDESC pTmp = pRangeCur;
-
-        RTMemFree(pTmp);
-
-        pRangeCur = pRangeCur->pNext;
-    }
-
-    RTMemFree(pBlkMap);
-
-    LogFlowFunc(("returns\n"));
-}
-
-/**
- * Loads the block map from the specified medium and creates all necessary
- * in memory structures to manage used and free blocks.
- *
- * @returns VBox status code.
- * @param   pStorage        Storage handle to read the block bitmap from.
- * @param   offBlkMap       Start of the block bitmap in blocks.
- * @param   cBlkMap         Size of the block bitmap on the disk in blocks.
- * @param   ppBlkMap        Where to store the block bitmap on success.
- */
-static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap, PVCIBLKMAP *ppBlkMap)
-{
-    int rc = VINF_SUCCESS;
-    VciBlkMap BlkMap;
-
-    LogFlowFunc(("pStorage=%#p offBlkMap=%llu cBlkMap=%u ppBlkMap=%#p\n",
-                 pStorage, offBlkMap, cBlkMap, ppBlkMap));
-
-    if (cBlkMap >= VCI_BYTE2BLOCK(sizeof(VciBlkMap)))
-    {
-        cBlkMap -= VCI_BYTE2BLOCK(sizeof(VciBlkMap));
-
-        rc = vciFileReadSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
-        if (RT_SUCCESS(rc))
-        {
-            offBlkMap += VCI_BYTE2BLOCK(sizeof(VciBlkMap));
-
-            BlkMap.u32Magic         = RT_LE2H_U32(BlkMap.u32Magic);
-            BlkMap.u32Version       = RT_LE2H_U32(BlkMap.u32Version);
-            BlkMap.cBlocks          = RT_LE2H_U32(BlkMap.cBlocks);
-            BlkMap.cBlocksFree      = RT_LE2H_U32(BlkMap.cBlocksFree);
-            BlkMap.cBlocksAllocMeta = RT_LE2H_U32(BlkMap.cBlocksAllocMeta);
-            BlkMap.cBlocksAllocData = RT_LE2H_U32(BlkMap.cBlocksAllocData);
-
-            if (   BlkMap.u32Magic == VCI_BLKMAP_MAGIC
-                && BlkMap.u32Version == VCI_BLKMAP_VERSION
-                && BlkMap.cBlocks == BlkMap.cBlocksFree + BlkMap.cBlocksAllocMeta + BlkMap.cBlocksAllocData
-                && BlkMap.cBlocks / 8 == cBlkMap)
-            {
-                PVCIBLKMAP pBlkMap = (PVCIBLKMAP)RTMemAllocZ(sizeof(VCIBLKMAP));
-                if (pBlkMap)
-                {
-                    pBlkMap->cBlocks          = BlkMap.cBlocks;
-                    pBlkMap->cBlocksFree      = BlkMap.cBlocksFree;
-                    pBlkMap->cBlocksAllocMeta = BlkMap.cBlocksAllocMeta;
-                    pBlkMap->cBlocksAllocData = BlkMap.cBlocksAllocData;
-
-                    /* Load the bitmap and construct the range list. */
-                    uint32_t cBlocksFree = 0;
-                    uint32_t cBlocksAllocated = 0;
-                    PVCIBLKRANGEDESC pRangeCur = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
-
-                    if (pRangeCur)
-                    {
-                        uint8_t abBitmapBuffer[16 * _1K];
-                        uint32_t cBlocksRead = 0;
-                        int iBit = 0;
-                        uint64_t cBlocksLeft = pBlkMap->cBlocks;
-
-                        cBlocksRead = RT_MIN(VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)), cBlocksLeft);
-                        rc = vciFileReadSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), abBitmapBuffer,
-                                             VCI_BLOCK2BYTE(cBlocksRead));
-
-                        if (RT_SUCCESS(rc))
-                        {
-                            pRangeCur->fFree        = !(abBitmapBuffer[0] & 0x01);
-                            pRangeCur->offAddrStart = 0;
-                            pRangeCur->cBlocks      = 0;
-                            pRangeCur->pNext        = NULL;
-                            pRangeCur->pPrev        = NULL;
-                            pBlkMap->pRangesHead = pRangeCur;
-                            pBlkMap->pRangesTail = pRangeCur;
-                        }
-
-                        while (   RT_SUCCESS(rc)
-                               && cBlocksLeft)
-                        {
-#if 0
-                            while (cBlocksRead)
-                            {
-                                if (pRangeCur->fFree)
-                                {
-                                    /* Check for the first set bit. */
-                                }
-                                else
-                                {
-                                    /* Check for the first free bit. */
-                                }
-
-                                if (iBit == -1)
-                                {
-                                    /* No change. */
-                                    pRangeCur->cBlocks += cBlocksRead;
-                                    cBlocksRead = 0;
-                                }
-                                else
-                                {
-                                    /* Create a new range descriptor. */
-                                }
-                            }
-#endif
-                            cBlocksLeft -= cBlocksRead;
-                            offBlkMap   += cBlocksRead;
-
-                            if (cBlocksLeft)
-                            {
-                                /* Read next chunk. */
-                            }
-                        }
-                    }
-                    else
-                        rc = VERR_NO_MEMORY;
-
-                    if (RT_SUCCESS(rc))
-                    {
-                        *ppBlkMap = pBlkMap;
-                        LogFlowFunc(("return success\n"));
-                        return VINF_SUCCESS;
-                    }
-                    else
-                        RTMemFree(pBlkMap);
-                }
-                else
-                    rc = VERR_NO_MEMORY;
-            }
-            else
-                rc = VERR_VD_GEN_INVALID_HEADER;
-        }
-        else if (RT_SUCCESS(rc))
-            rc = VERR_VD_GEN_INVALID_HEADER;
-    }
-    else
-        rc = VERR_VD_GEN_INVALID_HEADER;
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Saves the block map in the cache image. All necessary on disk structures
- * are written.
- *
- * @returns VBox status code.
- * @param   pBlkMap         The block bitmap to save.
- * @param   pStorage        Where the block bitmap should be written to.
- * @param   offBlkMap       Start of the block bitmap in blocks.
- * @param   cBlkMap         Size of the block bitmap on the disk in blocks.
- */
-static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap)
-{
-    int rc = VINF_SUCCESS;
-    VciBlkMap BlkMap;
-
-    LogFlowFunc(("pBlkMap=%#p pStorage=%#p offBlkMap=%llu cBlkMap=%u\n",
-                 pBlkMap, pStorage, offBlkMap, cBlkMap));
-
-    /* Make sure the number of blocks allocated for us match our expectations. */
-    if ((pBlkMap->cBlocks / 8) + VCI_BYTE2BLOCK(sizeof(VciBlkMap)) == cBlkMap)
-    {
-        /* Setup the header */
-        memset(&BlkMap, 0, sizeof(VciBlkMap));
-
-        BlkMap.u32Magic         = RT_H2LE_U32(VCI_BLKMAP_MAGIC);
-        BlkMap.u32Version       = RT_H2LE_U32(VCI_BLKMAP_VERSION);
-        BlkMap.cBlocks          = RT_H2LE_U32(pBlkMap->cBlocks);
-        BlkMap.cBlocksFree      = RT_H2LE_U32(pBlkMap->cBlocksFree);
-        BlkMap.cBlocksAllocMeta = RT_H2LE_U32(pBlkMap->cBlocksAllocMeta);
-        BlkMap.cBlocksAllocData = RT_H2LE_U32(pBlkMap->cBlocksAllocData);
-
-        rc = vciFileWriteSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
-        if (RT_SUCCESS(rc))
-        {
-            uint8_t abBitmapBuffer[16*_1K];
-            unsigned iBit = 0;
-            PVCIBLKRANGEDESC pCur = pBlkMap->pRangesHead;
-
-            offBlkMap += VCI_BYTE2BLOCK(sizeof(VciBlkMap));
-
-            /* Write the descriptor ranges. */
-            while (pCur)
-            {
-                uint64_t cBlocks = pCur->cBlocks;
-
-                while (cBlocks)
-                {
-                    uint64_t cBlocksMax = RT_MIN(cBlocks, sizeof(abBitmapBuffer) * 8 - iBit);
-
-                    if (pCur->fFree)
-                        ASMBitClearRange(abBitmapBuffer, iBit, iBit + cBlocksMax);
-                    else
-                        ASMBitSetRange(abBitmapBuffer, iBit, iBit + cBlocksMax);
-
-                    iBit    += cBlocksMax;
-                    cBlocks -= cBlocksMax;
-
-                    if (iBit == sizeof(abBitmapBuffer) * 8)
-                    {
-                        /* Buffer is full, write to file and reset. */
-                        rc = vciFileWriteSync(pStorage, offBlkMap, abBitmapBuffer, sizeof(abBitmapBuffer));
-                        if (RT_FAILURE(rc))
-                            break;
-
-                        offBlkMap += VCI_BYTE2BLOCK(sizeof(abBitmapBuffer));
-                        iBit = 0;
-                    }
-                }
-
-                pCur = pCur->pNext;
-            }
-
-            Assert(iBit % 8 == 0);
-
-            if (RT_SUCCESS(rc) && iBit)
-                rc = vciFileWriteSync(pStorage, offBlkMap, abBitmapBuffer, iBit / 8);
-        }
-    }
-    else
-        rc = VERR_INTERNAL_ERROR; /* @todo Better error code. */
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Finds the range block describing the given block address.
- *
- * @returns Pointer to the block range descriptor or NULL if none could be found.
- * @param   pBlkMap         The block bitmap to search on.
- * @param   offBlockAddr    The block address to search for.
- */
-static PVCIBLKRANGEDESC vciBlkMapFindByBlock(PVCIBLKMAP pBlkMap, uint64_t offBlockAddr)
-{
-    PVCIBLKRANGEDESC pBlk = pBlkMap->pRangesHead;
-
-    while (   pBlk
-           && pBlk->offAddrStart < offBlockAddr)
-        pBlk = pBlk->pNext;
-
-    return pBlk;
-}
-
-/**
- * Allocates the given number of blocks in the bitmap and returns the start block address.
- *
- * @returns VBox status code.
- * @param   pBlkMap          The block bitmap to allocate the blocks from.
- * @param   cBlocks          How many blocks to allocate.
- * @param   poffBlockAddr    Where to store the start address of the allocated region.
- */
-static int vciBlkMapAllocate(PVCIBLKMAP pBlkMap, uint32_t cBlocks, uint64_t *poffBlockAddr)
-{
-    PVCIBLKRANGEDESC pBestFit = NULL;
-    PVCIBLKRANGEDESC pCur = NULL;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pBlkMap=%#p cBlocks=%u poffBlockAddr=%#p\n",
-                 pBlkMap, cBlocks, poffBlockAddr));
-
-    pCur = pBlkMap->pRangesHead;
-
-    while (pCur)
-    {
-        if (   pCur->fFree
-            && pCur->cBlocks >= cBlocks)
-        {
-            if (   !pBestFit
-                || pCur->cBlocks < pBestFit->cBlocks)
-            {
-                pBestFit = pCur;
-                /* Stop searching if the size is matching exactly. */
-                if (pBestFit->cBlocks == cBlocks)
-                    break;
-            }
-        }
-        pCur = pCur->pNext;
-    }
-
-    Assert(!pBestFit || pBestFit->fFree);
-
-    if (pBestFit)
-    {
-        pBestFit->fFree = false;
-
-        if (pBestFit->cBlocks > cBlocks)
-        {
-            /* Create a new free block. */
-            PVCIBLKRANGEDESC pFree = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
-
-            if (pFree)
-            {
-                pFree->fFree = true;
-                pFree->cBlocks = pBestFit->cBlocks - cBlocks;
-                pFree->offAddrStart = pBestFit->offAddrStart + cBlocks;
-
-                /* Link into the list. */
-                pFree->pNext = pBestFit->pNext;
-                pBestFit->pNext = pFree;
-                pFree->pPrev    = pBestFit;
-                if (!pFree->pNext)
-                    pBlkMap->pRangesTail = pFree;
-
-                *poffBlockAddr = pBestFit->offAddrStart;
-            }
-            else
-            {
-                rc = VERR_NO_MEMORY;
-                pBestFit->fFree = true;
-            }
-        }
-    }
-    else
-        rc = VERR_VCI_NO_BLOCKS_FREE;
-
-    LogFlowFunc(("returns rc=%Rrc offBlockAddr=%llu\n", rc, *poffBlockAddr));
-    return rc;
-}
-
-/**
- * Try to extend the space of an already allocated block.
- *
- * @returns VBox status code.
- * @param   pBlkMap          The block bitmap to allocate the blocks from.
- * @param   cBlocksNew       How many blocks the extended block should have.
- * @param   offBlockAddrOld  The start address of the block to reallocate.
- * @param   poffBlockAddr    Where to store the start address of the allocated region.
- */
-static int vciBlkMapRealloc(PVCIBLKMAP pBlkMap, uint32_t cBlocksNew, uint64_t offBlockAddrOld,
-                            uint64_t *poffBlockAddr)
-{
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pBlkMap=%#p cBlocksNew=%u offBlockAddrOld=%llu poffBlockAddr=%#p\n",
-                 pBlkMap, cBlocksNew, offBlockAddrOld, poffBlockAddr));
-
-    AssertMsgFailed(("Implement\n"));
-
-    LogFlowFunc(("returns rc=%Rrc offBlockAddr=%llu\n", rc, *poffBlockAddr));
-    return rc;
-}
-
-/**
- * Frees a range of blocks.
- *
- * @returns nothing.
- * @param   pBlkMap          The block bitmap.
- * @param   offBlockAddr     Address of the first block to free.
- * @param   cBlocks          How many blocks to free.
- */
-static void vciBlkMapFree(PVCIBLKMAP pBlkMap, uint64_t offBlockAddr, uint32_t cBlocks)
-{
-    PVCIBLKRANGEDESC pBlk;
-
-    LogFlowFunc(("pBlkMap=%#p offBlockAddr=%llu cBlocks=%u\n",
-                 pBlkMap, offBlockAddr, cBlocks));
-
-    while (cBlocks)
-    {
-        pBlk = vciBlkMapFindByBlock(pBlkMap, offBlockAddr);
-        AssertPtr(pBlk);
-
-        /* Easy case, the whole block is freed. */
-        if (   pBlk->offAddrStart == offBlockAddr
-            && pBlk->cBlocks <= cBlocks)
-        {
-            pBlk->fFree = true;
-            cBlocks      -= pBlk->cBlocks;
-            offBlockAddr += pBlk->cBlocks;
-
-            /* Check if it is possible to merge free blocks. */
-            if (   pBlk->pPrev
-                && pBlk->pPrev->fFree)
-            {
-                PVCIBLKRANGEDESC pBlkPrev = pBlk->pPrev;
-
-                Assert(pBlkPrev->offAddrStart + pBlkPrev->cBlocks == pBlk->offAddrStart);
-                pBlkPrev->cBlocks += pBlk->cBlocks;
-                pBlkPrev->pNext = pBlk->pNext;
-                if (pBlk->pNext)
-                    pBlk->pNext->pPrev = pBlkPrev;
-                else
-                    pBlkMap->pRangesTail = pBlkPrev;
-
-                RTMemFree(pBlk);
-                pBlk = pBlkPrev;
-            }
-
-            /* Now the one to the right. */
-            if (   pBlk->pNext
-                && pBlk->pNext->fFree)
-            {
-                PVCIBLKRANGEDESC pBlkNext = pBlk->pNext;
-
-                Assert(pBlk->offAddrStart + pBlk->cBlocks == pBlkNext->offAddrStart);
-                pBlk->cBlocks += pBlkNext->cBlocks;
-                pBlk->pNext = pBlkNext->pNext;
-                if (pBlkNext->pNext)
-                    pBlkNext->pNext->pPrev = pBlk;
-                else
-                    pBlkMap->pRangesTail = pBlk;
-
-                RTMemFree(pBlkNext);
-            }
-        }
-        else
-        {
-            /* The block is intersecting. */
-            AssertMsgFailed(("TODO\n"));
-        }
-    }
-
-    LogFlowFunc(("returns\n"));
-}
-
-/**
- * Converts a tree node from the image to the in memory structure.
- *
- * @returns Pointer to the in memory tree node.
- * @param   offBlockAddrNode    Block address of the node.
- * @param   pNodeImage          Pointer to the image representation of the node.
- */
-static PVCITREENODE vciTreeNodeImage2Host(uint64_t offBlockAddrNode, PVciTreeNode pNodeImage)
-{
-    PVCITREENODE pNode = NULL;
-
-    if (pNodeImage->u8Type == VCI_TREE_NODE_TYPE_LEAF)
-    {
-        PVCITREENODELEAF pLeaf = (PVCITREENODELEAF)RTMemAllocZ(sizeof(VCITREENODELEAF));
-
-        if (pLeaf)
-        {
-            PVciCacheExtent pExtent = (PVciCacheExtent)&pNodeImage->au8Data[0];
-
-            pLeaf->Core.u8Type = VCI_TREE_NODE_TYPE_LEAF;
-
-            for (unsigned idx = 0; idx < RT_ELEMENTS(pLeaf->aExtents); idx++)
-            {
-                pLeaf->aExtents[idx].u64BlockOffset = RT_LE2H_U64(pExtent->u64BlockOffset);
-                pLeaf->aExtents[idx].u32Blocks      = RT_LE2H_U32(pExtent->u32Blocks);
-                pLeaf->aExtents[idx].u64BlockAddr   = RT_LE2H_U64(pExtent->u64BlockAddr);
-                pExtent++;
-
-                if (   pLeaf->aExtents[idx].u32Blocks
-                    && pLeaf->aExtents[idx].u64BlockAddr)
-                    pLeaf->cUsedNodes++;
-            }
-
-            pNode = &pLeaf->Core;
-        }
-    }
-    else if (pNodeImage->u8Type == VCI_TREE_NODE_TYPE_INTERNAL)
-    {
-        PVCITREENODEINT pInt = (PVCITREENODEINT)RTMemAllocZ(sizeof(VCITREENODEINT));
-
-        if (pInt)
-        {
-            PVciTreeNodeInternal pIntImage = (PVciTreeNodeInternal)&pNodeImage->au8Data[0];
-
-            pInt->Core.u8Type = VCI_TREE_NODE_TYPE_INTERNAL;
-
-            for (unsigned idx = 0; idx < RT_ELEMENTS(pInt->aIntNodes); idx++)
-            {
-                pInt->aIntNodes[idx].u64BlockOffset              = RT_LE2H_U64(pIntImage->u64BlockOffset);
-                pInt->aIntNodes[idx].u32Blocks                   = RT_LE2H_U32(pIntImage->u32Blocks);
-                pInt->aIntNodes[idx].PtrChild.fInMemory          = false;
-                pInt->aIntNodes[idx].PtrChild.u.offAddrBlockNode = RT_LE2H_U64(pIntImage->u64ChildAddr);
-                pIntImage++;
-
-                if (   pInt->aIntNodes[idx].u32Blocks
-                    && pInt->aIntNodes[idx].PtrChild.u.offAddrBlockNode)
-                    pInt->cUsedNodes++;
-            }
-
-            pNode = &pInt->Core;
-        }
-    }
-    else
-        AssertMsgFailed(("Invalid node type %d\n", pNodeImage->u8Type));
-
-    if (pNode)
-        pNode->u64BlockAddr = offBlockAddrNode;
-
-    return pNode;
-}
-
-/**
- * Looks up the cache extent for the given virtual block address.
- *
- * @returns Pointer to the cache extent or NULL if none could be found.
- * @param   pCache         The cache image instance.
- * @param   offBlockOffset The block offset to search for.
- */
-static PVCICACHEEXTENT vciCacheExtentLookup(PVCICACHE pCache, uint64_t offBlockOffset)
-{
-    int rc = VINF_SUCCESS;
-    PVCICACHEEXTENT pExtent = NULL;
-    PVCITREENODE pNodeCur = pCache->pRoot;
-
-    while (   RT_SUCCESS(rc)
-           && pNodeCur
-           && pNodeCur->u8Type != VCI_TREE_NODE_TYPE_LEAF)
-    {
-        PVCITREENODEINT pNodeInt = (PVCITREENODEINT)pNodeCur;
-
-        Assert(pNodeCur->u8Type == VCI_TREE_NODE_TYPE_INTERNAL);
-
-        /* Search for the correct internal node. */
-        unsigned idxMin = 0;
-        unsigned idxMax = pNodeInt->cUsedNodes;
-        unsigned idxCur = pNodeInt->cUsedNodes / 2;
-
-        while (idxMin < idxMax)
-        {
-            PVCINODEINTERNAL pInt = &pNodeInt->aIntNodes[idxCur];
-
-            /* Determine the search direction. */
-            if (offBlockOffset < pInt->u64BlockOffset)
-            {
-                /* Search left from the current extent. */
-                idxMax = idxCur;
-            }
-            else if (offBlockOffset >= pInt->u64BlockOffset + pInt->u32Blocks)
-            {
-                /* Search right from the current extent. */
-                idxMin = idxCur;
-            }
-            else
-            {
-                /* The block lies in the node, stop searching. */
-                if (pInt->PtrChild.fInMemory)
-                    pNodeCur = pInt->PtrChild.u.pNode;
-                else
-                {
-                    PVCITREENODE pNodeNew;
-                    VciTreeNode NodeTree;
-
-                    /* Read from disk and add to the tree. */
-                    rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pInt->PtrChild.u.offAddrBlockNode),
-                                         &NodeTree, sizeof(NodeTree));
-                    AssertRC(rc);
-
-                    pNodeNew = vciTreeNodeImage2Host(pInt->PtrChild.u.offAddrBlockNode, &NodeTree);
-                    if (pNodeNew)
-                    {
-                        /* Link to the parent. */
-                        pInt->PtrChild.fInMemory = true;
-                        pInt->PtrChild.u.pNode = pNodeNew;
-                        pNodeNew->pParent = pNodeCur;
-                        pNodeCur = pNodeNew;
-                    }
-                    else
-                        rc = VERR_NO_MEMORY;
-                }
-                break;
-            }
-
-            idxCur = idxMin + (idxMax - idxMin) / 2;
-        }
-    }
-
-    if (   RT_SUCCESS(rc)
-        && pNodeCur)
-    {
-        PVCITREENODELEAF pLeaf = (PVCITREENODELEAF)pNodeCur;
-        Assert(pNodeCur->u8Type == VCI_TREE_NODE_TYPE_LEAF);
-
-        /* Search the range. */
-        unsigned idxMin = 0;
-        unsigned idxMax = pLeaf->cUsedNodes;
-        unsigned idxCur = pLeaf->cUsedNodes / 2;
-
-        while (idxMin < idxMax)
-        {
-            PVCICACHEEXTENT pExtentCur = &pLeaf->aExtents[idxCur];
-
-            /* Determine the search direction. */
-            if (offBlockOffset < pExtentCur->u64BlockOffset)
-            {
-                /* Search left from the current extent. */
-                idxMax = idxCur;
-            }
-            else if (offBlockOffset >= pExtentCur->u64BlockOffset + pExtentCur->u32Blocks)
-            {
-                /* Search right from the current extent. */
-                idxMin = idxCur;
-            }
-            else
-            {
-                /* We found the extent, stop searching. */
-                pExtent = pExtentCur;
-                break;
-            }
-
-            idxCur = idxMin + (idxMax - idxMin) / 2;
-        }
-    }
-
-    return pExtent;
-}
-
-/**
- * Internal: Open an image, constructing all necessary data structures.
- */
-static int vciOpenImage(PVCICACHE pCache, unsigned uOpenFlags)
-{
-    VciHdr Hdr;
-    uint64_t cbFile;
-    int rc;
-
-    pCache->uOpenFlags = uOpenFlags;
-
-    pCache->pInterfaceError = VDInterfaceGet(pCache->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pCache->pInterfaceError)
-        pCache->pInterfaceErrorCallbacks = VDGetInterfaceError(pCache->pInterfaceError);
-
-    /* Get I/O interface. */
-    pCache->pInterfaceIO = VDInterfaceGet(pCache->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pCache->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pCache->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pCache->pInterfaceIO);
-    AssertPtrReturn(pCache->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the image.
-     */
-    rc = vciFileOpen(pCache, pCache->pszFilename,
-                     VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                false /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        goto out;
-    }
-
-    rc = vciFileGetSize(pCache, &cbFile);
-    if (RT_FAILURE(rc) || cbFile < sizeof(VciHdr))
-    {
-        rc = VERR_VD_GEN_INVALID_HEADER;
-        goto out;
-    }
-
-    rc = vciFileReadSync(pCache, 0, &Hdr, sizeof(Hdr));
-    if (RT_FAILURE(rc))
-    {
-        rc = VERR_VD_GEN_INVALID_HEADER;
-        goto out;
-    }
-
-    Hdr.u32Signature = RT_LE2H_U32(Hdr.u32Signature);
-    Hdr.u32Version   = RT_LE2H_U32(Hdr.u32Version);
-    Hdr.cBlocksCache = RT_LE2H_U64(Hdr.cBlocksCache);
-    Hdr.u32CacheType = RT_LE2H_U32(Hdr.u32CacheType);
-    Hdr.offTreeRoot  = RT_LE2H_U64(Hdr.offTreeRoot);
-    Hdr.offBlkMap    = RT_LE2H_U64(Hdr.offBlkMap);
-    Hdr.cBlkMap      = RT_LE2H_U64(Hdr.cBlkMap);
-
-    if (   Hdr.u32Signature == VCI_HDR_SIGNATURE
-        && Hdr.u32Version == VCI_HDR_VERSION)
-    {
-        pCache->offTreeRoot   = Hdr.offTreeRoot;
-        pCache->offBlksBitmap = Hdr.offBlkMap;
-
-        /* Load the block map. */
-        rc = vciBlkMapLoad(pCache, VCI_BLOCK2BYTE(pCache->offBlksBitmap), Hdr.cBlkMap, &pCache->pBlkMap);
-        if (RT_SUCCESS(rc))
-        {
-            /* Load the first tree node. */
-            VciTreeNode RootNode;
-
-            rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pCache->offTreeRoot), &RootNode, sizeof(VciTreeNode));
-            if (RT_SUCCESS(rc))
-            {
-                pCache->pRoot = vciTreeNodeImage2Host(pCache->offTreeRoot, &RootNode);
-                if (!pCache->pRoot)
-                    rc = VERR_NO_MEMORY;
-            }
-        }
-    }
-    else
-        rc = VERR_VD_GEN_INVALID_HEADER;
-
-out:
-    if (RT_FAILURE(rc))
-        vciFreeImage(pCache, false);
-    return rc;
-}
-
-/**
- * Internal: Create a vci image.
- */
-static int vciCreateImage(PVCICACHE pCache, uint64_t cbSize,
-                          unsigned uImageFlags, const char *pszComment,
-                          unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
-                          void *pvUser, unsigned uPercentStart,
-                          unsigned uPercentSpan)
-{
-    VciHdr Hdr;
-    VciTreeNode NodeRoot;
-    int rc;
-    uint64_t cBlocks = cbSize / VCI_BLOCK_SIZE; /* Size of the cache in blocks. */
-
-    if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
-    {
-        rc = vciError(pCache, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("VCI: cannot create diff image '%s'"), pCache->pszFilename);
-        return rc;
-    }
-
-    pCache->uImageFlags = uImageFlags;
-
-    pCache->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
-
-    pCache->pInterfaceError = VDInterfaceGet(pCache->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pCache->pInterfaceError)
-        pCache->pInterfaceErrorCallbacks = VDGetInterfaceError(pCache->pInterfaceError);
-
-    pCache->pInterfaceIO = VDInterfaceGet(pCache->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pCache->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pCache->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pCache->pInterfaceIO);
-    AssertPtrReturn(pCache->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    do
-    {
-        /* Create image file. */
-        rc = vciFileOpen(pCache, pCache->pszFilename,
-                         VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                                    true /* fCreate */));
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot create image '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        /* Allocate block bitmap. */
-        uint32_t cBlkMap = 0;
-        rc = vciBlkMapCreate(cBlocks, &pCache->pBlkMap, &cBlkMap);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot create block bitmap '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        /*
-         * Allocate space for the header in the block bitmap.
-         * Because the block map is empty the header has to start at block 0
-         */
-        uint64_t offHdr = 0;
-        rc = vciBlkMapAllocate(pCache->pBlkMap, VCI_BYTE2BLOCK(sizeof(VciHdr)), &offHdr);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for header in block bitmap '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        Assert(offHdr == 0);
-
-        /*
-         * Allocate space for the block map itself.
-         */
-        uint64_t offBlkMap = 0;
-        rc = vciBlkMapAllocate(pCache->pBlkMap, cBlkMap, &offBlkMap);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        /*
-         * Allocate space for the tree root node.
-         */
-        uint64_t offTreeRoot = 0;
-        rc = vciBlkMapAllocate(pCache->pBlkMap, VCI_BYTE2BLOCK(sizeof(VciTreeNode)), &offTreeRoot);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        /*
-         * Allocate the in memory root node.
-         */
-        pCache->pRoot = (PVCITREENODE)RTMemAllocZ(sizeof(VCITREENODELEAF));
-        if (!pCache->pRoot)
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate B+-Tree root pointer '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        pCache->pRoot->u8Type = VCI_TREE_NODE_TYPE_LEAF;
-        /* Rest remains 0 as the tree is still empty. */
-
-        /*
-         * Now that we are here we have all the basic structures and know where to place them in the image.
-         * It's time to write it now.
-         */
-
-        /* Setup the header. */
-        memset(&Hdr, 0, sizeof(VciHdr));
-        Hdr.u32Signature     = RT_H2LE_U32(VCI_HDR_SIGNATURE);
-        Hdr.u32Version       = RT_H2LE_U32(VCI_HDR_VERSION);
-        Hdr.cBlocksCache     = RT_H2LE_U64(cBlocks);
-        Hdr.fUncleanShutdown = VCI_HDR_UNCLEAN_SHUTDOWN;
-        Hdr.u32CacheType     = uImageFlags & VD_IMAGE_FLAGS_FIXED
-                               ? RT_H2LE_U32(VCI_HDR_CACHE_TYPE_FIXED)
-                               : RT_H2LE_U32(VCI_HDR_CACHE_TYPE_DYNAMIC);
-        Hdr.offTreeRoot      = RT_H2LE_U64(offTreeRoot);
-        Hdr.offBlkMap        = RT_H2LE_U64(offBlkMap);
-        Hdr.cBlkMap          = RT_H2LE_U64(cBlkMap);
-
-        rc = vciFileWriteSync(pCache, offHdr, &Hdr, VCI_BYTE2BLOCK(sizeof(VciHdr)));
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write header '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        rc = vciBlkMapSave(pCache->pBlkMap, pCache, offBlkMap, cBlkMap);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write block map '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        /* Setup the root tree. */
-        memset(&NodeRoot, 0, sizeof(VciTreeNode));
-        NodeRoot.u8Type = RT_H2LE_U32(VCI_TREE_NODE_TYPE_LEAF);
-
-        rc = vciFileWriteSync(pCache, offTreeRoot, &NodeRoot, VCI_BYTE2BLOCK(sizeof(VciTreeNode)));
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write root node '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        rc = vciFlushImage(pCache);
-        if (RT_FAILURE(rc))
-        {
-            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot flush '%s'"), pCache->pszFilename);
-            break;
-        }
-
-        pCache->cbSize = cbSize;
-
-    } while (0);
-
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan);
-
-    if (RT_FAILURE(rc))
-        vciFreeImage(pCache, rc != VERR_ALREADY_EXISTS);
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnProbe */
-static int vciProbe(const char *pszFilename, PVDINTERFACE pVDIfsCache,
-                    PVDINTERFACE pVDIfsImage)
-{
-    VciHdr Hdr;
-    PVDIOSTORAGE pStorage;
-    uint64_t cbFile;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
-
-    /* Get I/O interface. */
-    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
-    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
-    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
-                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
-                                                                   false /* fCreate */),
-                                        &pStorage);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage, &cbFile);
-    if (RT_FAILURE(rc) || cbFile < sizeof(VciHdr))
-    {
-        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-        rc = VERR_VD_GEN_INVALID_HEADER;
-        goto out;
-    }
-
-    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage, 0, &Hdr, sizeof(Hdr), NULL);
-    if (RT_FAILURE(rc))
-    {
-        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-        rc = VERR_VD_GEN_INVALID_HEADER;
-        goto out;
-    }
-
-    Hdr.u32Signature = RT_LE2H_U32(Hdr.u32Signature);
-    Hdr.u32Version   = RT_LE2H_U32(Hdr.u32Version);
-    Hdr.cBlocksCache = RT_LE2H_U64(Hdr.cBlocksCache);
-    Hdr.u32CacheType = RT_LE2H_U32(Hdr.u32CacheType);
-    Hdr.offTreeRoot  = RT_LE2H_U64(Hdr.offTreeRoot);
-    Hdr.offBlkMap    = RT_LE2H_U64(Hdr.offBlkMap);
-    Hdr.cBlkMap      = RT_LE2H_U64(Hdr.cBlkMap);
-
-    if (   Hdr.u32Signature == VCI_HDR_SIGNATURE
-        && Hdr.u32Version == VCI_HDR_VERSION)
-        rc = VINF_SUCCESS;
-    else
-        rc = VERR_VD_GEN_INVALID_HEADER;
-
-    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnOpen */
-static int vciOpen(const char *pszFilename, unsigned uOpenFlags,
-                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                   void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PVCICACHE pCache;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-
-    pCache = (PVCICACHE)RTMemAllocZ(sizeof(VCICACHE));
-    if (!pCache)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pCache->pszFilename = pszFilename;
-    pCache->pStorage = NULL;
-    pCache->pVDIfsDisk = pVDIfsDisk;
-    pCache->pVDIfsImage = pVDIfsImage;
-
-    rc = vciOpenImage(pCache, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pCache;
-    else
-        RTMemFree(pCache);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnCreate */
-static int vciCreate(const char *pszFilename, uint64_t cbSize,
-                     unsigned uImageFlags, const char *pszComment,
-                     PCRTUUID pUuid, unsigned uOpenFlags,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p",
-                 pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc;
-    PVCICACHE pCache;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pCache = (PVCICACHE)RTMemAllocZ(sizeof(VCICACHE));
-    if (!pCache)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pCache->pszFilename = pszFilename;
-    pCache->pStorage = NULL;
-    pCache->pVDIfsDisk = pVDIfsDisk;
-    pCache->pVDIfsImage = pVDIfsImage;
-
-    rc = vciCreateImage(pCache, cbSize, uImageFlags, pszComment, uOpenFlags,
-                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
-    if (RT_SUCCESS(rc))
-    {
-        /* So far the image is opened in read/write mode. Make sure the
-         * image is opened in read-only mode if the caller requested that. */
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            vciFreeImage(pCache, false);
-            rc = vciOpenImage(pCache, uOpenFlags);
-            if (RT_FAILURE(rc))
-            {
-                RTMemFree(pCache);
-                goto out;
-            }
-        }
-        *ppBackendData = pCache;
-    }
-    else
-        RTMemFree(pCache);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnClose */
-static int vciClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    rc = vciFreeImage(pCache, fDelete);
-    RTMemFree(pCache);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnRead */
-static int vciRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                   size_t cbToRead, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc = VINF_SUCCESS;
-    PVCICACHEEXTENT pExtent;
-
-    AssertPtr(pCache);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToRead % 512 == 0);
-
-
-    pExtent = vciCacheExtentLookup(pCache, VCI_BYTE2BLOCK(uOffset));
-    if (pExtent)
-    {
-        uint64_t offRead = uOffset - VCI_BLOCK2BYTE(pExtent->u64BlockOffset);
-        cbToRead = RT_MIN(cbToRead, VCI_BLOCK2BYTE(pExtent->u32Blocks) - offRead);
-
-        rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pExtent->u64BlockAddr) + offRead,
-                             pvBuf, cbToRead);
-    }
-    else
-    {
-        /** @todo Best fit to check whether we have cached data later and set
-         * pcbActuallyRead accordingly. */
-        rc = VERR_VD_BLOCK_FREE;
-    }
-
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbToRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnWrite */
-static int vciWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                    size_t cbToWrite, size_t *pcbWriteProcess)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
-                 pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pCache);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnFlush */
-static int vciFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    rc = vciFlushImage(pCache);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetVersion */
-static unsigned vciGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        return 1;
-    else
-        return 0;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetSize */
-static uint64_t vciGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pCache);
-
-    if (pCache && pCache->pStorage)
-        cb = pCache->cbSize;
-
-    LogFlowFunc(("returns %llu\n", cb));
-    return cb;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetFileSize */
-static uint64_t vciGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-    {
-        uint64_t cbFile;
-        if (pCache->pStorage)
-        {
-            int rc = vciFileGetSize(pCache, &cbFile);
-            if (RT_SUCCESS(rc))
-                cb = cbFile;
-        }
-    }
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetImageFlags */
-static unsigned vciGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        uImageFlags = pCache->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetOpenFlags */
-static unsigned vciGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        uOpenFlags = pCache->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnSetOpenFlags */
-static int vciSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. Just readonly and
-     * info flags are supported. */
-    if (!pCache || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    rc = vciFreeImage(pCache, false);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vciOpenImage(pCache, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetComment */
-static int vciGetComment(void *pBackendData, char *pszComment,
-                          size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnSetComment */
-static int vciSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-    {
-        if (pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetUuid */
-static int vciGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnSetUuid */
-static int vciSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    AssertPtr(pCache);
-
-    if (pCache)
-    {
-        if (!(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnGetModificationUuid */
-static int vciGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnSetModificationUuid */
-static int vciSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-    int rc;
-
-    AssertPtr(pCache);
-
-    if (pCache)
-    {
-        if (!(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnDump */
-static void vciDump(void *pBackendData)
-{
-    NOREF(pBackendData);
-}
-
-/** @copydoc VDCACHEBACKEND::pfnAsyncRead */
-static int vciAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
-                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    int rc = VERR_NOT_SUPPORTED;
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnAsyncWrite */
-static int vciAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                         PVDIOCTX pIoCtx, size_t *pcbWriteProcess)
-{
-    int rc = VERR_NOT_SUPPORTED;
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-
-    return rc;
-}
-
-/** @copydoc VDCACHEBACKEND::pfnAsyncFlush */
-static int vciAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    int rc = VERR_NOT_SUPPORTED;
-    PVCICACHE pCache = (PVCICACHE)pBackendData;
-
-    return rc;
-}
-
-
-VDCACHEBACKEND g_VciCacheBackend =
-{
-    /* pszBackendName */
-    "vci",
-    /* cbSize */
-    sizeof(VDCACHEBACKEND),
-    /* uBackendCaps */
-    VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC | VD_CAP_FILE | VD_CAP_VFS,
-    /* papszFileExtensions */
-    s_apszVciFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnProbe */
-    vciProbe,
-    /* pfnOpen */
-    vciOpen,
-    /* pfnCreate */
-    vciCreate,
-    /* pfnClose */
-    vciClose,
-    /* pfnRead */
-    vciRead,
-    /* pfnWrite */
-    vciWrite,
-    /* pfnFlush */
-    vciFlush,
-    /* pfnGetVersion */
-    vciGetVersion,
-    /* pfnGetSize */
-    vciGetSize,
-    /* pfnGetFileSize */
-    vciGetFileSize,
-    /* pfnGetImageFlags */
-    vciGetImageFlags,
-    /* pfnGetOpenFlags */
-    vciGetOpenFlags,
-    /* pfnSetOpenFlags */
-    vciSetOpenFlags,
-    /* pfnGetComment */
-    vciGetComment,
-    /* pfnSetComment */
-    vciSetComment,
-    /* pfnGetUuid */
-    vciGetUuid,
-    /* pfnSetUuid */
-    vciSetUuid,
-    /* pfnGetModificationUuid */
-    vciGetModificationUuid,
-    /* pfnSetModificationUuid */
-    vciSetModificationUuid,
-    /* pfnDump */
-    vciDump,
-    /* pfnAsyncRead */
-    vciAsyncRead,
-    /* pfnAsyncWrite */
-    vciAsyncWrite,
-    /* pfnAsyncFlush */
-    vciAsyncFlush,
-    /* pfnComposeLocation */
-    NULL,
-    /* pfnComposeName */
-    NULL
-};
-
Index: unk/src/VBox/Devices/Storage/VDICore.h
===================================================================
--- /trunk/src/VBox/Devices/Storage/VDICore.h	(revision 33566)
+++ 	(revision )
@@ -1,694 +1,0 @@
-/* $Id$ */
-/** @file
- * Virtual Disk Image (VDI), Core Code Header (internal).
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-#ifndef __VDICore_h__
-
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#include <VBox/VBoxHDD.h>
-#ifndef VBOX_VDICORE_VD
-#include <VBox/pdm.h>
-#endif /* !VBOX_VDICORE_VD */
-#include <VBox/mm.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/alloc.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include <iprt/string.h>
-#include <iprt/asm.h>
-
-
-/*******************************************************************************
-*   Constants And Macros, Structures and Typedefs                              *
-*******************************************************************************/
-
-/** Image info, not handled anyhow.
- *  Must be less than 64 bytes in length, including the trailing 0.
- */
-#define VDI_IMAGE_FILE_INFO   "<<< Oracle VM VirtualBox Disk Image >>>\n"
-
-/** The Sector size.
- * Currently we support only 512 bytes sectors.
- */
-#define VDI_GEOMETRY_SECTOR_SIZE    (512)
-/**  512 = 2^^9 */
-#define VDI_GEOMETRY_SECTOR_SHIFT   (9)
-
-/**
- * Harddisk geometry.
- */
-#pragma pack(1)
-typedef struct VDIDISKGEOMETRY
-{
-    /** Cylinders. */
-    uint32_t    cCylinders;
-    /** Heads. */
-    uint32_t    cHeads;
-    /** Sectors per track. */
-    uint32_t    cSectors;
-    /** Sector size. (bytes per sector) */
-    uint32_t    cbSector;
-} VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
-#pragma pack()
-
-/** Image signature. */
-#define VDI_IMAGE_SIGNATURE   (0xbeda107f)
-
-/**
- * Pre-Header to be stored in image file - used for version control.
- */
-#pragma pack(1)
-typedef struct VDIPREHEADER
-{
-    /** Just text info about image type, for eyes only. */
-    char            szFileInfo[64];
-    /** The image signature (VDI_IMAGE_SIGNATURE). */
-    uint32_t        u32Signature;
-    /** The image version (VDI_IMAGE_VERSION). */
-    uint32_t        u32Version;
-} VDIPREHEADER, *PVDIPREHEADER;
-#pragma pack()
-
-/**
- * Size of szComment field of HDD image header.
- */
-#define VDI_IMAGE_COMMENT_SIZE    256
-
-/**
- * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
- * Prepended by VDIPREHEADER.
- */
-#pragma pack(1)
-typedef struct VDIHEADER0
-{
-    /** The image type (VDI_IMAGE_TYPE_*). */
-    uint32_t        u32Type;
-    /** Image flags (VDI_IMAGE_FLAGS_*). */
-    uint32_t        fFlags;
-    /** Image comment. (UTF-8) */
-    char            szComment[VDI_IMAGE_COMMENT_SIZE];
-    /** Legacy image geometry (previous code stored PCHS there). */
-    VDIDISKGEOMETRY LegacyGeometry;
-    /** Size of disk (in bytes). */
-    uint64_t        cbDisk;
-    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
-    uint32_t        cbBlock;
-    /** Number of blocks. */
-    uint32_t        cBlocks;
-    /** Number of allocated blocks. */
-    uint32_t        cBlocksAllocated;
-    /** UUID of image. */
-    RTUUID          uuidCreate;
-    /** UUID of image's last modification. */
-    RTUUID          uuidModify;
-    /** Only for secondary images - UUID of primary image. */
-    RTUUID          uuidLinkage;
-} VDIHEADER0, *PVDIHEADER0;
-#pragma pack()
-
-/**
- * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
- * VDI_IMAGE_VERSION_MINOR = 1. Prepended by VDIPREHEADER.
- */
-#pragma pack(1)
-typedef struct VDIHEADER1
-{
-    /** Size of this structure in bytes. */
-    uint32_t        cbHeader;
-    /** The image type (VDI_IMAGE_TYPE_*). */
-    uint32_t        u32Type;
-    /** Image flags (VDI_IMAGE_FLAGS_*). */
-    uint32_t        fFlags;
-    /** Image comment. (UTF-8) */
-    char            szComment[VDI_IMAGE_COMMENT_SIZE];
-    /** Offset of Blocks array from the beginning of image file.
-     * Should be sector-aligned for HDD access optimization. */
-    uint32_t        offBlocks;
-    /** Offset of image data from the beginning of image file.
-     * Should be sector-aligned for HDD access optimization. */
-    uint32_t        offData;
-    /** Legacy image geometry (previous code stored PCHS there). */
-    VDIDISKGEOMETRY LegacyGeometry;
-    /** Was BIOS HDD translation mode, now unused. */
-    uint32_t        u32Dummy;
-    /** Size of disk (in bytes). */
-    uint64_t        cbDisk;
-    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
-    uint32_t        cbBlock;
-    /** Size of additional service information of every data block.
-     * Prepended before block data. May be 0.
-     * Should be a power of 2 and sector-aligned for optimization reasons. */
-    uint32_t        cbBlockExtra;
-    /** Number of blocks. */
-    uint32_t        cBlocks;
-    /** Number of allocated blocks. */
-    uint32_t        cBlocksAllocated;
-    /** UUID of image. */
-    RTUUID          uuidCreate;
-    /** UUID of image's last modification. */
-    RTUUID          uuidModify;
-    /** Only for secondary images - UUID of previous image. */
-    RTUUID          uuidLinkage;
-    /** Only for secondary images - UUID of previous image's last modification. */
-    RTUUID          uuidParentModify;
-} VDIHEADER1, *PVDIHEADER1;
-#pragma pack()
-
-/**
- * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
- * VDI_IMAGE_VERSION_MINOR = 1, the slightly changed variant necessary as the
- * old released code doesn't support changing the minor version at all.
- */
-#pragma pack(1)
-typedef struct VDIHEADER1PLUS
-{
-    /** Size of this structure in bytes. */
-    uint32_t        cbHeader;
-    /** The image type (VDI_IMAGE_TYPE_*). */
-    uint32_t        u32Type;
-    /** Image flags (VDI_IMAGE_FLAGS_*). */
-    uint32_t        fFlags;
-    /** Image comment. (UTF-8) */
-    char            szComment[VDI_IMAGE_COMMENT_SIZE];
-    /** Offset of blocks array from the beginning of image file.
-     * Should be sector-aligned for HDD access optimization. */
-    uint32_t        offBlocks;
-    /** Offset of image data from the beginning of image file.
-     * Should be sector-aligned for HDD access optimization. */
-    uint32_t        offData;
-    /** Legacy image geometry (previous code stored PCHS there). */
-    VDIDISKGEOMETRY LegacyGeometry;
-    /** Was BIOS HDD translation mode, now unused. */
-    uint32_t        u32Dummy;
-    /** Size of disk (in bytes). */
-    uint64_t        cbDisk;
-    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
-    uint32_t        cbBlock;
-    /** Size of additional service information of every data block.
-     * Prepended before block data. May be 0.
-     * Should be a power of 2 and sector-aligned for optimization reasons. */
-    uint32_t        cbBlockExtra;
-    /** Number of blocks. */
-    uint32_t        cBlocks;
-    /** Number of allocated blocks. */
-    uint32_t        cBlocksAllocated;
-    /** UUID of image. */
-    RTUUID          uuidCreate;
-    /** UUID of image's last modification. */
-    RTUUID          uuidModify;
-    /** Only for secondary images - UUID of previous image. */
-    RTUUID          uuidLinkage;
-    /** Only for secondary images - UUID of previous image's last modification. */
-    RTUUID          uuidParentModify;
-    /** LCHS image geometry (new field in VDI1.2 version. */
-    VDIDISKGEOMETRY LCHSGeometry;
-} VDIHEADER1PLUS, *PVDIHEADER1PLUS;
-#pragma pack()
-
-/**
- * Header structure for all versions.
- */
-typedef struct VDIHEADER
-{
-    unsigned        uVersion;
-    union
-    {
-        VDIHEADER0    v0;
-        VDIHEADER1    v1;
-        VDIHEADER1PLUS v1plus;
-    } u;
-} VDIHEADER, *PVDIHEADER;
-
-/**
- * File alignment boundary for both the block array and data area. Should be
- * at least the size of a physical sector on disk for performance reasons.
- * With the growing market share of disks with 4K sectors this needs to be
- * bumped, and maybe again later. */
-#define VDI_DATA_ALIGN _4K
-
-/** Block 'pointer'. */
-typedef uint32_t    VDIIMAGEBLOCKPOINTER;
-/** Pointer to a block 'pointer'. */
-typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
-
-/**
- * Block marked as free is not allocated in image file, read from this
- * block may returns any random data.
- */
-#define VDI_IMAGE_BLOCK_FREE   ((VDIIMAGEBLOCKPOINTER)~0)
-
-/**
- * Block marked as zero is not allocated in image file, read from this
- * block returns zeroes.
- */
-#define VDI_IMAGE_BLOCK_ZERO   ((VDIIMAGEBLOCKPOINTER)~1)
-
-/**
- * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
- * allocated in image file.
- */
-#define VDI_IMAGE_BLOCK_UNALLOCATED   (VDI_IMAGE_BLOCK_ZERO)
-#define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp)   (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
-
-#define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
-#define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
-
-#ifdef VBOX_VDICORE_VD
-/** @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;
-/** @} */
-#endif /* VBOX_VDICORE_VD */
-
-/*******************************************************************************
-*   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;
-}
-
-#ifdef VBOX_VDICORE_VD
-DECLINLINE(unsigned) getImageFlags(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0:
-            /* VDI image flag conversion to VD image flags. */
-            return ph->u.v0.fFlags << 8;
-        case 1:
-            /* VDI image flag conversion to VD image flags. */
-            return ph->u.v1.fFlags << 8;
-    }
-    AssertFailed();
-    return 0;
-}
-#else /* !VBOX_VDICORE_VD */
-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;
-}
-#endif /* !VBOX_VDICORE_VD */
-
-DECLINLINE(char *) getImageComment(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return &ph->u.v0.szComment[0];
-        case 1: return &ph->u.v1.szComment[0];
-    }
-    AssertFailed();
-    return NULL;
-}
-
-DECLINLINE(unsigned) getImageBlocksOffset(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
-        case 1: return ph->u.v1.offBlocks;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(uint32_t) getImageDataOffset(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
-                       (ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
-        case 1: return ph->u.v1.offData;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(void) setImageDataOffset(PVDIHEADER ph, uint32_t offData)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return;
-        case 1: ph->u.v1.offData = offData; return;
-    }
-    AssertFailed();
-}
-
-DECLINLINE(PVDIDISKGEOMETRY) getImageLCHSGeometry(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return NULL;
-        case 1:
-            switch (GET_MINOR_HEADER_VERSION(ph))
-            {
-                case 1:
-                    if (ph->u.v1.cbHeader < sizeof(ph->u.v1plus))
-                        return NULL;
-                    else
-                        return &ph->u.v1plus.LCHSGeometry;
-            }
-    }
-    AssertFailed();
-    return NULL;
-}
-
-DECLINLINE(uint64_t) getImageDiskSize(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return ph->u.v0.cbDisk;
-        case 1: return ph->u.v1.cbDisk;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(void) setImageDiskSize(PVDIHEADER ph, uint64_t cbDisk)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: ph->u.v0.cbDisk = cbDisk; return;
-        case 1: ph->u.v1.cbDisk = cbDisk; return;
-    }
-    AssertFailed();
-}
-
-DECLINLINE(unsigned) getImageBlockSize(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return ph->u.v0.cbBlock;
-        case 1: return ph->u.v1.cbBlock;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(unsigned) getImageExtraBlockSize(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return 0;
-        case 1: return ph->u.v1.cbBlockExtra;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(unsigned) getImageBlocks(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return ph->u.v0.cBlocks;
-        case 1: return ph->u.v1.cBlocks;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(void) setImageBlocks(PVDIHEADER ph, unsigned cBlocks)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: ph->u.v0.cBlocks = cBlocks; return;
-        case 1: ph->u.v1.cBlocks = cBlocks; return;
-    }
-    AssertFailed();
-}
-
-
-DECLINLINE(unsigned) getImageBlocksAllocated(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return ph->u.v0.cBlocksAllocated;
-        case 1: return ph->u.v1.cBlocksAllocated;
-    }
-    AssertFailed();
-    return 0;
-}
-
-DECLINLINE(void) setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: ph->u.v0.cBlocksAllocated = cBlocks; return;
-        case 1: ph->u.v1.cBlocksAllocated = cBlocks; return;
-    }
-    AssertFailed();
-}
-
-DECLINLINE(PRTUUID) getImageCreationUUID(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return &ph->u.v0.uuidCreate;
-        case 1: return &ph->u.v1.uuidCreate;
-    }
-    AssertFailed();
-    return NULL;
-}
-
-DECLINLINE(PRTUUID) getImageModificationUUID(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return &ph->u.v0.uuidModify;
-        case 1: return &ph->u.v1.uuidModify;
-    }
-    AssertFailed();
-    return NULL;
-}
-
-DECLINLINE(PRTUUID) getImageParentUUID(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 0: return &ph->u.v0.uuidLinkage;
-        case 1: return &ph->u.v1.uuidLinkage;
-    }
-    AssertFailed();
-    return NULL;
-}
-
-DECLINLINE(PRTUUID) getImageParentModificationUUID(PVDIHEADER ph)
-{
-    switch (GET_MAJOR_HEADER_VERSION(ph))
-    {
-        case 1: return &ph->u.v1.uuidParentModify;
-    }
-    AssertFailed();
-    return NULL;
-}
-
-#ifndef VBOX_VDICORE_VD
-/**
- * Default image block size, may be changed by setBlockSize/getBlockSize.
- *
- * Note: for speed reasons block size should be a power of 2 !
- */
-#define VDI_IMAGE_DEFAULT_BLOCK_SIZE            _1M
-#endif /* !VBOX_VDICORE_VD */
-
-#ifndef VBOX_VDICORE_VD
-/**
- * fModified bit flags.
- */
-#define VDI_IMAGE_MODIFIED_FLAG                 RT_BIT(0)
-#define VDI_IMAGE_MODIFIED_FIRST                RT_BIT(1)
-#define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE  RT_BIT(2)
-#endif /* !VBOX_VDICORE_VD */
-
-/**
- * Image structure
- */
-typedef struct VDIIMAGEDESC
-{
-#ifndef VBOX_VDICORE_VD
-    /** Link to parent image descriptor, if any. */
-    struct VDIIMAGEDESC    *pPrev;
-    /** Link to child image descriptor, if any. */
-    struct VDIIMAGEDESC    *pNext;
-#endif /* !VBOX_VDICORE_VD */
-    /** Opaque storage handle. */
-    PVDIOSTORAGE            pStorage;
-#ifndef VBOX_VDICORE_VD
-    /** True if the image is operating in readonly mode. */
-    bool                    fReadOnly;
-    /** Image open flags, VDI_OPEN_FLAGS_*. */
-    unsigned                fOpen;
-#else /* VBOX_VDICORE_VD */
-    /** Image open flags, VD_OPEN_FLAGS_*. */
-    unsigned                uOpenFlags;
-#endif /* VBOX_VDICORE_VD */
-    /** Image pre-header. */
-    VDIPREHEADER            PreHeader;
-    /** Image header. */
-    VDIHEADER               Header;
-    /** Pointer to a block array. */
-    PVDIIMAGEBLOCKPOINTER   paBlocks;
-#ifndef VBOX_VDICORE_VD
-    /** fFlags copy from image header, for speed optimization. */
-    unsigned                fFlags;
-#else /* VBOX_VDICORE_VD */
-    /** fFlags copy from image header, for speed optimization. */
-    unsigned                uImageFlags;
-#endif /* VBOX_VDICORE_VD */
-    /** 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;
-#ifndef VBOX_VDICORE_VD
-    /** Block shift value for converting block index into offset in image. */
-    unsigned                uShiftIndex2Offset;
-#endif /* !VBOX_VDICORE_VD */
-    /** Offset of data from the beginning of block. */
-    unsigned                offStartBlockData;
-#ifndef VBOX_VDICORE_VD
-    /** 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];
-#else /* VBOX_VDICORE_VD */
-    /** Total size of image block (including the extra data). */
-    unsigned                cbTotalBlockData;
-    /** Container filename. (UTF-8) */
-    const char             *pszFilename;
-    /** Physical geometry of this image (never actually stored). */
-    VDGEOMETRY              PCHSGeometry;
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE            pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE            pVDIfsImage;
-    /** Error interface. */
-    PVDINTERFACE            pInterfaceError;
-    /** Error interface callback table. */
-    PVDINTERFACEERROR       pInterfaceErrorCallbacks;
-    /** I/O interface. */
-    PVDINTERFACE            pInterfaceIO;
-    /** I/O interface callbacks. */
-    PVDINTERFACEIOINT       pInterfaceIOCallbacks;
-#endif /* VBOX_VDICORE_VD */
-} VDIIMAGEDESC, *PVDIIMAGEDESC;
-
-#ifndef VBOX_VDICORE_VD
-/**
- * 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)
-#endif /* !VBOX_VDICORE_VD */
-
-/** 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;
-
-#ifndef VBOX_VDICORE_VD
-    /** The media interface. */
-    PDMIMEDIA       IMedia;
-    /** Pointer to the driver instance. */
-    PPDMDRVINS      pDrvIns;
-#endif /* !VBOX_VDICORE_VD */
-};
-
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-RT_C_DECLS_BEGIN
-
-#ifndef VBOX_VDICORE_VD
-VBOXDDU_DECL(void) vdiInitVDIDisk(PVDIDISK pDisk);
-VBOXDDU_DECL(void) VDIFlushImage(PVDIIMAGEDESC pImage);
-VBOXDDU_DECL(int)  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
-#endif /* !VBOX_VDICORE_VD */
-
-RT_C_DECLS_END
-
-#endif
Index: unk/src/VBox/Devices/Storage/VDIHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VDIHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,2751 +1,0 @@
-/** @file
- * Virtual Disk Image (VDI), Core Code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_VDI
-#include <VBox/VBoxHDD-Plugin.h>
-#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
-#include "VDICore.h"
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/alloc.h>
-#include <iprt/assert.h>
-#include <iprt/uuid.h>
-#include <iprt/string.h>
-#include <iprt/asm.h>
-
-#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aVdiFileExtensions[] =
-{
-    {"vdi", VDTYPE_HDD},
-    {NULL, VDTYPE_INVALID}
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-static unsigned getPowerOfTwo(unsigned uNumber);
-static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
-static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
-static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
-                          const char *pszComment, uint64_t cbDisk,
-                          uint32_t cbBlock, uint32_t cbBlockExtra);
-static int  vdiValidateHeader(PVDIHEADER pHeader);
-static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
-static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
-static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
-static int  vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
-static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx);
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) vdiError(PVDIIMAGEDESC pImage, int rc, RT_SRC_POS_DECL,
-                         const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser,
-                                                   rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) vdiMessage(PVDIIMAGEDESC pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) vdiFileOpen(PVDIIMAGEDESC pImage, const char *pszFilename,
-                            uint32_t fOpen)
-{
-    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                  pszFilename, fOpen,
-                                                  &pImage->pStorage);
-}
-
-DECLINLINE(int) vdiFileClose(PVDIIMAGEDESC pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                   pImage->pStorage);
-}
-
-DECLINLINE(int) vdiFileDelete(PVDIIMAGEDESC pImage, const char *pszFilename)
-{
-    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                    pszFilename);
-}
-
-DECLINLINE(int) vdiFileMove(PVDIIMAGEDESC pImage, const char *pszSrc,
-                            const char *pszDst, unsigned fMove)
-{
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) vdiFileGetFreeSpace(PVDIIMAGEDESC pImage, const char *pszFilename,
-                                    int64_t *pcbFree)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
-                                                          pszFilename, pcbFree);
-}
-
-DECLINLINE(int) vdiFileGetSize(PVDIIMAGEDESC pImage, uint64_t *pcbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, pcbSize);
-}
-
-DECLINLINE(int) vdiFileSetSize(PVDIIMAGEDESC pImage, uint64_t cbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, cbSize);
-}
-
-DECLINLINE(int) vdiFileWriteSync(PVDIIMAGEDESC pImage, uint64_t uOffset,
-                                 const void *pvBuffer, size_t cbBuffer,
-                                 size_t *pcbWritten)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage, uOffset,
-                                                       pvBuffer, cbBuffer, pcbWritten);
-}
-
-DECLINLINE(int) vdiFileReadSync(PVDIIMAGEDESC pImage, uint64_t uOffset,
-                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pImage->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, pcbRead);
-}
-
-DECLINLINE(int) vdiFileFlushSync(PVDIIMAGEDESC pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage);
-}
-
-DECLINLINE(int) vdiFileReadUserAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
-                                     PVDIOCTX pIoCtx, size_t cbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
-                                                           pImage->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-DECLINLINE(int) vdiFileWriteUserAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
-                                      PVDIOCTX pIoCtx, size_t cbWrite,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) vdiFileWriteMetaAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
-                                      void *pvBuffer, size_t cbBuffer,
-                                      PVDIOCTX pIoCtx,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pvBuffer,
-                                                            cbBuffer, pIoCtx,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) vdiFileFlushAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
-                                  PFNVDXFERCOMPLETED pfnComplete,
-                                  void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                        pImage->pStorage,
-                                                        pIoCtx, pfnComplete,
-                                                        pvCompleteUser);
-}
-
-DECLINLINE(size_t) vdiFileIoCtxSet(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
-                                   int ch, size_t cbSet)
-{
-    return pImage->pInterfaceIOCallbacks->pfnIoCtxSet(pImage->pInterfaceIO->pvUser,
-                                                      pIoCtx, ch, cbSet);
-}
-
-
-/**
- * Internal: Flush the image file to disk.
- */
-static void vdiFlushImage(PVDIIMAGEDESC pImage)
-{
-    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-    {
-        /* Save header. */
-        int rc = vdiUpdateHeader(pImage);
-        AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Rrc\n",
-                         pImage->pszFilename, rc));
-        vdiFileFlushSync(pImage);
-    }
-}
-
-/**
- * Internal: Free all allocated space for representing an image, and optionally
- * delete the image from disk.
- */
-static int vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (pImage->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-                vdiFlushImage(pImage);
-
-            vdiFileClose(pImage);
-            pImage->pStorage = NULL;
-        }
-
-        if (pImage->paBlocks)
-        {
-            RTMemFree(pImage->paBlocks);
-            pImage->paBlocks = NULL;
-        }
-
-        if (fDelete && pImage->pszFilename)
-            vdiFileDelete(pImage, pImage->pszFilename);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * 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 VDI 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)-1);
-}
-
-/**
- * Internal: check VDI preheader.
- */
-static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
-{
-    if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
-        return VERR_VD_VDI_INVALID_HEADER;
-
-    if (    VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
-        &&  pPreHdr->u32Version != 0x00000002)    /* old version. */
-        return VERR_VD_VDI_UNSUPPORTED_VERSION;
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal: translate VD image flags to VDI image type enum.
- */
-static VDIIMAGETYPE vdiTranslateImageFlags2VDI(unsigned uImageFlags)
-{
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        return VDI_IMAGE_TYPE_FIXED;
-    else if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
-        return VDI_IMAGE_TYPE_DIFF;
-    else
-        return VDI_IMAGE_TYPE_NORMAL;
-}
-
-/**
- * Internal: translate VDI image type enum to VD image type enum.
- */
-static unsigned vdiTranslateVDI2ImageFlags(VDIIMAGETYPE enmType)
-{
-    switch (enmType)
-    {
-        case VDI_IMAGE_TYPE_NORMAL:
-            return VD_IMAGE_FLAGS_NONE;
-        case VDI_IMAGE_TYPE_FIXED:
-            return VD_IMAGE_FLAGS_FIXED;
-        case VDI_IMAGE_TYPE_DIFF:
-            return VD_IMAGE_FLAGS_DIFF;
-        default:
-            AssertMsgFailed(("invalid VDIIMAGETYPE enmType=%d\n", (int)enmType));
-            return VD_IMAGE_FLAGS_NONE;
-    }
-}
-
-/**
- * Internal: Init VDI header. Always use latest header version.
- * @param   pHeader     Assumes it was initially initialized to all zeros.
- */
-static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
-                          const char *pszComment, uint64_t cbDisk,
-                          uint32_t cbBlock, uint32_t cbBlockExtra)
-{
-    pHeader->uVersion = VDI_IMAGE_VERSION;
-    pHeader->u.v1plus.cbHeader = sizeof(VDIHEADER1PLUS);
-    pHeader->u.v1plus.u32Type = (uint32_t)vdiTranslateImageFlags2VDI(uImageFlags);
-    pHeader->u.v1plus.fFlags = (uImageFlags & VD_VDI_IMAGE_FLAGS_ZERO_EXPAND) ? 1 : 0;
-#ifdef VBOX_STRICT
-    char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
-    Assert(!memcmp(pHeader->u.v1plus.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
-#endif
-    pHeader->u.v1plus.szComment[0] = '\0';
-    if (pszComment)
-    {
-        AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1plus.szComment),
-                  ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
-        strncat(pHeader->u.v1plus.szComment, pszComment, sizeof(pHeader->u.v1plus.szComment)-1);
-    }
-
-    /* Mark the legacy geometry not-calculated. */
-    pHeader->u.v1plus.LegacyGeometry.cCylinders = 0;
-    pHeader->u.v1plus.LegacyGeometry.cHeads = 0;
-    pHeader->u.v1plus.LegacyGeometry.cSectors = 0;
-    pHeader->u.v1plus.LegacyGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-    pHeader->u.v1plus.u32Dummy = 0; /* used to be the translation value */
-
-    pHeader->u.v1plus.cbDisk = cbDisk;
-    pHeader->u.v1plus.cbBlock = cbBlock;
-    pHeader->u.v1plus.cBlocks = (uint32_t)(cbDisk / cbBlock);
-    if (cbDisk % cbBlock)
-        pHeader->u.v1plus.cBlocks++;
-    pHeader->u.v1plus.cbBlockExtra = cbBlockExtra;
-    pHeader->u.v1plus.cBlocksAllocated = 0;
-
-    /* Init offsets. */
-    pHeader->u.v1plus.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1PLUS), VDI_DATA_ALIGN);
-    pHeader->u.v1plus.offData = RT_ALIGN_32(pHeader->u.v1plus.offBlocks + (pHeader->u.v1plus.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_DATA_ALIGN);
-
-    /* Init uuids. */
-    RTUuidCreate(&pHeader->u.v1plus.uuidCreate);
-    RTUuidClear(&pHeader->u.v1plus.uuidModify);
-    RTUuidClear(&pHeader->u.v1plus.uuidLinkage);
-    RTUuidClear(&pHeader->u.v1plus.uuidParentModify);
-
-    /* Mark LCHS geometry not-calculated. */
-    pHeader->u.v1plus.LCHSGeometry.cCylinders = 0;
-    pHeader->u.v1plus.LCHSGeometry.cHeads = 0;
-    pHeader->u.v1plus.LCHSGeometry.cSectors = 0;
-    pHeader->u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-}
-
-/**
- * Internal: Check VDI header.
- */
-static int vdiValidateHeader(PVDIHEADER pHeader)
-{
-    /* Check version-dependent 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_VD_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_VD_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_VD_VDI_INVALID_HEADER;
-            }
-
-            break;
-        }
-        default:
-            /* Unsupported. */
-            return VERR_VD_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) & ~VD_VDI_IMAGE_FLAGS_MASK)
-    {
-        LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
-        fFailed = true;
-    }
-
-    if (   getImageLCHSGeometry(pHeader)
-        && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
-    {
-        LogRel(("VDI: wrong sector size (%d != %d)\n",
-               (getImageLCHSGeometry(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_VD_VDI_INVALID_HEADER : VINF_SUCCESS;
-}
-
-/**
- * Internal: Set up VDIIMAGEDESC structure by image header.
- */
-static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
-{
-    pImage->uImageFlags        = getImageFlags(&pImage->Header);
-    pImage->uImageFlags       |= vdiTranslateVDI2ImageFlags(getImageType(&pImage->Header));
-    pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
-    pImage->offStartData       = getImageDataOffset(&pImage->Header);
-    pImage->uBlockMask         = getImageBlockSize(&pImage->Header) - 1;
-    pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
-    pImage->offStartBlockData  = getImageExtraBlockSize(&pImage->Header);
-    pImage->cbTotalBlockData   =   pImage->offStartBlockData
-                                 + getImageBlockSize(&pImage->Header);
-}
-
-/**
- * Internal: Create VDI image file.
- */
-static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
-                          unsigned uImageFlags, const char *pszComment,
-                          PCVDGEOMETRY pPCHSGeometry,
-                          PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
-                          unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
-                          void *pvUser, unsigned uPercentStart,
-                          unsigned uPercentSpan)
-{
-    int rc;
-    uint64_t cbTotal;
-    uint64_t cbFill;
-    uint64_t uOff;
-
-    /* Special check for comment length. */
-    if (   VALID_PTR(pszComment)
-        && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
-    {
-        rc = vdiError(pImage, VERR_VD_VDI_COMMENT_TOO_LONG, RT_SRC_POS, N_("VDI: comment is too long for '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    AssertPtr(pPCHSGeometry);
-    AssertPtr(pLCHSGeometry);
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    vdiInitPreHeader(&pImage->PreHeader);
-    vdiInitHeader(&pImage->Header, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
-    /* Save PCHS geometry. Not much work, and makes the flow of information
-     * quite a bit clearer - relying on the higher level isn't obvious. */
-    pImage->PCHSGeometry = *pPCHSGeometry;
-    /* Set LCHS geometry (legacy geometry is ignored for the current 1.1+). */
-    pImage->Header.u.v1plus.LCHSGeometry.cCylinders = pLCHSGeometry->cCylinders;
-    pImage->Header.u.v1plus.LCHSGeometry.cHeads = pLCHSGeometry->cHeads;
-    pImage->Header.u.v1plus.LCHSGeometry.cSectors = pLCHSGeometry->cSectors;
-    pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-
-    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
-    if (!pImage->paBlocks)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-    {
-        /* for growing images mark all blocks in paBlocks as free. */
-        for (unsigned 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 */
-        for (unsigned 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 image file. */
-    rc = vdiFileOpen(pImage, pImage->pszFilename,
-                     VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                                true /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: cannot create image '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    cbTotal =   pImage->offStartData
-              + (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
-
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        /* Check the free space on the disk and leave early if there is not
-         * sufficient space available. */
-        int64_t cbFree = 0;
-        rc = vdiFileGetFreeSpace(pImage, pImage->pszFilename, &cbFree);
-        if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbTotal))
-        {
-            rc = vdiError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("VDI: disk would overflow creating image '%s'"), pImage->pszFilename);
-            goto out;
-        }
-    }
-
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        /*
-         * Allocate & commit whole file if fixed image, it must be more
-         * effective than expanding file by write operations.
-         */
-        rc = vdiFileSetSize(pImage, cbTotal);
-    }
-    else
-    {
-        /* Set file size to hold header and blocks array. */
-        rc = vdiFileSetSize(pImage, pImage->offStartData);
-    }
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: setting image size failed for '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Use specified image uuid */
-    *getImageCreationUUID(&pImage->Header) = *pUuid;
-
-    /* Generate image last-modify uuid */
-    RTUuidCreate(getImageModificationUUID(&pImage->Header));
-
-    /* Write pre-header. */
-    rc = vdiFileWriteSync(pImage, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Write header. */
-    rc = vdiFileWriteSync(pImage, sizeof(pImage->PreHeader),
-                          &pImage->Header.u.v1plus,
-                          sizeof(pImage->Header.u.v1plus), NULL);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    rc = vdiFileWriteSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
-                          getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
-                          NULL);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block pointers failed for '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        /* Fill image with zeroes. We do this for every fixed-size image since on some systems
-         * (for example Windows Vista), it takes ages to write a block near the end of a sparse
-         * file and the guest could complain about an ATA timeout. */
-
-        /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
-         *        Currently supported file systems are ext4 and ocfs2. */
-
-        /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
-        const size_t cbBuf = 128 * _1K;
-        void *pvBuf = RTMemTmpAllocZ(cbBuf);
-        if (!pvBuf)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        cbFill = (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
-        uOff = 0;
-        /* Write data to all image blocks. */
-        while (uOff < cbFill)
-        {
-            unsigned cbChunk = (unsigned)RT_MIN(cbFill, cbBuf);
-
-            rc = vdiFileWriteSync(pImage, pImage->offStartData + uOff,
-                                  pvBuf, cbChunk, NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block failed for '%s'"), pImage->pszFilename);
-                RTMemTmpFree(pvBuf);
-                goto out;
-            }
-
-            uOff += cbChunk;
-
-            if (pfnProgress)
-            {
-                rc = pfnProgress(pvUser,
-                                 uPercentStart + uOff * uPercentSpan / cbFill);
-                if (RT_FAILURE(rc))
-                    goto out;
-            }
-        }
-        RTMemTmpFree(pvBuf);
-    }
-
-out:
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan);
-
-    if (RT_FAILURE(rc))
-        vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
-    return rc;
-}
-
-/**
- * Internal: Open a VDI image.
- */
-static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
-{
-    int rc;
-
-    pImage->uOpenFlags = uOpenFlags;
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the image.
-     */
-    rc = vdiFileOpen(pImage, pImage->pszFilename,
-                     VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        goto out;
-    }
-
-    /* Read pre-header. */
-    rc = vdiFileReadSync(pImage, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    rc = vdiValidatePreHeader(&pImage->PreHeader);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: invalid pre-header in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Read header. */
-    pImage->Header.uVersion = pImage->PreHeader.u32Version;
-    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
-    {
-        case 0:
-            rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
-                                 &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
-                                 NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
-                goto out;
-            }
-            break;
-        case 1:
-            rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
-                                 &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
-                                 NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
-                goto out;
-            }
-            /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write mode.
-             * Conversion is harmless, as any VirtualBox version supporting VDI
-             * 1.1 doesn't touch fields it doesn't know about. */
-            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                && GET_MINOR_HEADER_VERSION(&pImage->Header) == 1
-                && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
-            {
-                pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
-                /* Mark LCHS geometry not-calculated. */
-                pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
-                pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
-                pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
-                pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-            }
-            else if (pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
-            {
-                /* Read the actual VDI 1.1+ header completely. */
-                rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
-                                     &pImage->Header.u.v1plus,
-                                     sizeof(pImage->Header.u.v1plus), NULL);
-                if (RT_FAILURE(rc))
-                {
-                    rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
-                    goto out;
-                }
-            }
-            break;
-        default:
-            rc = vdiError(pImage, VERR_VD_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: unsupported major version %u in '%s'"), GET_MAJOR_HEADER_VERSION(&pImage->Header), pImage->pszFilename);
-            goto out;
-    }
-
-    rc = vdiValidateHeader(&pImage->Header);
-    if (RT_FAILURE(rc))
-    {
-        rc = vdiError(pImage, VERR_VD_VDI_INVALID_HEADER, RT_SRC_POS, N_("VDI: invalid header in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Setup image parameters by header. */
-    vdiSetupImageDesc(pImage);
-
-    /* Allocate memory for blocks array. */
-    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
-    if (!pImage->paBlocks)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    /* Read blocks array. */
-    rc = vdiFileReadSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
-                         getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
-                         NULL);
-
-out:
-    if (RT_FAILURE(rc))
-        vdiFreeImage(pImage, false);
-    return rc;
-}
-
-/**
- * Internal: Save header to file.
- */
-static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
-{
-    int rc;
-    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
-    {
-        case 0:
-            rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
-                                  &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
-                                  NULL);
-            break;
-        case 1:
-            if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
-                rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
-                                      &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
-                                      NULL);
-            else
-                rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
-                                      &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus),
-                                      NULL);
-            break;
-        default:
-            rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            break;
-    }
-    AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
-    return rc;
-}
-
-/**
- * Internal: Save header to file - async version.
- */
-static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
-{
-    int rc;
-    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
-    {
-        case 0:
-            rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
-                                       &pImage->Header.u.v0,
-                                       sizeof(pImage->Header.u.v0),
-                                       pIoCtx, NULL, NULL);
-            break;
-        case 1:
-            if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
-                rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
-                                           &pImage->Header.u.v1,
-                                           sizeof(pImage->Header.u.v1),
-                                           pIoCtx, NULL, NULL);
-            else
-                rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
-                                           &pImage->Header.u.v1plus,
-                                           sizeof(pImage->Header.u.v1plus),
-                                           pIoCtx, NULL, NULL);
-            break;
-        default:
-            rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            break;
-    }
-    AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
-              ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, 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 (RT_SUCCESS(rc))
-    {
-        /* write only one block pointer. */
-        rc = vdiFileWriteSync(pImage, pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
-                              &pImage->paBlocks[uBlock], sizeof(VDIIMAGEBLOCKPOINTER),
-                              NULL);
-        AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
-                         uBlock, pImage->pszFilename, rc));
-    }
-    return rc;
-}
-
-/**
- * Internal: Save block pointer to file, save header to file - async version.
- */
-static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
-                                   PVDIOCTX pIoCtx)
-{
-    /* Update image header. */
-    int rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
-    if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-    {
-        /* write only one block pointer. */
-        rc = vdiFileWriteMetaAsync(pImage,
-                                   pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
-                                   &pImage->paBlocks[uBlock],
-                                   sizeof(VDIIMAGEBLOCKPOINTER),
-                                   pIoCtx, NULL, NULL);
-        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
-                  ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
-                  uBlock, pImage->pszFilename, rc));
-    }
-    return rc;
-}
-
-/**
- * Internal: Flush the image file to disk - async version.
- */
-static int vdiFlushImageAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-
-    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-    {
-        /* Save header. */
-        rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
-        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
-                  ("vdiUpdateHeaderAsync() failed, filename=\"%s\", rc=%Rrc\n",
-                  pImage->pszFilename, rc));
-        rc = vdiFileFlushAsync(pImage, pIoCtx, NULL, NULL);
-        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
-                  ("Flushing data to disk failed rc=%Rrc\n", rc));
-    }
-
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int vdiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
-    int rc = VINF_SUCCESS;
-    PVDIIMAGEDESC pImage;
-
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->paBlocks = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
-    vdiFreeImage(pImage, false);
-    RTMemFree(pImage);
-
-    if (RT_SUCCESS(rc))
-        *penmType = VDTYPE_HDD;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int vdiOpen(const char *pszFilename, unsigned uOpenFlags,
-                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                   VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PVDIIMAGEDESC pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->paBlocks = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = vdiOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pImage;
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int vdiCreate(const char *pszFilename, uint64_t cbSize,
-                     unsigned uImageFlags, const char *pszComment,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     PCRTUUID pUuid, unsigned uOpenFlags,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc;
-    PVDIIMAGEDESC pImage;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check size. Maximum 4PB-3M. No tricks with adjusting the 1M block size
-     * so far, which would extend the size. */
-    cbSize = RT_ALIGN_64(cbSize, _1M);
-    if (    !cbSize
-        ||  cbSize >= _1P * 2 - _1M * 3)
-    {
-        rc = VERR_VD_INVALID_SIZE;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename
-        || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
-        || !VALID_PTR(pPCHSGeometry)
-        || !VALID_PTR(pLCHSGeometry))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->paBlocks = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = vdiCreateImage(pImage, cbSize, uImageFlags, pszComment,
-                        pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
-                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
-    if (RT_SUCCESS(rc))
-    {
-        /* So far the image is opened in read/write mode. Make sure the
-         * image is opened in read-only mode if the caller requested that. */
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            vdiFreeImage(pImage, false);
-            rc = vdiOpenImage(pImage, uOpenFlags);
-            if (RT_FAILURE(rc))
-            {
-                RTMemFree(pImage);
-                goto out;
-            }
-        }
-        *ppBackendData = pImage;
-    }
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int vdiRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-
-    int rc = VINF_SUCCESS;
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-
-    /* Check arguments. */
-    if (   !pImage
-        || !pszFilename
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Close the image. */
-    vdiFreeImage(pImage, false);
-
-    /* Rename the file. */
-    rc = vdiFileMove(pImage, pImage->pszFilename, pszFilename, 0);
-    if (RT_FAILURE(rc))
-    {
-        /* The move failed, try to reopen the original image. */
-        int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
-        if (RT_FAILURE(rc2))
-            rc = rc2;
-
-        goto out;
-    }
-
-    /* Update pImage with the new information. */
-    pImage->pszFilename = pszFilename;
-
-    /* Open the new image. */
-    rc = vdiOpenImage(pImage, pImage->uOpenFlags);
-    if (RT_FAILURE(rc))
-        goto out;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int vdiClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    rc = vdiFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                   size_t cbToRead, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uBlock;
-    unsigned offRead;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(!(uOffset % 512));
-    Assert(!(cbToRead % 512));
-
-    if (   uOffset + cbToRead > getImageDiskSize(&pImage->Header)
-        || !VALID_PTR(pvBuf)
-        || !cbToRead)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Calculate starting block number and offset inside it. */
-    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
-    offRead = (unsigned)uOffset & pImage->uBlockMask;
-
-    /* Clip read range to at most the rest of the block. */
-    cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
-    Assert(!(cbToRead % 512));
-
-    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
-        rc = VERR_VD_BLOCK_FREE;
-    else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
-    {
-        memset(pvBuf, 0, cbToRead);
-        rc = VINF_SUCCESS;
-    }
-    else
-    {
-        /* Block present in image file, read relevant data. */
-        uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
-                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
-        rc = vdiFileReadSync(pImage, u64Offset, pvBuf, cbToRead, NULL);
-    }
-
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbToRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**@copydoc VBOXHDDBACKEND::pfnWrite */
-static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                    size_t cbToWrite, size_t *pcbWriteProcess,
-                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uBlock;
-    unsigned offWrite;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-    Assert(!(uOffset % 512));
-    Assert(!(cbToWrite % 512));
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    if (!VALID_PTR(pvBuf) || !cbToWrite)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* No size check here, will do that later.  For dynamic images which are
-     * not multiples of the block size in length, this would prevent writing to
-     * the last block. */
-
-    /* Calculate starting block number and offset inside it. */
-    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
-    offWrite = (unsigned)uOffset & pImage->uBlockMask;
-
-    /* Clip write range to at most the rest of the block. */
-    cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
-    Assert(!(cbToWrite % 512));
-
-    do
-    {
-        if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
-        {
-            /* Block is either free or zero. */
-            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
-                && (   pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
-                    || cbToWrite == getImageBlockSize(&pImage->Header)))
-            {
-                /* 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));
-                Assert(cbToWrite * 8 <= UINT32_MAX);
-                if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
-                {
-                    pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
-                    break;
-                }
-            }
-
-            if (cbToWrite == getImageBlockSize(&pImage->Header))
-            {
-                /* Full block write to previously unallocated block.
-                 * Allocate block and write data. */
-                Assert(!offWrite);
-                unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
-                uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
-                                   + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdiFileWriteSync(pImage, u64Offset, pvBuf, cbToWrite, NULL);
-                if (RT_FAILURE(rc))
-                    goto out;
-                pImage->paBlocks[uBlock] = cBlocksAllocated;
-                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
-
-                rc = vdiUpdateBlockInfo(pImage, uBlock);
-                if (RT_FAILURE(rc))
-                    goto out;
-
-                *pcbPreRead = 0;
-                *pcbPostRead = 0;
-            }
-            else
-            {
-                /* Trying to do a partial write to an unallocated block. Don't do
-                 * anything except letting the upper layer know what to do. */
-                *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
-                *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
-                rc = VERR_VD_BLOCK_FREE;
-            }
-        }
-        else
-        {
-            /* Block present in image file, write relevant data. */
-            uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
-                               + (pImage->offStartData + pImage->offStartBlockData + offWrite);
-            rc = vdiFileWriteSync(pImage, u64Offset, pvBuf, cbToWrite, NULL);
-        }
-    } while (0);
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbToWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vdiFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    Assert(pImage);
-
-    vdiFlushImage(pImage);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned vdiGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uVersion;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uVersion = pImage->PreHeader.u32Version;
-    else
-        uVersion = 0;
-
-    LogFlowFunc(("returns %#x\n", uVersion));
-    return uVersion;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t vdiGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    uint64_t cbSize;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        cbSize = getImageDiskSize(&pImage->Header);
-    else
-        cbSize = 0;
-
-    LogFlowFunc(("returns %llu\n", cbSize));
-    return cbSize;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t vdiGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        uint64_t cbFile;
-        if (pImage->pStorage)
-        {
-            int rc = vdiFileGetSize(pImage, &cbFile);
-            if (RT_SUCCESS(rc))
-                cb += cbFile;
-        }
-    }
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pImage->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
-        PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
-        if (!pGeometry)
-            pGeometry = &DummyGeo;
-
-        if (    pGeometry->cCylinders > 0
-            &&  pGeometry->cHeads > 0
-            &&  pGeometry->cSectors > 0)
-        {
-            pLCHSGeometry->cCylinders = pGeometry->cCylinders;
-            pLCHSGeometry->cHeads = pGeometry->cHeads;
-            pLCHSGeometry->cSectors = pGeometry->cSectors;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    PVDIDISKGEOMETRY pGeometry;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pGeometry = getImageLCHSGeometry(&pImage->Header);
-        if (pGeometry)
-        {
-            pGeometry->cCylinders = pLCHSGeometry->cCylinders;
-            pGeometry->cHeads = pLCHSGeometry->cHeads;
-            pGeometry->cSectors = pLCHSGeometry->cSectors;
-            pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-
-            /* Update header information in base image file. */
-            vdiFlushImage(pImage);
-        }
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned vdiGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uImageFlags = pImage->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned vdiGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-    const char *pszFilename;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    pszFilename = pImage->pszFilename;
-    rc = vdiFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vdiOpenImage(pImage, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int vdiGetComment(void *pBackendData, char *pszComment,
-                         size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        char *pszTmp = getImageComment(&pImage->Header);
-        /* Make this foolproof even if the image doesn't have the zero
-         * termination. With some luck the repaired header will be saved. */
-        size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
-        if (cb == VDI_IMAGE_COMMENT_SIZE)
-        {
-            pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
-            cb--;
-        }
-        if (cb < cbComment)
-        {
-            /* memcpy is much better than strncpy. */
-            memcpy(pszComment, pszTmp, cb + 1);
-        }
-        else
-            rc = VERR_BUFFER_OVERFLOW;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int vdiSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-        {
-            size_t cchComment = pszComment ? strlen(pszComment) : 0;
-            if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
-            {
-                LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
-                rc = VERR_VD_VDI_COMMENT_TOO_LONG;
-                goto out;
-            }
-
-            /* we don't support old style images */
-            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
-            {
-                /*
-                 * Update the comment field, making sure to zero out all of the previous comment.
-                 */
-                memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
-                memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
-
-                /* write out new the header */
-                rc = vdiUpdateHeader(pImage);
-            }
-            else
-                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-        }
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = *getImageCreationUUID(&pImage->Header);
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
-                pImage->Header.u.v1.uuidCreate = *pUuid;
-            /* Make it possible to clone old VDIs. */
-            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
-                pImage->Header.u.v0.uuidCreate = *pUuid;
-            else
-            {
-                LogFunc(("Version is not supported!\n"));
-                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            }
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = *getImageModificationUUID(&pImage->Header);
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
-                pImage->Header.u.v1.uuidModify = *pUuid;
-            /* Make it possible to clone old VDIs. */
-            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
-                pImage->Header.u.v0.uuidModify = *pUuid;
-            else
-            {
-                LogFunc(("Version is not supported!\n"));
-                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            }
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = *getImageParentUUID(&pImage->Header);
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
-                pImage->Header.u.v1.uuidLinkage = *pUuid;
-            /* Make it possible to clone old VDIs. */
-            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
-                pImage->Header.u.v0.uuidLinkage = *pUuid;
-            else
-            {
-                LogFunc(("Version is not supported!\n"));
-                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            }
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = *getImageParentModificationUUID(&pImage->Header);
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
-                pImage->Header.u.v1.uuidParentModify = *pUuid;
-            else
-            {
-                LogFunc(("Version is not supported!\n"));
-                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
-            }
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void vdiDump(void *pBackendData)
-{
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-
-    vdiMessage(pImage, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
-                pImage->pszFilename,
-                (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
-                pImage->uOpenFlags,
-                pImage->pStorage);
-    vdiMessage(pImage, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
-                pImage->PreHeader.u32Version,
-                getImageType(&pImage->Header),
-                getImageFlags(&pImage->Header),
-                getImageDiskSize(&pImage->Header));
-    vdiMessage(pImage, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
-                getImageBlockSize(&pImage->Header),
-                getImageExtraBlockSize(&pImage->Header),
-                getImageBlocks(&pImage->Header),
-                getImageBlocksAllocated(&pImage->Header));
-    vdiMessage(pImage, "Header: offBlocks=%u offData=%u\n",
-                getImageBlocksOffset(&pImage->Header),
-                getImageDataOffset(&pImage->Header));
-    PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
-    if (pg)
-        vdiMessage(pImage, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
-                    pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
-    vdiMessage(pImage, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
-    vdiMessage(pImage, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
-    vdiMessage(pImage, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
-    if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
-        vdiMessage(pImage, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
-    vdiMessage(pImage, "Image:  fFlags=%08X offStartBlocks=%u offStartData=%u\n",
-                pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
-    vdiMessage(pImage, "Image:  uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
-                pImage->uBlockMask,
-                pImage->cbTotalBlockData,
-                pImage->uShiftOffset2Index,
-                pImage->offStartBlockData);
-
-    unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
-    for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
-    {
-        if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
-        {
-            cBlocksNotFree++;
-            if (pImage->paBlocks[uBlock] >= cBlocks)
-                cBadBlocks++;
-        }
-    }
-    if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
-    {
-        vdiMessage(pImage, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
-                cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
-    }
-    if (cBadBlocks)
-    {
-        vdiMessage(pImage, "!! WARNING: %u bad blocks found !!\n",
-                cBadBlocks);
-    }
-}
-
-static bool vdiIsAsyncIOSupported(void *pBackendData)
-{
-    return true;
-}
-
-static int vdiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
-                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uBlock;
-    unsigned offRead;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(!(uOffset % 512));
-    Assert(!(cbToRead % 512));
-
-    if (   uOffset + cbToRead > getImageDiskSize(&pImage->Header)
-        || !VALID_PTR(pIoCtx)
-        || !cbToRead)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Calculate starting block number and offset inside it. */
-    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
-    offRead = (unsigned)uOffset & pImage->uBlockMask;
-
-    /* Clip read range to at most the rest of the block. */
-    cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
-    Assert(!(cbToRead % 512));
-
-    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
-        rc = VERR_VD_BLOCK_FREE;
-    else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
-    {
-        size_t cbSet;
-
-        cbSet = vdiFileIoCtxSet(pImage, pIoCtx, 0, cbToRead);
-        Assert(cbSet == cbToRead);
-
-        rc = VINF_SUCCESS;
-    }
-    else
-    {
-        /* Block present in image file, read relevant data. */
-        uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
-                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
-        rc = vdiFileReadUserAsync(pImage, u64Offset, pIoCtx, cbToRead);
-    }
-
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbToRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-static int vdiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
-                         PVDIOCTX pIoCtx,
-                         size_t *pcbWriteProcess, size_t *pcbPreRead,
-                         size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    unsigned uBlock;
-    unsigned offWrite;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-    Assert(!(uOffset % 512));
-    Assert(!(cbToWrite % 512));
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    if (!VALID_PTR(pIoCtx) || !cbToWrite)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* No size check here, will do that later.  For dynamic images which are
-     * not multiples of the block size in length, this would prevent writing to
-     * the last block. */
-
-    /* Calculate starting block number and offset inside it. */
-    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
-    offWrite = (unsigned)uOffset & pImage->uBlockMask;
-
-    /* Clip write range to at most the rest of the block. */
-    cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
-    Assert(!(cbToWrite % 512));
-
-    do
-    {
-        if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
-        {
-            /* Block is either free or zero. */
-            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
-                && (   pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
-                    || cbToWrite == getImageBlockSize(&pImage->Header)))
-            {
-#if 0 /** @todo Provide interface to check an I/O context for a specific value */
-                /* 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));
-                Assert(cbToWrite * 8 <= UINT32_MAX);
-                if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
-                {
-                    pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
-                    break;
-                }
-#endif
-            }
-
-            if (   cbToWrite == getImageBlockSize(&pImage->Header)
-                && !(fWrite & VD_WRITE_NO_ALLOC))
-            {
-                /* Full block write to previously unallocated block.
-                 * Allocate block and write data. */
-                Assert(!offWrite);
-                unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
-                uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
-                                   + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdiFileWriteUserAsync(pImage, u64Offset, pIoCtx, cbToWrite, NULL, NULL);
-                if (RT_UNLIKELY(RT_FAILURE_NP(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)))
-                    goto out;
-                pImage->paBlocks[uBlock] = cBlocksAllocated;
-                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
-
-                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx);
-                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-                    goto out;
-
-                *pcbPreRead = 0;
-                *pcbPostRead = 0;
-            }
-            else
-            {
-                /* Trying to do a partial write to an unallocated block. Don't do
-                 * anything except letting the upper layer know what to do. */
-                *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
-                *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
-                rc = VERR_VD_BLOCK_FREE;
-            }
-        }
-        else
-        {
-            /* Block present in image file, write relevant data. */
-            uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
-                               + (pImage->offStartData + pImage->offStartBlockData + offWrite);
-            rc = vdiFileWriteUserAsync(pImage, u64Offset, pIoCtx, cbToWrite, NULL, NULL);
-        }
-    } while (0);
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbToWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-static int vdiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    Assert(pImage);
-
-    rc = vdiFlushImageAsync(pImage, pIoCtx);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCompact */
-static int vdiCompact(void *pBackendData, unsigned uPercentStart,
-                      unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
-                      PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
-{
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-    void *pvBuf = NULL, *pvTmp = NULL;
-    unsigned *paBlocks2 = NULL;
-
-    int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
-    void *pvParent = NULL;
-    PVDINTERFACE pIfParentState = VDInterfaceGet(pVDIfsOperation,
-                                                 VDINTERFACETYPE_PARENTSTATE);
-    PVDINTERFACEPARENTSTATE pCbParentState = NULL;
-    if (pIfParentState)
-    {
-        pCbParentState = VDGetInterfaceParentState(pIfParentState);
-        if (pCbParentState)
-            pfnParentRead = pCbParentState->pfnParentRead;
-        pvParent = pIfParentState->pvUser;
-    }
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    do {
-        AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
-
-        AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
-                        rc = VERR_VD_IMAGE_READ_ONLY);
-
-        unsigned cBlocks;
-        unsigned cBlocksToMove = 0;
-        size_t cbBlock;
-        cBlocks = getImageBlocks(&pImage->Header);
-        cbBlock = getImageBlockSize(&pImage->Header);
-        if (pfnParentRead)
-        {
-            pvBuf = RTMemTmpAlloc(cbBlock);
-            AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
-        }
-        pvTmp = RTMemTmpAlloc(cbBlock);
-        AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
-
-        uint64_t cbFile;
-        rc = vdiFileGetSize(pImage, &cbFile);
-        AssertRCBreak(rc);
-        unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
-        if (cBlocksAllocated == 0)
-        {
-            /* No data blocks in this image, no need to compact. */
-            rc = VINF_SUCCESS;
-            break;
-        }
-
-        /* Allocate block array for back resolving. */
-        paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
-        AssertBreakStmt(VALID_PTR(paBlocks2), rc = VERR_NO_MEMORY);
-        /* Fill out back resolving, check/fix allocation errors before
-         * compacting the image, just to be on the safe side. Update the
-         * image contents straight away, as this enables cancelling. */
-        for (unsigned i = 0; i < cBlocksAllocated; i++)
-            paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
-        rc = VINF_SUCCESS;
-        for (unsigned i = 0; i < cBlocks; i++)
-        {
-            VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
-            if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
-            {
-                if (ptrBlock < cBlocksAllocated)
-                {
-                    if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
-                        paBlocks2[ptrBlock] = i;
-                    else
-                    {
-                        LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
-                                 i, pImage->pszFilename));
-                        pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
-                        rc = vdiUpdateBlockInfo(pImage, i);
-                        if (RT_FAILURE(rc))
-                            break;
-                    }
-                }
-                else
-                {
-                    LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
-                             i, pImage->pszFilename));
-                    pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
-                    rc = vdiUpdateBlockInfo(pImage, i);
-                    if (RT_FAILURE(rc))
-                        break;
-                }
-            }
-        }
-        if (RT_FAILURE(rc))
-            break;
-
-        /* Find redundant information and update the block pointers
-         * accordingly, creating bubbles. Keep disk up to date, as this
-         * enables cancelling. */
-        for (unsigned i = 0; i < cBlocks; i++)
-        {
-            VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
-            if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
-            {
-                /* Block present in image file, read relevant data. */
-                uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
-                                   + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdiFileReadSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
-                if (RT_FAILURE(rc))
-                    break;
-
-                if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
-                {
-                    pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
-                    rc = vdiUpdateBlockInfo(pImage, i);
-                    if (RT_FAILURE(rc))
-                        break;
-                    paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
-                    /* Adjust progress info, one block to be relocated. */
-                    cBlocksToMove++;
-                }
-                else if (pfnParentRead)
-                {
-                    rc = pfnParentRead(pvParent, i * cbBlock, pvBuf, cbBlock);
-                    if (RT_FAILURE(rc))
-                        break;
-                    if (!memcmp(pvTmp, pvBuf, cbBlock))
-                    {
-                        pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
-                        rc = vdiUpdateBlockInfo(pImage, i);
-                        if (RT_FAILURE(rc))
-                            break;
-                        paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
-                        /* Adjust progress info, one block to be relocated. */
-                        cBlocksToMove++;
-                    }
-                }
-            }
-
-            if (pCbProgress && pCbProgress->pfnProgress)
-            {
-                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                              (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
-                if (RT_FAILURE(rc))
-                    break;
-            }
-        }
-        if (RT_FAILURE(rc))
-            break;
-
-        /* Fill bubbles with other data (if available). */
-        unsigned cBlocksMoved = 0;
-        unsigned uBlockUsedPos = cBlocksAllocated;
-        for (unsigned i = 0; i < cBlocksAllocated; i++)
-        {
-            unsigned uBlock = paBlocks2[i];
-            if (uBlock == VDI_IMAGE_BLOCK_FREE)
-            {
-                unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
-                while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
-                {
-                    uBlockUsedPos--;
-                    uBlockData = paBlocks2[uBlockUsedPos];
-                }
-                /* Terminate early if there is no block which needs copying. */
-                if (uBlockUsedPos == i)
-                    break;
-                uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
-                                   + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdiFileReadSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
-                u64Offset = (uint64_t)i * pImage->cbTotalBlockData
-                          + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdiFileWriteSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
-                pImage->paBlocks[uBlockData] = i;
-                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
-                rc = vdiUpdateBlockInfo(pImage, uBlockData);
-                if (RT_FAILURE(rc))
-                    break;
-                paBlocks2[i] = uBlockData;
-                paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
-                cBlocksMoved++;
-            }
-
-            if (pCbProgress && pCbProgress->pfnProgress)
-            {
-                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                              (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
-
-                if (RT_FAILURE(rc))
-                    break;
-            }
-        }
-        if (RT_FAILURE(rc))
-            break;
-
-        /* Update image header. */
-        setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
-        vdiUpdateHeader(pImage);
-
-        /* Truncate the image to the proper size to finish compacting. */
-        rc = vdiFileSetSize(pImage,
-                              (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
-                            + pImage->offStartData + pImage->offStartBlockData);
-    } while (0);
-
-    if (paBlocks2)
-        RTMemTmpFree(paBlocks2);
-    if (pvTmp)
-        RTMemTmpFree(pvTmp);
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-
-    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
-    {
-        pCbProgress->pfnProgress(pIfProgress->pvUser,
-                                 uPercentStart + uPercentSpan);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnResize */
-static int vdiResize(void *pBackendData, uint64_t cbSize,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation)
-{
-    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /*
-     * Making the image smaller is not supported at the moment.
-     * Resizing is also not supported for fixed size images and
-     * very old images.
-     */
-    /** @todo implement making the image smaller, it is the responsibility of
-     * the user to know what he's doing. */
-    if (   cbSize < getImageDiskSize(&pImage->Header)
-        || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
-        || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        rc = VERR_NOT_SUPPORTED;
-    else if (cbSize > getImageDiskSize(&pImage->Header))
-    {
-        unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
-        uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header);    /** < New number of blocks in the image after the resize */
-        if (cbSize % getImageBlockSize(&pImage->Header))
-            cBlocksNew++;
-
-        uint32_t cBlocksOld      = getImageBlocks(&pImage->Header);           /** < Number of blocks before the resize. */
-        uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
-        uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
-
-        if (   pImage->offStartData != offStartDataNew
-            && cBlocksAllocated > 0)
-        {
-            /* Calculate how many sectors need to be relocated. */
-            uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
-            unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
-            if (cbOverlapping % getImageBlockSize(&pImage->Header))
-                cBlocksReloc++;
-
-            /* Since only full blocks can be relocated the new data start is
-             * determined by moving it block by block. */
-            cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
-            offStartDataNew = pImage->offStartData;
-
-            /* Do the relocation. */
-            LogFlow(("Relocating %u blocks\n", cBlocksReloc));
-
-            /*
-             * Get the blocks we need to relocate first, they are appended to the end
-             * of the image.
-             */
-            void *pvBuf = NULL, *pvZero = NULL;
-            do
-            {
-                VDIIMAGEBLOCKPOINTER uBlock = 0;
-
-                /* Allocate data buffer. */
-                pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
-                if (!pvBuf)
-                {
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-
-                /* Allocate buffer for overwriting with zeroes. */
-                pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
-                if (!pvZero)
-                {
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-
-                for (unsigned i = 0; i < cBlocksReloc; i++)
-                {
-                    /* Search the index in the block table. */
-                    for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
-                    {
-                        if (pImage->paBlocks[idxBlock] == uBlock)
-                        {
-                            /* Read data and append to the end of the image. */
-                            rc = vdiFileReadSync(pImage, offStartDataNew, pvBuf, pImage->cbTotalBlockData, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            uint64_t offBlockAppend;
-                            rc = vdiFileGetSize(pImage, &offBlockAppend);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            rc = vdiFileWriteSync(pImage, offBlockAppend, pvBuf, pImage->cbTotalBlockData, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            /* Zero out the old block area. */
-                            rc = vdiFileWriteSync(pImage, offStartDataNew, pvZero, pImage->cbTotalBlockData, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            /* Update block counter. */
-                            pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
-
-                            /*
-                             * Decrease the block number of all other entries in the array.
-                             * They were moved one block to the front.
-                             * Doing it as a separate step iterating over the array again
-                             * because an error while relocating the block might end up
-                             * in a corrupted image otherwise.
-                             */
-                            for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
-                            {
-                                if (   idxBlock2 != idxBlock
-                                    && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
-                                    pImage->paBlocks[idxBlock2]--;
-                            }
-
-                            /* Continue with the next block. */
-                            break;
-                        }
-                    }
-
-                    if (RT_FAILURE(rc))
-                        break;
-
-                    uBlock++;
-                    offStartDataNew += pImage->cbTotalBlockData;
-                }
-            } while (0);
-
-            if (pvBuf)
-                RTMemFree(pvBuf);
-            if (pvZero)
-                RTMemFree(pvZero);
-        }
-
-        /*
-         * We need to update the new offsets for the image data in the out of memory
-         * case too because we relocated the blocks already.
-         */
-        pImage->offStartData = offStartDataNew;
-        setImageDataOffset(&pImage->Header, offStartDataNew);
-
-        /*
-         * Relocation done, expand the block array and update the header with
-         * the new data.
-         */
-        if (RT_SUCCESS(rc))
-        {
-            PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
-            if (paBlocksNew)
-            {
-                pImage->paBlocks = paBlocksNew;
-
-                /* Mark the new blocks as unallocated. */
-                for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
-                    pImage->paBlocks[idxBlock] = VDI_IMAGE_BLOCK_FREE;
-            }
-            else
-                rc = VERR_NO_MEMORY;
-
-            /* Write the block array before updating the rest. */
-            rc = vdiFileWriteSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
-                                  cbBlockspaceNew, NULL);
-
-            if (RT_SUCCESS(rc))
-            {
-                /* Update size and new block count. */
-                setImageDiskSize(&pImage->Header, cbSize);
-                setImageBlocks(&pImage->Header, cBlocksNew);
-                /* Update geometry. */
-                pImage->PCHSGeometry = *pPCHSGeometry;
-
-                PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
-                if (pGeometry)
-                {
-                    pGeometry->cCylinders = pLCHSGeometry->cCylinders;
-                    pGeometry->cHeads = pLCHSGeometry->cHeads;
-                    pGeometry->cSectors = pLCHSGeometry->cSectors;
-                    pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
-                }
-            }
-        }
-
-        /* Update header information in base image file. */
-        vdiFlushImage(pImage);
-    }
-    /* Same size doesn't change the image at all. */
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_VDIBackend =
-{
-    /* pszBackendName */
-    "VDI",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-      VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
-    | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aVdiFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    vdiCheckIfValid,
-    /* pfnOpen */
-    vdiOpen,
-    /* pfnCreate */
-    vdiCreate,
-    /* pfnRename */
-    vdiRename,
-    /* pfnClose */
-    vdiClose,
-    /* pfnRead */
-    vdiRead,
-    /* pfnWrite */
-    vdiWrite,
-    /* pfnFlush */
-    vdiFlush,
-    /* pfnGetVersion */
-    vdiGetVersion,
-    /* pfnGetSize */
-    vdiGetSize,
-    /* pfnGetFileSize */
-    vdiGetFileSize,
-    /* pfnGetPCHSGeometry */
-    vdiGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    vdiSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    vdiGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    vdiSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    vdiGetImageFlags,
-    /* pfnGetOpenFlags */
-    vdiGetOpenFlags,
-    /* pfnSetOpenFlags */
-    vdiSetOpenFlags,
-    /* pfnGetComment */
-    vdiGetComment,
-    /* pfnSetComment */
-    vdiSetComment,
-    /* pfnGetUuid */
-    vdiGetUuid,
-    /* pfnSetUuid */
-    vdiSetUuid,
-    /* pfnGetModificationUuid */
-    vdiGetModificationUuid,
-    /* pfnSetModificationUuid */
-    vdiSetModificationUuid,
-    /* pfnGetParentUuid */
-    vdiGetParentUuid,
-    /* pfnSetParentUuid */
-    vdiSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    vdiGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    vdiSetParentModificationUuid,
-    /* pfnDump */
-    vdiDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    vdiIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    vdiAsyncRead,
-    /* pfnAsyncWrite */
-    vdiAsyncWrite,
-    /* pfnAsyncFlush */
-    vdiAsyncFlush,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    vdiCompact,
-    /* pfnResize */
-    vdiResize
-};
Index: unk/src/VBox/Devices/Storage/VHDHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VHDHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,3115 +1,0 @@
-/* $Id$ */
-/** @file
- * VHD Disk image, Core Code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_VHD
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <VBox/version.h>
-#include <iprt/asm.h>
-#include <iprt/assert.h>
-#include <iprt/mem.h>
-#include <iprt/uuid.h>
-#include <iprt/path.h>
-#include <iprt/string.h>
-
-#define VHD_RELATIVE_MAX_PATH 512
-#define VHD_ABSOLUTE_MAX_PATH 512
-
-#define VHD_SECTOR_SIZE 512
-#define VHD_BLOCK_SIZE  (2 * _1M)
-
-/* This is common to all VHD disk types and is located at the end of the image */
-#pragma pack(1)
-typedef struct VHDFooter
-{
-    char     Cookie[8];
-    uint32_t Features;
-    uint32_t Version;
-    uint64_t DataOffset;
-    uint32_t TimeStamp;
-    uint8_t  CreatorApp[4];
-    uint32_t CreatorVer;
-    uint32_t CreatorOS;
-    uint64_t OrigSize;
-    uint64_t CurSize;
-    uint16_t DiskGeometryCylinder;
-    uint8_t  DiskGeometryHeads;
-    uint8_t  DiskGeometrySectors;
-    uint32_t DiskType;
-    uint32_t Checksum;
-    char     UniqueID[16];
-    uint8_t  SavedState;
-    uint8_t  Reserved[427];
-} VHDFooter;
-#pragma pack()
-
-/* this really is spelled with only one n */
-#define VHD_FOOTER_COOKIE "conectix"
-#define VHD_FOOTER_COOKIE_SIZE 8
-
-#define VHD_FOOTER_FEATURES_NOT_ENABLED   0
-#define VHD_FOOTER_FEATURES_TEMPORARY     1
-#define VHD_FOOTER_FEATURES_RESERVED      2
-
-#define VHD_FOOTER_FILE_FORMAT_VERSION    0x00010000
-#define VHD_FOOTER_DATA_OFFSET_FIXED      UINT64_C(0xffffffffffffffff)
-#define VHD_FOOTER_DISK_TYPE_FIXED        2
-#define VHD_FOOTER_DISK_TYPE_DYNAMIC      3
-#define VHD_FOOTER_DISK_TYPE_DIFFERENCING 4
-
-#define VHD_MAX_LOCATOR_ENTRIES           8
-#define VHD_PLATFORM_CODE_NONE            0
-#define VHD_PLATFORM_CODE_WI2R            0x57693272
-#define VHD_PLATFORM_CODE_WI2K            0x5769326B
-#define VHD_PLATFORM_CODE_W2RU            0x57327275
-#define VHD_PLATFORM_CODE_W2KU            0x57326B75
-#define VHD_PLATFORM_CODE_MAC             0x4D163220
-#define VHD_PLATFORM_CODE_MACX            0x4D163258
-
-/* Header for expanding disk images. */
-#pragma pack(1)
-typedef struct VHDParentLocatorEntry
-{
-    uint32_t u32Code;
-    uint32_t u32DataSpace;
-    uint32_t u32DataLength;
-    uint32_t u32Reserved;
-    uint64_t u64DataOffset;
-} VHDPLE, *PVHDPLE;
-
-typedef struct VHDDynamicDiskHeader
-{
-    char     Cookie[8];
-    uint64_t DataOffset;
-    uint64_t TableOffset;
-    uint32_t HeaderVersion;
-    uint32_t MaxTableEntries;
-    uint32_t BlockSize;
-    uint32_t Checksum;
-    uint8_t  ParentUuid[16];
-    uint32_t ParentTimeStamp;
-    uint32_t Reserved0;
-    uint16_t ParentUnicodeName[256];
-    VHDPLE   ParentLocatorEntry[VHD_MAX_LOCATOR_ENTRIES];
-    uint8_t  Reserved1[256];
-} VHDDynamicDiskHeader;
-#pragma pack()
-
-#define VHD_DYNAMIC_DISK_HEADER_COOKIE "cxsparse"
-#define VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE 8
-#define VHD_DYNAMIC_DISK_HEADER_VERSION 0x00010000
-
-/**
- * Complete VHD image data structure.
- */
-typedef struct VHDIMAGE
-{
-    /** Image file name. */
-    const char       *pszFilename;
-    /** Opaque storage handle. */
-    PVDIOSTORAGE      pStorage;
-
-    /** I/O interface. */
-    PVDINTERFACE      pInterfaceIO;
-    /** I/O interface callbacks. */
-    PVDINTERFACEIOINT pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE      pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE      pVDIfsImage;
-    /** Error interface. */
-    PVDINTERFACE      pInterfaceError;
-    /** Error interface callback table. */
-    PVDINTERFACEERROR pInterfaceErrorCallbacks;
-
-    /** Open flags passed by VBoxHDD layer. */
-    unsigned        uOpenFlags;
-    /** Image flags defined during creation or determined during open. */
-    unsigned        uImageFlags;
-    /** Total size of the image. */
-    uint64_t        cbSize;
-
-    /** Physical geometry of this image. */
-    VDGEOMETRY      PCHSGeometry;
-    /** Logical geometry of this image. */
-    VDGEOMETRY      LCHSGeometry;
-
-    /** Image UUID. */
-    RTUUID          ImageUuid;
-    /** Parent image UUID. */
-    RTUUID          ParentUuid;
-
-    /** Parent's time stamp at the time of image creation. */
-    uint32_t        u32ParentTimeStamp;
-    /** Relative path to the parent image. */
-    char            *pszParentFilename;
-
-    /** The Block Allocation Table. */
-    uint32_t        *pBlockAllocationTable;
-    /** Number of entries in the table. */
-    uint32_t        cBlockAllocationTableEntries;
-
-    /** Size of one data block. */
-    uint32_t        cbDataBlock;
-    /** Sectors per data block. */
-    uint32_t        cSectorsPerDataBlock;
-    /** Length of the sector bitmap in bytes. */
-    uint32_t        cbDataBlockBitmap;
-    /** A copy of the disk footer. */
-    VHDFooter       vhdFooterCopy;
-    /** Current end offset of the file (without the disk footer). */
-    uint64_t        uCurrentEndOfFile;
-    /** Size of the data block bitmap in sectors. */
-    uint32_t        cDataBlockBitmapSectors;
-    /** Start of the block allocation table. */
-    uint64_t        uBlockAllocationTableOffset;
-    /** Buffer to hold block's bitmap for bit search operations. */
-    uint8_t         *pu8Bitmap;
-    /** Offset to the next data structure (dynamic disk header). */
-    uint64_t        u64DataOffset;
-    /** Flag to force dynamic disk header update. */
-    bool            fDynHdrNeedsUpdate;
-} VHDIMAGE, *PVHDIMAGE;
-
-/**
- * Structure tracking the expansion process of the image
- * for async access.
- */
-typedef struct VHDIMAGEEXPAND
-{
-    /** Flag indicating the status of each step. */
-    volatile uint32_t fFlags;
-    /** The index in the  block allocation table which is written. */
-    uint32_t          idxBatAllocated;
-    /** Big endian representation of the block index
-     * which is written in the BAT. */
-    uint32_t          idxBlockBe;
-    /** Old end of the file - used for rollback in case of an error. */
-    uint64_t          cbEofOld;
-    /** Sector bitmap written to the new block - variable in size. */
-    uint8_t           au8Bitmap[1];
-} VHDIMAGEEXPAND, *PVHDIMAGEEXPAND;
-
-/**
- * Flag defines
- */
-#define VHDIMAGEEXPAND_STEP_IN_PROGRESS (0x0)
-#define VHDIMAGEEXPAND_STEP_FAILED      (0x2)
-#define VHDIMAGEEXPAND_STEP_SUCCESS     (0x3)
-/** All steps completed successfully. */
-#define VHDIMAGEEXPAND_ALL_SUCCESS      (0xff)
-/** All steps completed (no success indicator) */
-#define VHDIMAGEEXPAND_ALL_COMPLETE     (0xaa)
-
-/** Every status field has 2 bits so we can encode 4 steps in one byte. */
-#define VHDIMAGEEXPAND_STATUS_MASK              0x03
-#define VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT 0x00
-#define VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT   0x02
-#define VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT      0x04
-#define VHDIMAGEEXPAND_BAT_STATUS_SHIFT         0x06
-
-/**
- * Helper macros to get and set the status field.
- */
-#define VHDIMAGEEXPAND_STATUS_GET(fFlags, cShift) \
-    (((fFlags) >> (cShift)) & VHDIMAGEEXPAND_STATUS_MASK)
-#define VHDIMAGEEXPAND_STATUS_SET(fFlags, cShift, uVal) \
-    ASMAtomicOrU32(&(fFlags), ((uVal) & VHDIMAGEEXPAND_STATUS_MASK) << (cShift))
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aVhdFileExtensions[] =
-{
-    {"vhd", VDTYPE_HDD},
-    {NULL, VDTYPE_INVALID}
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) vhdError(PVHDIMAGE pImage, int rc, RT_SRC_POS_DECL,
-                         const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) vhdMessage(PVHDIMAGE pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-
-DECLINLINE(int) vhdFileOpen(PVHDIMAGE pImage, const char *pszFilename,
-                            uint32_t fOpen)
-{
-    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                  pszFilename, fOpen,
-                                                  &pImage->pStorage);
-}
-
-DECLINLINE(int) vhdFileClose(PVHDIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                   pImage->pStorage);
-}
-
-DECLINLINE(int) vhdFileDelete(PVHDIMAGE pImage, const char *pszFilename)
-{
-    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                    pszFilename);
-}
-
-DECLINLINE(int) vhdFileMove(PVHDIMAGE pImage, const char *pszSrc,
-                            const char *pszDst, unsigned fMove)
-{
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-DECLINLINE(int) vhdFileGetFreeSpace(PVHDIMAGE pImage, const char *pszFilename,
-                                    int64_t *pcbFree)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
-                                                          pszFilename, pcbFree);
-}
-
-DECLINLINE(int) vhdFileGetModificationTime(PVHDIMAGE pImage,
-                                           const char *pszFilename,
-                                           PRTTIMESPEC pModificationTime)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
-                                                                 pszFilename,
-                                                                 pModificationTime);
-}
-
-DECLINLINE(int) vhdFileGetSize(PVHDIMAGE pImage, uint64_t *pcbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, pcbSize);
-}
-
-DECLINLINE(int) vhdFileSetSize(PVHDIMAGE pImage, uint64_t cbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pImage->pStorage, cbSize);
-}
-
-DECLINLINE(int) vhdFileWriteSync(PVHDIMAGE pImage, uint64_t uOffset,
-                                 const void *pvBuffer, size_t cbBuffer,
-                                 size_t *pcbWritten)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage, uOffset,
-                                                       pvBuffer, cbBuffer, pcbWritten);
-}
-
-DECLINLINE(int) vhdFileReadSync(PVHDIMAGE pImage, uint64_t uOffset,
-                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pImage->pStorage, uOffset,
-                                                      pvBuffer, cbBuffer, pcbRead);
-}
-
-DECLINLINE(int) vhdFileFlushSync(PVHDIMAGE pImage)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pImage->pStorage);
-}
-
-DECLINLINE(int) vhdFileReadUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
-                                     PVDIOCTX pIoCtx, size_t cbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
-                                                           pImage->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-DECLINLINE(int) vhdFileWriteUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
-                                      PVDIOCTX pIoCtx, size_t cbWrite,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) vhdFileReadMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
-                                     void *pvBuffer, size_t cbBuffer,
-                                     PVDIOCTX pIoCtx, PPVDMETAXFER ppMetaXfer,
-                                     PFNVDXFERCOMPLETED pfnComplete,
-                                     void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                           pImage->pStorage,
-                                                           uOffset, pvBuffer,
-                                                           cbBuffer, pIoCtx,
-                                                           ppMetaXfer,
-                                                           pfnComplete,
-                                                           pvCompleteUser);
-}
-
-DECLINLINE(int) vhdFileWriteMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
-                                      void *pvBuffer, size_t cbBuffer,
-                                      PVDIOCTX pIoCtx,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                            pImage->pStorage,
-                                                            uOffset, pvBuffer,
-                                                            cbBuffer, pIoCtx,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-DECLINLINE(int) vhdFileFlushAsync(PVHDIMAGE pImage, PVDIOCTX pIoCtx,
-                                  PFNVDXFERCOMPLETED pfnComplete,
-                                  void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                        pImage->pStorage,
-                                                        pIoCtx, pfnComplete,
-                                                        pvCompleteUser);
-}
-
-DECLINLINE(void) vhdFileMetaXferRelease(PVHDIMAGE pImage, PVDMETAXFER pMetaXfer)
-{
-    pImage->pInterfaceIOCallbacks->pfnMetaXferRelease(pImage->pInterfaceIO->pvUser,
-                                                      pMetaXfer);
-}
-
-
-/**
- * Internal: Compute and update header checksum.
- */
-static uint32_t vhdChecksum(void *pHeader, uint32_t cbSize)
-{
-    uint32_t checksum = 0;
-    for (uint32_t i = 0; i < cbSize; i++)
-        checksum += ((unsigned char *)pHeader)[i];
-    return ~checksum;
-}
-
-/**
- * Internal: Convert filename to UTF16 with appropriate endianness.
- */
-static int vhdFilenameToUtf16(const char *pszFilename, uint16_t *pu16Buf,
-                              uint32_t cbBufSize, uint32_t *pcbActualSize,
-                              bool fBigEndian)
-{
-    int      rc;
-    PRTUTF16 tmp16 = NULL;
-    size_t   cTmp16Len;
-
-    rc = RTStrToUtf16(pszFilename, &tmp16);
-    if (RT_FAILURE(rc))
-        goto out;
-    cTmp16Len = RTUtf16Len(tmp16);
-    if (cTmp16Len * sizeof(*tmp16) > cbBufSize)
-    {
-        rc = VERR_FILENAME_TOO_LONG;
-        goto out;
-    }
-
-    if (fBigEndian)
-        for (unsigned i = 0; i < cTmp16Len; i++)
-            pu16Buf[i] = RT_H2BE_U16(tmp16[i]);
-    else
-        memcpy(pu16Buf, tmp16, cTmp16Len * sizeof(*tmp16));
-    if (pcbActualSize)
-        *pcbActualSize = (uint32_t)(cTmp16Len * sizeof(*tmp16));
-
-out:
-    if (tmp16)
-        RTUtf16Free(tmp16);
-    return rc;
-}
-
-/**
- * Internal: Update one locator entry.
- */
-static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszFilename)
-{
-    int      rc;
-    uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE;
-    void     *pvBuf = RTMemTmpAllocZ(cbMaxLen);
-    char     *pszTmp;
-
-    if (!pvBuf)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    switch (RT_BE2H_U32(pLocator->u32Code))
-    {
-        case VHD_PLATFORM_CODE_WI2R:
-            /* Update plain relative name. */
-            cb = (uint32_t)strlen(pszFilename);
-            if (cb > cbMaxLen)
-            {
-                rc = VERR_FILENAME_TOO_LONG;
-                goto out;
-            }
-            memcpy(pvBuf, pszFilename, cb);
-            pLocator->u32DataLength = RT_H2BE_U32(cb);
-            break;
-        case VHD_PLATFORM_CODE_WI2K:
-            /* Update plain absolute name. */
-            rc = RTPathAbs(pszFilename, (char *)pvBuf, cbMaxLen);
-            if (RT_FAILURE(rc))
-                goto out;
-            pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
-            break;
-        case VHD_PLATFORM_CODE_W2RU:
-            /* Update unicode relative name. */
-            rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
-            if (RT_FAILURE(rc))
-                goto out;
-            pLocator->u32DataLength = RT_H2BE_U32(cb);
-            break;
-        case VHD_PLATFORM_CODE_W2KU:
-            /* Update unicode absolute name. */
-            pszTmp = (char*)RTMemTmpAllocZ(cbMaxLen);
-            if (!pszTmp)
-            {
-                rc = VERR_NO_MEMORY;
-                goto out;
-            }
-            rc = RTPathAbs(pszFilename, pszTmp, cbMaxLen);
-            if (RT_FAILURE(rc))
-            {
-                RTMemTmpFree(pszTmp);
-                goto out;
-            }
-            rc = vhdFilenameToUtf16(pszTmp, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
-            RTMemTmpFree(pszTmp);
-            if (RT_FAILURE(rc))
-                goto out;
-            pLocator->u32DataLength = RT_H2BE_U32(cb);
-            break;
-        default:
-            rc = VERR_NOT_IMPLEMENTED;
-            goto out;
-    }
-    rc = vhdFileWriteSync(pImage, RT_BE2H_U64(pLocator->u64DataOffset),
-                          pvBuf, RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE,
-                          NULL);
-
-out:
-    if (pvBuf)
-        RTMemTmpFree(pvBuf);
-    return rc;
-}
-
-/**
- * Internal: Update dynamic disk header from VHDIMAGE.
- */
-static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
-{
-    VHDDynamicDiskHeader ddh;
-    int rc, i;
-
-    if (!pImage)
-        return VERR_VD_NOT_OPENED;
-
-    rc = vhdFileReadSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    if (memcmp(ddh.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE) != 0)
-        return VERR_VD_VHD_INVALID_HEADER;
-
-    uint32_t u32Checksum = RT_BE2H_U32(ddh.Checksum);
-    ddh.Checksum = 0;
-    if (u32Checksum != vhdChecksum(&ddh, sizeof(ddh)))
-        return VERR_VD_VHD_INVALID_HEADER;
-
-    /* Update parent's timestamp. */
-    ddh.ParentTimeStamp = RT_H2BE_U32(pImage->u32ParentTimeStamp);
-    /* Update parent's filename. */
-    if (pImage->pszParentFilename)
-    {
-        rc = vhdFilenameToUtf16(RTPathFilename(pImage->pszParentFilename),
-             ddh.ParentUnicodeName, sizeof(ddh.ParentUnicodeName) - 1, NULL, true);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    /* Update parent's locators. */
-    for (i = 0; i < VHD_MAX_LOCATOR_ENTRIES; i++)
-    {
-        /* Skip empty locators */
-        if (ddh.ParentLocatorEntry[i].u32Code != RT_H2BE_U32(VHD_PLATFORM_CODE_NONE))
-        {
-            if (pImage->pszParentFilename)
-            {
-                rc = vhdLocatorUpdate(pImage, &ddh.ParentLocatorEntry[i], pImage->pszParentFilename);
-                if (RT_FAILURE(rc))
-                    return rc;
-            }
-            else
-            {
-                /* The parent was deleted. */
-                ddh.ParentLocatorEntry[i].u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_NONE);
-            }
-        }
-    }
-    /* Update parent's UUID */
-    memcpy(ddh.ParentUuid, pImage->ParentUuid.au8, sizeof(ddh.ParentUuid));
-
-    /* Update data offset and number of table entries. */
-    ddh.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
-
-    ddh.Checksum = 0;
-    ddh.Checksum = RT_H2BE_U32(vhdChecksum(&ddh, sizeof(ddh)));
-    rc = vhdFileWriteSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
-    return rc;
-}
-
-/**
- * Internal: Update the VHD footer.
- */
-static int vhdUpdateFooter(PVHDIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Update fields which can change. */
-    pImage->vhdFooterCopy.CurSize              = RT_H2BE_U64(pImage->cbSize);
-    pImage->vhdFooterCopy.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
-    pImage->vhdFooterCopy.DiskGeometryHeads    = pImage->PCHSGeometry.cHeads;
-    pImage->vhdFooterCopy.DiskGeometrySectors  = pImage->PCHSGeometry.cSectors;
-
-    pImage->vhdFooterCopy.Checksum = 0;
-    pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
-
-    if (pImage->pBlockAllocationTable)
-        rc = vhdFileWriteSync(pImage, 0, &pImage->vhdFooterCopy,
-                              sizeof(VHDFooter), NULL);
-
-    if (RT_SUCCESS(rc))
-        rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
-                              &pImage->vhdFooterCopy, sizeof(VHDFooter), NULL);
-
-    return rc;
-}
-
-/**
- * Internal. Flush image data to disk.
- */
-static int vhdFlushImage(PVHDIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        return VINF_SUCCESS;
-
-    if (pImage->pBlockAllocationTable)
-    {
-        /*
-         * This is an expanding image. Write the BAT and copy of the disk footer.
-         */
-        size_t   cbBlockAllocationTableToWrite = pImage->cBlockAllocationTableEntries * sizeof(uint32_t);
-        uint32_t *pBlockAllocationTableToWrite = (uint32_t *)RTMemAllocZ(cbBlockAllocationTableToWrite);
-
-        if (!pBlockAllocationTableToWrite)
-            return VERR_NO_MEMORY;
-
-        /*
-         * The BAT entries have to be stored in big endian format.
-         */
-        for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
-            pBlockAllocationTableToWrite[i] = RT_H2BE_U32(pImage->pBlockAllocationTable[i]);
-
-        /*
-         * Write the block allocation table after the copy of the disk footer and the dynamic disk header.
-         */
-        vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
-                         pBlockAllocationTableToWrite,
-                         cbBlockAllocationTableToWrite, NULL);
-        if (pImage->fDynHdrNeedsUpdate)
-            rc = vhdDynamicHeaderUpdate(pImage);
-        RTMemFree(pBlockAllocationTableToWrite);
-    }
-
-    if (RT_SUCCESS(rc))
-        rc = vhdUpdateFooter(pImage);
-
-    if (RT_SUCCESS(rc))
-        rc = vhdFileFlushSync(pImage);
-
-    return rc;
-}
-
-/**
- * Internal. Free all allocated space for representing an image except pImage,
- * and optionally delete the image from disk.
- */
-static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (pImage->pStorage)
-        {
-            /* No point updating the file that is deleted anyway. */
-            if (!fDelete)
-                vhdFlushImage(pImage);
-
-            vhdFileClose(pImage);
-            pImage->pStorage = NULL;
-        }
-
-        if (pImage->pszParentFilename)
-        {
-            RTStrFree(pImage->pszParentFilename);
-            pImage->pszParentFilename = NULL;
-        }
-        if (pImage->pBlockAllocationTable)
-        {
-            RTMemFree(pImage->pBlockAllocationTable);
-            pImage->pBlockAllocationTable = NULL;
-        }
-        if (pImage->pu8Bitmap)
-        {
-            RTMemFree(pImage->pu8Bitmap);
-            pImage->pu8Bitmap = NULL;
-        }
-
-        if (fDelete && pImage->pszFilename)
-            rc = vhdFileDelete(pImage, pImage->pszFilename);
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/* 946684800 is the number of seconds between 1/1/1970 and 1/1/2000 */
-#define VHD_TO_UNIX_EPOCH_SECONDS UINT64_C(946684800)
-
-static uint32_t vhdRtTime2VhdTime(PCRTTIMESPEC pRtTimeStamp)
-{
-    uint64_t u64Seconds = RTTimeSpecGetSeconds(pRtTimeStamp);
-    return (uint32_t)(u64Seconds - VHD_TO_UNIX_EPOCH_SECONDS);
-}
-
-static void vhdTime2RtTime(PRTTIMESPEC pRtTimeStamp, uint32_t u32VhdTimeStamp)
-{
-    RTTimeSpecSetSeconds(pRtTimeStamp, VHD_TO_UNIX_EPOCH_SECONDS + u32VhdTimeStamp);
-}
-
-/**
- * Internal: Allocates the block bitmap rounding up to the next 32bit or 64bit boundary.
- *           Can be freed with RTMemFree. The memory is zeroed.
- */
-DECLINLINE(uint8_t *)vhdBlockBitmapAllocate(PVHDIMAGE pImage)
-{
-#ifdef RT_ARCH_AMD64
-    return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 8);
-#else
-    return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 4);
-#endif
-}
-
-/**
- * Internal: called when the async expansion process completed (failure or success).
- *           Will do the necessary rollback if an error occurred.
- */
-static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAGEEXPAND pExpand)
-{
-    int rc = VINF_SUCCESS;
-    uint32_t fFlags = ASMAtomicReadU32(&pExpand->fFlags);
-    bool fIoInProgress = false;
-
-    /* Quick path, check if everything succeeded. */
-    if (fFlags == VHDIMAGEEXPAND_ALL_SUCCESS)
-    {
-        RTMemFree(pExpand);
-    }
-    else
-    {
-        uint32_t uStatus;
-
-        uStatus = VHDIMAGEEXPAND_STATUS_GET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
-        if (   uStatus == VHDIMAGEEXPAND_STEP_FAILED
-            || uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
-        {
-            /* Undo and restore the old value. */
-            pImage->pBlockAllocationTable[pExpand->idxBatAllocated] = ~0U;
-
-            /* Restore the old value on the disk.
-             * No need for a completion callback because we can't
-             * do anything if this fails. */
-            if (uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
-            {
-                rc = vhdFileWriteMetaAsync(pImage,
-                                             pImage->uBlockAllocationTableOffset
-                                           + pExpand->idxBatAllocated * sizeof(uint32_t),
-                                           &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
-                                           sizeof(uint32_t), pIoCtx, NULL, NULL);
-                fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
-            }
-        }
-
-        /* Restore old size (including the footer because another application might
-         * fill up the free space making it impossible to add the footer)
-         * and add the footer at the right place again. */
-        rc = vhdFileSetSize(pImage, pExpand->cbEofOld + sizeof(VHDFooter));
-        AssertRC(rc);
-
-        pImage->uCurrentEndOfFile = pExpand->cbEofOld;
-        rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
-                                   &pImage->vhdFooterCopy, sizeof(VHDFooter),
-                                   pIoCtx, NULL, NULL);
-        fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
-    }
-
-    return fIoInProgress ? VERR_VD_ASYNC_IO_IN_PROGRESS : rc;
-}
-
-static int vhdAsyncExpansionStepCompleted(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq, unsigned iStep)
-{
-   PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-   PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)pvUser;
-
-   LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc iStep=%u\n",
-                pBackendData, pIoCtx, pvUser, rcReq, iStep));
-
-   if (RT_SUCCESS(rcReq))
-       VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_SUCCESS);
-   else
-       VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_FAILED);
-
-   if ((pExpand->fFlags & VHDIMAGEEXPAND_ALL_COMPLETE) == VHDIMAGEEXPAND_ALL_COMPLETE)
-       return vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
-
-   return VERR_VD_ASYNC_IO_IN_PROGRESS;
-}
-
-static int vhdAsyncExpansionDataBlockBitmapComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
-{
-    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT);
-}
-
-static int vhdAsyncExpansionDataComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
-{
-    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT);
-}
-
-static int vhdAsyncExpansionBatUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
-{
-    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
-}
-
-static int vhdAsyncExpansionFooterUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
-{
-    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT);
-}
-
-static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffset)
-{
-    VHDDynamicDiskHeader vhdDynamicDiskHeader;
-    int rc = VINF_SUCCESS;
-    uint32_t *pBlockAllocationTable;
-    uint64_t uBlockAllocationTableOffset;
-    unsigned i = 0;
-
-    Log(("Open a dynamic disk.\n"));
-
-    /*
-     * Read the dynamic disk header.
-     */
-    rc = vhdFileReadSync(pImage, uDynamicDiskHeaderOffset,
-                         &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader),
-                         NULL);
-    if (memcmp(vhdDynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE))
-        return VERR_INVALID_PARAMETER;
-
-    pImage->cbDataBlock = RT_BE2H_U32(vhdDynamicDiskHeader.BlockSize);
-    LogFlowFunc(("BlockSize=%u\n", pImage->cbDataBlock));
-    pImage->cBlockAllocationTableEntries = RT_BE2H_U32(vhdDynamicDiskHeader.MaxTableEntries);
-    LogFlowFunc(("MaxTableEntries=%lu\n", pImage->cBlockAllocationTableEntries));
-    AssertMsg(!(pImage->cbDataBlock % VHD_SECTOR_SIZE), ("%s: Data block size is not a multiple of %!\n", __FUNCTION__, VHD_SECTOR_SIZE));
-
-    pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
-    LogFlowFunc(("SectorsPerDataBlock=%u\n", pImage->cSectorsPerDataBlock));
-
-    /*
-     * Every block starts with a bitmap indicating which sectors are valid and which are not.
-     * We store the size of it to be able to calculate the real offset.
-     */
-    pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
-    pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
-    /* Round up to full sector size */
-    if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
-        pImage->cDataBlockBitmapSectors++;
-    LogFlowFunc(("cbDataBlockBitmap=%u\n", pImage->cbDataBlockBitmap));
-    LogFlowFunc(("cDataBlockBitmapSectors=%u\n", pImage->cDataBlockBitmapSectors));
-
-    pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
-    if (!pImage->pu8Bitmap)
-        return VERR_NO_MEMORY;
-
-    pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
-    if (!pBlockAllocationTable)
-        return VERR_NO_MEMORY;
-
-    /*
-     * Read the table.
-     */
-    uBlockAllocationTableOffset = RT_BE2H_U64(vhdDynamicDiskHeader.TableOffset);
-    LogFlowFunc(("uBlockAllocationTableOffset=%llu\n", uBlockAllocationTableOffset));
-    pImage->uBlockAllocationTableOffset = uBlockAllocationTableOffset;
-    rc = vhdFileReadSync(pImage, uBlockAllocationTableOffset,
-                         pBlockAllocationTable,
-                         pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
-                         NULL);
-
-    /*
-     * Because the offset entries inside the allocation table are stored big endian
-     * we need to convert them into host endian.
-     */
-    pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
-    if (!pImage->pBlockAllocationTable)
-    {
-        RTMemFree(pBlockAllocationTable);
-        return VERR_NO_MEMORY;
-    }
-
-    for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
-        pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
-
-    RTMemFree(pBlockAllocationTable);
-
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
-        memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
-
-    return rc;
-}
-
-static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
-{
-    uint64_t FileSize;
-    VHDFooter vhdFooter;
-
-    pImage->uOpenFlags = uOpenFlags;
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the image.
-     */
-    int rc = vhdFileOpen(pImage, pImage->pszFilename,
-                         VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                    false /* fCreate */));
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        return rc;
-    }
-
-    rc = vhdFileGetSize(pImage, &FileSize);
-    pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
-
-    rc = vhdFileReadSync(pImage, pImage->uCurrentEndOfFile,
-                         &vhdFooter, sizeof(VHDFooter), NULL);
-    if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
-        return VERR_VD_VHD_INVALID_HEADER;
-
-    switch (RT_BE2H_U32(vhdFooter.DiskType))
-    {
-        case VHD_FOOTER_DISK_TYPE_FIXED:
-            {
-                pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-            }
-            break;
-        case VHD_FOOTER_DISK_TYPE_DYNAMIC:
-            {
-                pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
-            }
-            break;
-        case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
-            {
-                pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
-                pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
-            }
-            break;
-        default:
-            return VERR_NOT_IMPLEMENTED;
-    }
-
-    pImage->cbSize       = RT_BE2H_U64(vhdFooter.CurSize);
-    pImage->LCHSGeometry.cCylinders   = 0;
-    pImage->LCHSGeometry.cHeads       = 0;
-    pImage->LCHSGeometry.cSectors     = 0;
-    pImage->PCHSGeometry.cCylinders   = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
-    pImage->PCHSGeometry.cHeads       = vhdFooter.DiskGeometryHeads;
-    pImage->PCHSGeometry.cSectors     = vhdFooter.DiskGeometrySectors;
-
-    /*
-     * Copy of the disk footer.
-     * If we allocate new blocks in differencing disks on write access
-     * the footer is overwritten. We need to write it at the end of the file.
-     */
-    memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
-
-    /*
-     * Is there a better way?
-     */
-    memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
-
-    pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
-    LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
-
-    if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
-
-    if (RT_FAILURE(rc))
-        vhdFreeImage(pImage, false);
-    return rc;
-}
-
-/**
- * Internal: Checks if a sector in the block bitmap is set
- */
-DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
-{
-    uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
-
-    /*
-     * The index of the bit in the byte of the data block bitmap.
-     * The most significant bit stands for a lower sector number.
-     */
-    uint8_t  iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
-    uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
-
-    AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
-                ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
-
-    return ASMBitTest(puBitmap, iBitInByte);
-}
-
-/**
- * Internal: Sets the given sector in the sector bitmap.
- */
-DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
-{
-    uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
-
-    /*
-     * The index of the bit in the byte of the data block bitmap.
-     * The most significant bit stands for a lower sector number.
-     */
-    uint8_t  iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
-    uint8_t  *puBitmap  = pu8Bitmap + iBitmap;
-
-    AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
-                ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
-
-    return !ASMBitTestAndSet(puBitmap, iBitInByte);
-}
-
-/**
- * Internal: Derive drive geometry from its size.
- */
-static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
-{
-    uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
-    uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
-
-    if (u64TotalSectors > 65535 * 16 * 255)
-    {
-        /* ATA disks limited to 127 GB. */
-        u64TotalSectors = 65535 * 16 * 255;
-    }
-
-    if (u64TotalSectors >= 65535 * 16 * 63)
-    {
-        u32SectorsPerTrack    = 255;
-        u32Heads              = 16;
-        u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
-    }
-    else
-    {
-        u32SectorsPerTrack    = 17;
-        u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
-
-        u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
-
-        if (u32Heads < 4)
-        {
-            u32Heads = 4;
-        }
-        if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
-        {
-            u32SectorsPerTrack    = 31;
-            u32Heads              = 16;
-            u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
-        }
-        if (u32CylinderTimesHeads >= (u32Heads * 1024))
-        {
-            u32SectorsPerTrack    = 63;
-            u32Heads              = 16;
-            u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
-        }
-    }
-    pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
-    pImage->PCHSGeometry.cHeads     = u32Heads;
-    pImage->PCHSGeometry.cSectors   = u32SectorsPerTrack;
-    pImage->LCHSGeometry.cCylinders = 0;
-    pImage->LCHSGeometry.cHeads     = 0;
-    pImage->LCHSGeometry.cSectors   = 0;
-}
-
-
-static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
-{
-    PVHDPLE pLocator = pDDH->ParentLocatorEntry;
-    /* Relative Windows path. */
-    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
-    pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
-    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
-    u64Offset += VHD_RELATIVE_MAX_PATH;
-    pLocator++;
-    /* Absolute Windows path. */
-    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
-    pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
-    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
-    u64Offset += VHD_ABSOLUTE_MAX_PATH;
-    pLocator++;
-    /* Unicode relative Windows path. */
-    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
-    pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
-    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
-    u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
-    pLocator++;
-    /* Unicode absolute Windows path. */
-    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
-    pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
-    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
-    return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
-}
-
-/**
- * Internal: Additional code for dynamic VHD image creation.
- */
-static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
-{
-    int rc;
-    VHDDynamicDiskHeader DynamicDiskHeader;
-    uint32_t u32BlockAllocationTableSectors;
-    void    *pvTmp = NULL;
-
-    memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
-
-    pImage->u64DataOffset           = sizeof(VHDFooter);
-    pImage->cbDataBlock             = VHD_BLOCK_SIZE; /* 2 MB */
-    pImage->cSectorsPerDataBlock    = pImage->cbDataBlock / VHD_SECTOR_SIZE;
-    pImage->cbDataBlockBitmap       = pImage->cSectorsPerDataBlock / 8;
-    pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
-    /* Align to sector boundary */
-    if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
-        pImage->cDataBlockBitmapSectors++;
-    pImage->pu8Bitmap               = vhdBlockBitmapAllocate(pImage);
-    if (!pImage->pu8Bitmap)
-        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
-
-    /* Initialize BAT. */
-    pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
-    pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
-    u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
-    pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
-    if (!pImage->pBlockAllocationTable)
-        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
-
-    for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
-    {
-        pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
-    }
-
-    /* Round up to the sector size. */
-    if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
-        pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
-                                                              pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
-    else
-        pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
-
-    /* Set dynamic image size. */
-    pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
-    if (!pvTmp)
-        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
-
-    rc = vhdFileWriteSync(pImage, 0, pvTmp,
-                          pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
-    if (RT_FAILURE(rc))
-    {
-        RTMemTmpFree(pvTmp);
-        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
-    }
-
-    RTMemTmpFree(pvTmp);
-
-    /* Initialize and write the dynamic disk header. */
-    memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
-    DynamicDiskHeader.DataOffset      = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
-    DynamicDiskHeader.TableOffset     = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
-    DynamicDiskHeader.HeaderVersion   = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
-    DynamicDiskHeader.BlockSize       = RT_H2BE_U32(pImage->cbDataBlock);
-    DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
-    /* Compute and update checksum. */
-    DynamicDiskHeader.Checksum = 0;
-    DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
-
-    rc = vhdFileWriteSync(pImage, sizeof(VHDFooter), &DynamicDiskHeader,
-                          sizeof(DynamicDiskHeader), NULL);
-    if (RT_FAILURE(rc))
-        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
-
-    /* Write BAT. */
-    rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
-                          pImage->pBlockAllocationTable,
-                          pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
-                          NULL);
-    if (RT_FAILURE(rc))
-        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
-
-    return rc;
-}
-
-/**
- * Internal: The actual code for VHD image creation, both fixed and dynamic.
- */
-static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
-                          unsigned uImageFlags, const char *pszComment,
-                          PCVDGEOMETRY pPCHSGeometry,
-                          PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
-                          unsigned uOpenFlags,
-                          PFNVDPROGRESS pfnProgress, void *pvUser,
-                          unsigned uPercentStart, unsigned uPercentSpan)
-{
-    int rc;
-    VHDFooter Footer;
-    RTTIMESPEC now;
-
-    pImage->uOpenFlags = uOpenFlags;
-    pImage->uImageFlags = uImageFlags;
-
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    rc = vhdFileOpen(pImage, pImage->pszFilename,
-                     VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                                true /* fCreate */));
-    if (RT_FAILURE(rc))
-        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
-
-
-    pImage->cbSize = cbSize;
-    pImage->ImageUuid = *pUuid;
-    RTUuidClear(&pImage->ParentUuid);
-    vhdSetDiskGeometry(pImage, cbSize);
-
-    /* Initialize the footer. */
-    memset(&Footer, 0, sizeof(Footer));
-    memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
-    Footer.Features = RT_H2BE_U32(0x2);
-    Footer.Version  = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
-    Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
-    memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
-    Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
-#ifdef RT_OS_DARWIN
-    Footer.CreatorOS  = RT_H2BE_U32(0x4D616320); /* "Mac " */
-#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
-    Footer.CreatorOS  = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
-#endif
-    Footer.OrigSize   = RT_H2BE_U64(cbSize);
-    Footer.CurSize    = Footer.OrigSize;
-    Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
-    Footer.DiskGeometryHeads    = pImage->PCHSGeometry.cHeads;
-    Footer.DiskGeometrySectors  = pImage->PCHSGeometry.cSectors;
-    memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
-    Footer.SavedState = 0;
-
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        Footer.DiskType   = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
-        /*
-         * Initialize fixed image.
-         * "The size of the entire file is the size of the hard disk in
-         * the guest operating system plus the size of the footer."
-         */
-        pImage->u64DataOffset     = VHD_FOOTER_DATA_OFFSET_FIXED;
-        pImage->uCurrentEndOfFile = cbSize;
-        /** @todo r=klaus replace this with actual data writes, see the experience
-         * with VDI files on Windows, can cause long freezes when writing. */
-        rc = vhdFileSetSize(pImage, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
-        if (RT_FAILURE(rc))
-        {
-            vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
-            goto out;
-        }
-    }
-    else
-    {
-        /*
-         * Initialize dynamic image.
-         *
-         * The overall structure of dynamic disk is:
-         *
-         * [Copy of hard disk footer (512 bytes)]
-         * [Dynamic disk header (1024 bytes)]
-         * [BAT (Block Allocation Table)]
-         * [Parent Locators]
-         * [Data block 1]
-         * [Data block 2]
-         * ...
-         * [Data block N]
-         * [Hard disk footer (512 bytes)]
-         */
-        Footer.DiskType   = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
-                              ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
-                              : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
-        /* We are half way thorough with creation of image, let the caller know. */
-        if (pfnProgress)
-            pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
-
-        rc = vhdCreateDynamicImage(pImage, cbSize);
-        if (RT_FAILURE(rc))
-            goto out;
-    }
-
-    Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
-
-    /* Compute and update the footer checksum. */
-    Footer.Checksum = 0;
-    Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
-
-    pImage->vhdFooterCopy = Footer;
-
-    /* Store the footer */
-    rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, &Footer,
-                          sizeof(Footer), NULL);
-    if (RT_FAILURE(rc))
-    {
-        vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Dynamic images contain a copy of the footer at the very beginning of the file. */
-    if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-    {
-        /* Write the copy of the footer. */
-        rc = vhdFileWriteSync(pImage, 0, &Footer, sizeof(Footer), NULL);
-        if (RT_FAILURE(rc))
-        {
-            vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
-            goto out;
-        }
-    }
-
-out:
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan);
-
-    if (RT_FAILURE(rc))
-        vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
-    return rc;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
-    int rc;
-    PVDIOSTORAGE pStorage;
-    uint64_t cbFile;
-    VHDFooter vhdFooter;
-
-    /* Get I/O interface. */
-    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
-    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
-    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
-                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
-                                                                   false /* fCreate */),
-                                        &pStorage);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
-                                           &cbFile);
-    if (RT_FAILURE(rc))
-    {
-        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-        rc = VERR_VD_VHD_INVALID_HEADER;
-        goto out;
-    }
-
-    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
-                                            cbFile - sizeof(VHDFooter),
-                                            &vhdFooter, sizeof(VHDFooter), NULL);
-    if (RT_FAILURE(rc) || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
-        rc = VERR_VD_VHD_INVALID_HEADER;
-    else
-    {
-        *penmType = VDTYPE_HDD;
-        rc = VINF_SUCCESS;
-    }
-
-    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int vhdOpen(const char *pszFilename, unsigned uOpenFlags,
-                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                   VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = vhdOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pImage;
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int vhdCreate(const char *pszFilename, uint64_t cbSize,
-                     unsigned uImageFlags, const char *pszComment,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     PCRTUUID pUuid, unsigned uOpenFlags,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        return rc;
-    }
-
-    /* @todo Check the values of other params */
-
-    pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        return rc;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pStorage = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIO)))
-    {
-        RTMemFree(pImage);
-        return VERR_INVALID_PARAMETER;
-    }
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIOCallbacks)))
-    {
-        RTMemFree(pImage);
-        return VERR_INVALID_PARAMETER;
-    }
-
-    rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
-                        pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
-                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
-
-    if (RT_SUCCESS(rc))
-    {
-        /* So far the image is opened in read/write mode. Make sure the
-         * image is opened in read-only mode if the caller requested that. */
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            vhdFreeImage(pImage, false);
-            rc = vhdOpenImage(pImage, uOpenFlags);
-            if (RT_FAILURE(rc))
-            {
-                RTMemFree(pImage);
-                goto out;
-            }
-        }
-        *ppBackendData = pImage;
-    }
-    else
-        RTMemFree(pImage);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int vhdRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    /* Check arguments. */
-    if (   !pImage
-        || !pszFilename
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Close the image. */
-    rc = vhdFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Rename the file. */
-    rc = vhdFileMove(pImage, pImage->pszFilename, pszFilename, 0);
-    if (RT_FAILURE(rc))
-    {
-        /* The move failed, try to reopen the original image. */
-        int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
-        if (RT_FAILURE(rc2))
-            rc = rc2;
-
-        goto out;
-    }
-
-    /* Update pImage with the new information. */
-    pImage->pszFilename = pszFilename;
-
-    /* Open the old file with new name. */
-    rc = vhdOpenImage(pImage, pImage->uOpenFlags);
-    if (RT_FAILURE(rc))
-        goto out;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int vhdClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    rc = vhdFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                   size_t cbBuf, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%p uOffset=%#llx pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    if (uOffset + cbBuf > pImage->cbSize)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /*
-     * If we have a dynamic disk image, we need to find the data block and sector to read.
-     */
-    if (pImage->pBlockAllocationTable)
-    {
-        /*
-         * Get the data block first.
-         */
-        uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
-        uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
-        uint64_t uVhdOffset;
-
-        LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
-        LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
-
-        /*
-         * If the block is not allocated the content of the entry is ~0
-         */
-        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
-        {
-            /* Return block size as read. */
-            *pcbActuallyRead = RT_MIN(cbBuf, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
-            rc = VERR_VD_BLOCK_FREE;
-            goto out;
-        }
-
-        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
-        LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
-
-        /*
-         * Clip read range to remain in this data block.
-         */
-        cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
-        /* Read in the block's bitmap. */
-        rc = vhdFileReadSync(pImage,
-                             ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                             pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
-                             NULL);
-        if (RT_SUCCESS(rc))
-        {
-            uint32_t cSectors = 0;
-
-            if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-            {
-                cBATEntryIndex++;
-                cSectors = 1;
-
-                /*
-                 * The first sector being read is marked dirty, read as much as we
-                 * can from child. Note that only sectors that are marked dirty
-                 * must be read from child.
-                 */
-                while (   (cSectors < (cbBuf / VHD_SECTOR_SIZE))
-                       && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-                {
-                    cBATEntryIndex++;
-                    cSectors++;
-                }
-
-                cbBuf = cSectors * VHD_SECTOR_SIZE;
-
-                LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
-                rc = vhdFileReadSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
-            }
-            else
-            {
-                /*
-                 * The first sector being read is marked clean, so we should read from
-                 * our parent instead, but only as much as there are the following
-                 * clean sectors, because the block may still contain dirty sectors
-                 * further on. We just need to compute the number of clean sectors
-                 * and pass it to our caller along with the notification that they
-                 * should be read from the parent.
-                 */
-                cBATEntryIndex++;
-                cSectors = 1;
-
-                while (   (cSectors < (cbBuf / VHD_SECTOR_SIZE))
-                       && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-                {
-                    cBATEntryIndex++;
-                    cSectors++;
-                }
-
-                cbBuf = cSectors * VHD_SECTOR_SIZE;
-                LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
-                rc = VERR_VD_BLOCK_FREE;
-            }
-        }
-        else
-            AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
-    }
-    else
-    {
-        rc = vhdFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        if (pcbActuallyRead)
-            *pcbActuallyRead = cbBuf;
-
-        Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n"
-                "%.*Rhxd\n",
-                uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
-    }
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                    size_t cbBuf, size_t *pcbWriteProcess,
-                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
-             pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
-
-    AssertPtr(pImage);
-    Assert(uOffset % VHD_SECTOR_SIZE == 0);
-    Assert(cbBuf % VHD_SECTOR_SIZE == 0);
-
-    if (pImage->pBlockAllocationTable)
-    {
-        /*
-         * Get the data block first.
-         */
-        uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
-        uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
-        uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
-        uint64_t uVhdOffset;
-
-        /*
-         * Clip write range.
-         */
-        cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
-        /*
-         * If the block is not allocated the content of the entry is ~0
-         * and we need to allocate a new block. Note that while blocks are
-         * allocated with a relatively big granularity, each sector has its
-         * own bitmap entry, indicating whether it has been written or not.
-         * So that means for the purposes of the higher level that the
-         * granularity is invisible. This means there's no need to return
-         * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
-         */
-        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
-        {
-            /* Check if the block allocation should be suppressed. */
-            if (fWrite & VD_WRITE_NO_ALLOC)
-            {
-                *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
-                *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
-
-                if (pcbWriteProcess)
-                    *pcbWriteProcess = cbBuf;
-                rc = VERR_VD_BLOCK_FREE;
-                goto out;
-            }
-
-            size_t  cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
-            uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
-
-            if (!pNewBlock)
-            {
-                rc = VERR_NO_MEMORY;
-                goto out;
-            }
-
-            /*
-             * Write the new block at the current end of the file.
-             */
-            rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
-                                  pNewBlock, cbNewBlock, NULL);
-            AssertRC(rc);
-
-            /*
-             * Set the new end of the file and link the new block into the BAT.
-             */
-            pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
-            pImage->uCurrentEndOfFile += cbNewBlock;
-            RTMemFree(pNewBlock);
-
-            /* Write the updated BAT and the footer to remain in a consistent state. */
-            rc = vhdFlushImage(pImage);
-            AssertRC(rc);
-        }
-
-        /*
-         * Calculate the real offset in the file.
-         */
-        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
-
-        /* Write data. */
-        vhdFileWriteSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
-
-        /* Read in the block's bitmap. */
-        rc = vhdFileReadSync(pImage,
-                             ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                             pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
-                             NULL);
-        if (RT_SUCCESS(rc))
-        {
-            bool fChanged = false;
-
-            /* Set the bits for all sectors having been written. */
-            for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
-            {
-                fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
-                cBATEntryIndex++;
-            }
-
-            if (fChanged)
-            {
-                /* Write the bitmap back. */
-                rc = vhdFileWriteSync(pImage,
-                                      ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                                      pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
-                                      NULL);
-            }
-        }
-    }
-    else
-    {
-        rc = vhdFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbBuf;
-
-    /* Stay on the safe side. Do not run the risk of confusing the higher
-     * level, as that can be pretty lethal to image consistency. */
-    *pcbPreRead = 0;
-    *pcbPostRead = 0;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vhdFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    rc = vhdFlushImage(pImage);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned vhdGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    unsigned ver = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        ver = 1; /**< @todo use correct version */
-
-    LogFlowFunc(("returns %u\n", ver));
-    return ver;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t vhdGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-        cb = pImage->cbSize;
-
-    LogFlowFunc(("returns %llu\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t vhdGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-        cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pImage->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
-{
-LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->LCHSGeometry.cCylinders)
-        {
-            *pLCHSGeometry = pImage->LCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-
-        pImage->LCHSGeometry = *pLCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned vhdGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uImageFlags = pImage->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned vhdGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    rc = vhdFreeImage(pImage, false);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vhdOpenImage(pImage, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int vhdGetComment(void *pBackendData, char *pszComment,
-                         size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int vhdSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-            rc = VERR_NOT_SUPPORTED;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ImageUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            pImage->ImageUuid = *pUuid;
-            /* Update the footer copy. It will get written to disk when the image is closed. */
-            memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
-            /* Update checksum. */
-            pImage->vhdFooterCopy.Checksum = 0;
-            pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
-
-            /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
-            if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
-                pImage->fDynHdrNeedsUpdate = true;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ParentUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (pImage && pImage->pStorage)
-    {
-        if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        {
-            pImage->ParentUuid = *pUuid;
-            pImage->fDynHdrNeedsUpdate = true;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = VERR_NOT_SUPPORTED;
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-            rc = VERR_NOT_SUPPORTED;
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void vhdDump(void *pBackendData)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        vhdMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
-                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
-                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
-                    VHD_SECTOR_SIZE);
-        vhdMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
-        vhdMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
-    }
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetTimestamp */
-static int vhdGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
-{
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        rc = vhdFileGetModificationTime(pImage, pImage->pszFilename, pTimeStamp);
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
-static int vhdGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
-{
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
-static int vhdSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
-{
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-        {
-            pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
-            pImage->fDynHdrNeedsUpdate = true;
-        }
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
-static int vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
-{
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-        *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
-static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
-{
-    int rc = VINF_SUCCESS;
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            rc = VERR_VD_IMAGE_READ_ONLY;
-        else
-        {
-            if (pImage->pszParentFilename)
-                RTStrFree(pImage->pszParentFilename);
-            pImage->pszParentFilename = RTStrDup(pszParentFilename);
-            if (!pImage->pszParentFilename)
-                rc = VERR_NO_MEMORY;
-            else
-                pImage->fDynHdrNeedsUpdate = true;
-        }
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
-static bool vhdIsAsyncIOSupported(void *pBackendData)
-{
-    return true;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
-                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
-
-    if (uOffset + cbRead > pImage->cbSize)
-        return VERR_INVALID_PARAMETER;
-
-    /*
-     * If we have a dynamic disk image, we need to find the data block and sector to read.
-     */
-    if (pImage->pBlockAllocationTable)
-    {
-        /*
-         * Get the data block first.
-         */
-        uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
-        uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
-        uint64_t uVhdOffset;
-
-        LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
-        LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
-
-        /*
-         * If the block is not allocated the content of the entry is ~0
-         */
-        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
-        {
-            /* Return block size as read. */
-            *pcbActuallyRead = RT_MIN(cbRead, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
-            return VERR_VD_BLOCK_FREE;
-        }
-
-        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
-        LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
-
-        /*
-         * Clip read range to remain in this data block.
-         */
-        cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
-        /* Read in the block's bitmap. */
-        PVDMETAXFER pMetaXfer;
-        rc = vhdFileReadMetaAsync(pImage,
-                                  ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                                  pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
-                                  pIoCtx, &pMetaXfer, NULL, NULL);
-
-        if (RT_SUCCESS(rc))
-        {
-            uint32_t cSectors = 0;
-
-            vhdFileMetaXferRelease(pImage, pMetaXfer);
-            if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-            {
-                cBATEntryIndex++;
-                cSectors = 1;
-
-                /*
-                 * The first sector being read is marked dirty, read as much as we
-                 * can from child. Note that only sectors that are marked dirty
-                 * must be read from child.
-                 */
-                while (   (cSectors < (cbRead / VHD_SECTOR_SIZE))
-                       && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-                {
-                    cBATEntryIndex++;
-                    cSectors++;
-                }
-
-                cbRead = cSectors * VHD_SECTOR_SIZE;
-
-                LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
-                rc = vhdFileReadUserAsync(pImage, uVhdOffset, pIoCtx, cbRead);
-            }
-            else
-            {
-                /*
-                 * The first sector being read is marked clean, so we should read from
-                 * our parent instead, but only as much as there are the following
-                 * clean sectors, because the block may still contain dirty sectors
-                 * further on. We just need to compute the number of clean sectors
-                 * and pass it to our caller along with the notification that they
-                 * should be read from the parent.
-                 */
-                cBATEntryIndex++;
-                cSectors = 1;
-
-                while (   (cSectors < (cbRead / VHD_SECTOR_SIZE))
-                       && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
-                {
-                    cBATEntryIndex++;
-                    cSectors++;
-                }
-
-                cbRead = cSectors * VHD_SECTOR_SIZE;
-                LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
-                rc = VERR_VD_BLOCK_FREE;
-            }
-        }
-        else
-            AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
-    }
-    else
-    {
-        rc = vhdFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
-    }
-
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbRead;
-
-    LogFlowFunc(("returns rc=%Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                         PVDIOCTX pIoCtx,
-                         size_t *pcbWriteProcess, size_t *pcbPreRead,
-                         size_t *pcbPostRead, unsigned fWrite)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
-             pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
-
-    AssertPtr(pImage);
-    Assert(uOffset % VHD_SECTOR_SIZE == 0);
-    Assert(cbWrite % VHD_SECTOR_SIZE == 0);
-
-    if (pImage->pBlockAllocationTable)
-    {
-        /*
-         * Get the data block first.
-         */
-        uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
-        uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
-        uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
-        uint64_t uVhdOffset;
-
-        /*
-         * Clip write range.
-         */
-        cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
-
-        /*
-         * If the block is not allocated the content of the entry is ~0
-         * and we need to allocate a new block. Note that while blocks are
-         * allocated with a relatively big granularity, each sector has its
-         * own bitmap entry, indicating whether it has been written or not.
-         * So that means for the purposes of the higher level that the
-         * granularity is invisible. This means there's no need to return
-         * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
-         */
-        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
-        {
-            /* Check if the block allocation should be suppressed. */
-            if (fWrite & VD_WRITE_NO_ALLOC)
-            {
-                *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
-                *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
-
-                if (pcbWriteProcess)
-                    *pcbWriteProcess = cbWrite;
-                return VERR_VD_BLOCK_FREE;
-            }
-
-            PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
-            bool fIoInProgress = false;
-
-            if (!pExpand)
-                return VERR_NO_MEMORY;
-
-            pExpand->cbEofOld = pImage->uCurrentEndOfFile;
-            pExpand->idxBatAllocated = cBlockAllocationTableEntry;
-            pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
-
-            /* Set the bits for all sectors having been written. */
-            for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
-            {
-                /* No need to check for a changed value because this is an initial write. */
-                vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
-                cBATEntryIndex++;
-            }
-
-            do
-            {
-                /*
-                 * Start with the sector bitmap.
-                 */
-                rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
-                                           pExpand->au8Bitmap,
-                                           pImage->cbDataBlockBitmap, pIoCtx,
-                                           vhdAsyncExpansionDataBlockBitmapComplete,
-                                           pExpand);
-                if (RT_SUCCESS(rc))
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
-                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                    fIoInProgress = true;
-                else
-                {
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    break;
-                }
-
-
-                /*
-                 * Write the new block at the current end of the file.
-                 */
-                rc = vhdFileWriteUserAsync(pImage,
-                                           pImage->uCurrentEndOfFile + pImage->cbDataBlockBitmap,
-                                           pIoCtx, cbWrite,
-                                           vhdAsyncExpansionDataComplete,
-                                           pExpand);
-                if (RT_SUCCESS(rc))
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
-                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                    fIoInProgress = true;
-                else
-                {
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    break;
-                }
-
-                /*
-                 * Write entry in the BAT.
-                 */
-                rc = vhdFileWriteMetaAsync(pImage,
-                                           pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
-                                           &pExpand->idxBlockBe,
-                                           sizeof(uint32_t), pIoCtx,
-                                           vhdAsyncExpansionBatUpdateComplete,
-                                           pExpand);
-                if (RT_SUCCESS(rc))
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
-                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                    fIoInProgress = true;
-                else
-                {
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    break;
-                }
-
-                /*
-                 * Set the new end of the file and link the new block into the BAT.
-                 */
-                pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
-                pImage->uCurrentEndOfFile += pImage->cbDataBlockBitmap + pImage->cbDataBlock;
-
-                /* Update the footer. */
-                rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
-                                           &pImage->vhdFooterCopy,
-                                           sizeof(VHDFooter), pIoCtx,
-                                           vhdAsyncExpansionFooterUpdateComplete,
-                                           pExpand);
-                if (RT_SUCCESS(rc))
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
-                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                    fIoInProgress = true;
-                else
-                {
-                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
-                    break;
-                }
-
-            } while (0);
-
-            if (!fIoInProgress)
-                vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
-            else
-                rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
-        }
-        else
-        {
-            /*
-             * Calculate the real offset in the file.
-             */
-            uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
-
-            /* Read in the block's bitmap. */
-            PVDMETAXFER pMetaXfer;
-            rc = vhdFileReadMetaAsync(pImage,
-                                      ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                                      pImage->pu8Bitmap,
-                                      pImage->cbDataBlockBitmap, pIoCtx,
-                                      &pMetaXfer, NULL, NULL);
-            if (RT_SUCCESS(rc))
-            {
-                vhdFileMetaXferRelease(pImage, pMetaXfer);
-
-                /* Write data. */
-                rc = vhdFileWriteUserAsync(pImage, uVhdOffset, pIoCtx, cbWrite,
-                                           NULL, NULL);
-                if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                {
-                    bool fChanged = false;
-
-                    /* Set the bits for all sectors having been written. */
-                    for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
-                    {
-                        fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
-                        cBATEntryIndex++;
-                    }
-
-                    /* Only write the bitmap if it was changed. */
-                    if (fChanged)
-                    {
-                        /*
-                         * Write the bitmap back.
-                         *
-                         * @note We don't have a completion callback here because we
-                         * can't do anything if the write fails for some reason.
-                         * The error will propagated to the device/guest
-                         * by the generic VD layer already and we don't need
-                         * to rollback anything here.
-                         */
-                        rc = vhdFileWriteMetaAsync(pImage,
-                                                   ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
-                                                   pImage->pu8Bitmap,
-                                                   pImage->cbDataBlockBitmap,
-                                                   pIoCtx, NULL, NULL);
-                    }
-                }
-            }
-        }
-    }
-    else
-    {
-        rc = vhdFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbWrite;
-
-    /* Stay on the safe side. Do not run the risk of confusing the higher
-     * level, as that can be pretty lethal to image consistency. */
-    *pcbPreRead = 0;
-    *pcbPostRead = 0;
-
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-
-    /* No need to write anything here. Data is always updated on a write. */
-    return vhdFileFlushAsync(pImage, pIoCtx, NULL, NULL);
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnResize */
-static int vhdResize(void *pBackendData, uint64_t cbSize,
-                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                     unsigned uPercentStart, unsigned uPercentSpan,
-                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                     PVDINTERFACE pVDIfsOperation)
-{
-    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        if (pCbProgress)
-            pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Making the image smaller is not supported at the moment. */
-    if (   cbSize < pImage->cbSize
-        || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        rc = VERR_NOT_SUPPORTED;
-    else if (cbSize > pImage->cbSize)
-    {
-        unsigned cBlocksAllocated = 0;
-        size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap;     /** < Size of a block including the sector bitmap. */
-        uint32_t cBlocksNew = cbSize / pImage->cbDataBlock;                   /** < New number of blocks in the image after the resize */
-        if (cbSize % pImage->cbDataBlock)
-            cBlocksNew++;
-
-        uint32_t cBlocksOld      = pImage->cBlockAllocationTableEntries;      /** < Number of blocks before the resize. */
-        uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE);                         /** < Required space for the block array after the resize. */
-        uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
-        uint64_t offStartDataOld = ~0ULL;
-
-        /* Go through the BAT and find the data start offset. */
-        for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
-        {
-            if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
-            {
-                uint64_t offStartBlock = pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
-                if (offStartBlock < offStartDataOld)
-                    offStartDataOld = offStartBlock;
-                cBlocksAllocated++;
-            }
-        }
-
-        if (   offStartDataOld != offStartDataNew
-            && cBlocksAllocated > 0)
-        {
-            /* Calculate how many sectors nee to be relocated. */
-            uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
-            unsigned cBlocksReloc = cbOverlapping / cbBlock;
-            if (cbOverlapping % cbBlock)
-                cBlocksReloc++;
-
-            cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
-            offStartDataNew = offStartDataOld;
-
-            /* Do the relocation. */
-            LogFlow(("Relocating %u blocks\n", cBlocksReloc));
-
-            /*
-             * Get the blocks we need to relocate first, they are appended to the end
-             * of the image.
-             */
-            void *pvBuf = NULL, *pvZero = NULL;
-            do
-            {
-                /* Allocate data buffer. */
-                pvBuf = RTMemAllocZ(cbBlock);
-                if (!pvBuf)
-                {
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-
-                /* Allocate buffer for overwriting with zeroes. */
-                pvZero = RTMemAllocZ(cbBlock);
-                if (!pvZero)
-                {
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-
-                for (unsigned i = 0; i < cBlocksReloc; i++)
-                {
-                    uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
-
-                    /* Search the index in the block table. */
-                    for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
-                    {
-                        if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
-                        {
-                            /* Read data and append to the end of the image. */
-                            rc = vhdFileReadSync(pImage, offStartDataNew, pvBuf, cbBlock, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            /* Zero out the old block area. */
-                            rc = vhdFileWriteSync(pImage, offStartDataNew, pvZero, cbBlock, NULL);
-                            if (RT_FAILURE(rc))
-                                break;
-
-                            /* Update block counter. */
-                            pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
-
-                            pImage->uCurrentEndOfFile += cbBlock;
-
-                            /* Continue with the next block. */
-                            break;
-                        }
-                    }
-
-                    if (RT_FAILURE(rc))
-                        break;
-
-                    offStartDataNew += cbBlock;
-                }
-            } while (0);
-
-            if (pvBuf)
-                RTMemFree(pvBuf);
-            if (pvZero)
-                RTMemFree(pvZero);
-        }
-
-        /*
-         * Relocation done, expand the block array and update the header with
-         * the new data.
-         */
-        if (RT_SUCCESS(rc))
-        {
-            uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
-            if (paBlocksNew)
-            {
-                pImage->pBlockAllocationTable = paBlocksNew;
-
-                /* Mark the new blocks as unallocated. */
-                for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
-                    pImage->pBlockAllocationTable[idxBlock] = ~0U;
-            }
-            else
-                rc = VERR_NO_MEMORY;
-
-            if (RT_SUCCESS(rc))
-            {
-                /* Write the block array before updating the rest. */
-                rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset, pImage->pBlockAllocationTable,
-                                      cBlocksNew * sizeof(uint32_t), NULL);
-            }
-
-            if (RT_SUCCESS(rc))
-            {
-                /* Update size and new block count. */
-                pImage->cBlockAllocationTableEntries = cBlocksNew;
-                pImage->cbSize = cbSize;
-
-                /* Update geometry. */
-                pImage->PCHSGeometry = *pPCHSGeometry;
-                pImage->LCHSGeometry = *pLCHSGeometry;
-            }
-        }
-
-        /* Update header information in base image file. */
-        pImage->fDynHdrNeedsUpdate = true;
-        vhdFlush(pImage);
-    }
-    /* Same size doesn't change the image at all. */
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_VhdBackend =
-{
-    /* pszBackendName */
-    "VHD",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-    VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
-    VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
-    VD_CAP_ASYNC | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aVhdFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    vhdCheckIfValid,
-    /* pfnOpen */
-    vhdOpen,
-    /* pfnCreate */
-    vhdCreate,
-    /* pfnRename */
-    vhdRename,
-    /* pfnClose */
-    vhdClose,
-    /* pfnRead */
-    vhdRead,
-    /* pfnWrite */
-    vhdWrite,
-    /* pfnFlush */
-    vhdFlush,
-    /* pfnGetVersion */
-    vhdGetVersion,
-    /* pfnGetSize */
-    vhdGetSize,
-    /* pfnGetFileSize */
-    vhdGetFileSize,
-    /* pfnGetPCHSGeometry */
-    vhdGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    vhdSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    vhdGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    vhdSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    vhdGetImageFlags,
-    /* pfnGetOpenFlags */
-    vhdGetOpenFlags,
-    /* pfnSetOpenFlags */
-    vhdSetOpenFlags,
-    /* pfnGetComment */
-    vhdGetComment,
-    /* pfnSetComment */
-    vhdSetComment,
-    /* pfnGetUuid */
-    vhdGetUuid,
-    /* pfnSetUuid */
-    vhdSetUuid,
-    /* pfnGetModificationUuid */
-    vhdGetModificationUuid,
-    /* pfnSetModificationUuid */
-    vhdSetModificationUuid,
-    /* pfnGetParentUuid */
-    vhdGetParentUuid,
-    /* pfnSetParentUuid */
-    vhdSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    vhdGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    vhdSetParentModificationUuid,
-    /* pfnDump */
-    vhdDump,
-    /* pfnGetTimeStamp */
-    vhdGetTimeStamp,
-    /* pfnGetParentTimeStamp */
-    vhdGetParentTimeStamp,
-    /* pfnSetParentTimeStamp */
-    vhdSetParentTimeStamp,
-    /* pfnGetParentFilename */
-    vhdGetParentFilename,
-    /* pfnSetParentFilename */
-    vhdSetParentFilename,
-    /* pfnIsAsyncIOSupported */
-    vhdIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    vhdAsyncRead,
-    /* pfnAsyncWrite */
-    vhdAsyncWrite,
-    /* pfnAsyncFlush */
-    vhdAsyncFlush,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    vhdResize
-};
Index: unk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp	(revision 33566)
+++ 	(revision )
@@ -1,7418 +1,0 @@
-/* $Id$ */
-/** @file
- * VMDK disk image, core code.
- */
-
-/*
- * Copyright (C) 2006-2010 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-*   Header Files                                                               *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_VD_VMDK
-#include <VBox/VBoxHDD-Plugin.h>
-#include <VBox/err.h>
-
-#include <VBox/log.h>
-#include <iprt/assert.h>
-#include <iprt/alloc.h>
-#include <iprt/uuid.h>
-#include <iprt/path.h>
-#include <iprt/string.h>
-#include <iprt/rand.h>
-#include <iprt/zip.h>
-#include <iprt/asm.h>
-
-
-/*******************************************************************************
-*   Constants And Macros, Structures and Typedefs                              *
-*******************************************************************************/
-
-/** Maximum encoded string size (including NUL) we allow for VMDK images.
- * Deliberately not set high to avoid running out of descriptor space. */
-#define VMDK_ENCODED_COMMENT_MAX 1024
-
-/** VMDK descriptor DDB entry for PCHS cylinders. */
-#define VMDK_DDB_GEO_PCHS_CYLINDERS "ddb.geometry.cylinders"
-
-/** VMDK descriptor DDB entry for PCHS heads. */
-#define VMDK_DDB_GEO_PCHS_HEADS "ddb.geometry.heads"
-
-/** VMDK descriptor DDB entry for PCHS sectors. */
-#define VMDK_DDB_GEO_PCHS_SECTORS "ddb.geometry.sectors"
-
-/** VMDK descriptor DDB entry for LCHS cylinders. */
-#define VMDK_DDB_GEO_LCHS_CYLINDERS "ddb.geometry.biosCylinders"
-
-/** VMDK descriptor DDB entry for LCHS heads. */
-#define VMDK_DDB_GEO_LCHS_HEADS "ddb.geometry.biosHeads"
-
-/** VMDK descriptor DDB entry for LCHS sectors. */
-#define VMDK_DDB_GEO_LCHS_SECTORS "ddb.geometry.biosSectors"
-
-/** VMDK descriptor DDB entry for image UUID. */
-#define VMDK_DDB_IMAGE_UUID "ddb.uuid.image"
-
-/** VMDK descriptor DDB entry for image modification UUID. */
-#define VMDK_DDB_MODIFICATION_UUID "ddb.uuid.modification"
-
-/** VMDK descriptor DDB entry for parent image UUID. */
-#define VMDK_DDB_PARENT_UUID "ddb.uuid.parent"
-
-/** VMDK descriptor DDB entry for parent image modification UUID. */
-#define VMDK_DDB_PARENT_MODIFICATION_UUID "ddb.uuid.parentmodification"
-
-/** No compression for streamOptimized files. */
-#define VMDK_COMPRESSION_NONE 0
-
-/** Deflate compression for streamOptimized files. */
-#define VMDK_COMPRESSION_DEFLATE 1
-
-/** Marker that the actual GD value is stored in the footer. */
-#define VMDK_GD_AT_END 0xffffffffffffffffULL
-
-/** Marker for end-of-stream in streamOptimized images. */
-#define VMDK_MARKER_EOS 0
-
-/** Marker for grain table block in streamOptimized images. */
-#define VMDK_MARKER_GT 1
-
-/** Marker for grain directory block in streamOptimized images. */
-#define VMDK_MARKER_GD 2
-
-/** Marker for footer in streamOptimized images. */
-#define VMDK_MARKER_FOOTER 3
-
-/** Dummy marker for "don't check the marker value". */
-#define VMDK_MARKER_IGNORE 0xffffffffU
-
-/**
- * Magic number for hosted images created by VMware Workstation 4, VMware
- * Workstation 5, VMware Server or VMware Player. Not necessarily sparse.
- */
-#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
-
-/**
- * VMDK hosted binary extent header. The "Sparse" is a total misnomer, as
- * this header is also used for monolithic flat images.
- */
-#pragma pack(1)
-typedef struct SparseExtentHeader
-{
-    uint32_t    magicNumber;
-    uint32_t    version;
-    uint32_t    flags;
-    uint64_t    capacity;
-    uint64_t    grainSize;
-    uint64_t    descriptorOffset;
-    uint64_t    descriptorSize;
-    uint32_t    numGTEsPerGT;
-    uint64_t    rgdOffset;
-    uint64_t    gdOffset;
-    uint64_t    overHead;
-    bool        uncleanShutdown;
-    char        singleEndLineChar;
-    char        nonEndLineChar;
-    char        doubleEndLineChar1;
-    char        doubleEndLineChar2;
-    uint16_t    compressAlgorithm;
-    uint8_t     pad[433];
-} SparseExtentHeader;
-#pragma pack()
-
-/** VMDK capacity for a single chunk when 2G splitting is turned on. Should be
- * divisible by the default grain size (64K) */
-#define VMDK_2G_SPLIT_SIZE (2047 * 1024 * 1024)
-
-/** VMDK streamOptimized file format marker. The type field may or may not
- * be actually valid, but there's always data to read there. */
-#pragma pack(1)
-typedef struct VMDKMARKER
-{
-    uint64_t uSector;
-    uint32_t cbSize;
-    uint32_t uType;
-} VMDKMARKER, *PVMDKMARKER;
-#pragma pack()
-
-
-#ifdef VBOX_WITH_VMDK_ESX
-
-/** @todo the ESX code is not tested, not used, and lacks error messages. */
-
-/**
- * Magic number for images created by VMware GSX Server 3 or ESX Server 3.
- */
-#define VMDK_ESX_SPARSE_MAGICNUMBER 0x44574f43 /* 'C' 'O' 'W' 'D' */
-
-#pragma pack(1)
-typedef struct COWDisk_Header
-{
-    uint32_t    magicNumber;
-    uint32_t    version;
-    uint32_t    flags;
-    uint32_t    numSectors;
-    uint32_t    grainSize;
-    uint32_t    gdOffset;
-    uint32_t    numGDEntries;
-    uint32_t    freeSector;
-    /* The spec incompletely documents quite a few further fields, but states
-     * that they are unused by the current format. Replace them by padding. */
-    char        reserved1[1604];
-    uint32_t    savedGeneration;
-    char        reserved2[8];
-    uint32_t    uncleanShutdown;
-    char        padding[396];
-} COWDisk_Header;
-#pragma pack()
-#endif /* VBOX_WITH_VMDK_ESX */
-
-
-/** Convert sector number/size to byte offset/size. */
-#define VMDK_SECTOR2BYTE(u) ((uint64_t)(u) << 9)
-
-/** Convert byte offset/size to sector number/size. */
-#define VMDK_BYTE2SECTOR(u) ((u) >> 9)
-
-/**
- * VMDK extent type.
- */
-typedef enum VMDKETYPE
-{
-    /** Hosted sparse extent. */
-    VMDKETYPE_HOSTED_SPARSE = 1,
-    /** Flat extent. */
-    VMDKETYPE_FLAT,
-    /** Zero extent. */
-    VMDKETYPE_ZERO,
-    /** VMFS extent, used by ESX. */
-    VMDKETYPE_VMFS
-#ifdef VBOX_WITH_VMDK_ESX
-    ,
-    /** ESX sparse extent. */
-    VMDKETYPE_ESX_SPARSE
-#endif /* VBOX_WITH_VMDK_ESX */
-} VMDKETYPE, *PVMDKETYPE;
-
-/**
- * VMDK access type for a extent.
- */
-typedef enum VMDKACCESS
-{
-    /** No access allowed. */
-    VMDKACCESS_NOACCESS = 0,
-    /** Read-only access. */
-    VMDKACCESS_READONLY,
-    /** Read-write access. */
-    VMDKACCESS_READWRITE
-} VMDKACCESS, *PVMDKACCESS;
-
-/** Forward declaration for PVMDKIMAGE. */
-typedef struct VMDKIMAGE *PVMDKIMAGE;
-
-/**
- * Extents files entry. Used for opening a particular file only once.
- */
-typedef struct VMDKFILE
-{
-    /** Pointer to filename. Local copy. */
-    const char      *pszFilename;
-    /** File open flags for consistency checking. */
-    unsigned         fOpen;
-    /** Flag whether this file has been opened for async I/O. */
-    bool             fAsyncIO;
-    /** Handle for sync/async file abstraction.*/
-    PVDIOSTORAGE     pStorage;
-    /** Reference counter. */
-    unsigned         uReferences;
-    /** Flag whether the file should be deleted on last close. */
-    bool             fDelete;
-    /** Pointer to the image we belong to (for debugging purposes). */
-    PVMDKIMAGE       pImage;
-    /** Pointer to next file descriptor. */
-    struct VMDKFILE *pNext;
-    /** Pointer to the previous file descriptor. */
-    struct VMDKFILE *pPrev;
-} VMDKFILE, *PVMDKFILE;
-
-/**
- * VMDK extent data structure.
- */
-typedef struct VMDKEXTENT
-{
-    /** File handle. */
-    PVMDKFILE    pFile;
-    /** Base name of the image extent. */
-    const char  *pszBasename;
-    /** Full name of the image extent. */
-    const char  *pszFullname;
-    /** Number of sectors in this extent. */
-    uint64_t    cSectors;
-    /** Number of sectors per block (grain in VMDK speak). */
-    uint64_t    cSectorsPerGrain;
-    /** Starting sector number of descriptor. */
-    uint64_t    uDescriptorSector;
-    /** Size of descriptor in sectors. */
-    uint64_t    cDescriptorSectors;
-    /** Starting sector number of grain directory. */
-    uint64_t    uSectorGD;
-    /** Starting sector number of redundant grain directory. */
-    uint64_t    uSectorRGD;
-    /** Total number of metadata sectors. */
-    uint64_t    cOverheadSectors;
-    /** Nominal size (i.e. as described by the descriptor) of this extent. */
-    uint64_t    cNominalSectors;
-    /** Sector offset (i.e. as described by the descriptor) of this extent. */
-    uint64_t    uSectorOffset;
-    /** Number of entries in a grain table. */
-    uint32_t    cGTEntries;
-    /** Number of sectors reachable via a grain directory entry. */
-    uint32_t    cSectorsPerGDE;
-    /** Number of entries in the grain directory. */
-    uint32_t    cGDEntries;
-    /** Pointer to the next free sector. Legacy information. Do not use. */
-    uint32_t    uFreeSector;
-    /** Number of this extent in the list of images. */
-    uint32_t    uExtent;
-    /** Pointer to the descriptor (NULL if no descriptor in this extent). */
-    char        *pDescData;
-    /** Pointer to the grain directory. */
-    uint32_t    *pGD;
-    /** Pointer to the redundant grain directory. */
-    uint32_t    *pRGD;
-    /** VMDK version of this extent. 1=1.0/1.1 */
-    uint32_t    uVersion;
-    /** Type of this extent. */
-    VMDKETYPE   enmType;
-    /** Access to this extent. */
-    VMDKACCESS  enmAccess;
-    /** Flag whether this extent is marked as unclean. */
-    bool        fUncleanShutdown;
-    /** Flag whether the metadata in the extent header needs to be updated. */
-    bool        fMetaDirty;
-    /** Flag whether there is a footer in this extent. */
-    bool        fFooter;
-    /** Compression type for this extent. */
-    uint16_t    uCompression;
-    /** Append position for writing new grain. Only for sparse extents. */
-    uint64_t    uAppendPosition;
-    /** Last grain which was accessed. Only for streamOptimized extents. */
-    uint32_t    uLastGrainAccess;
-    /** Starting sector corresponding to the grain buffer. */
-    uint32_t    uGrainSectorAbs;
-    /** Grain number corresponding to the grain buffer. */
-    uint32_t    uGrain;
-    /** Actual size of the compressed data, only valid for reading. */
-    uint32_t    cbGrainStreamRead;
-    /** Size of compressed grain buffer for streamOptimized extents. */
-    size_t      cbCompGrain;
-    /** Compressed grain buffer for streamOptimized extents, with marker. */
-    void        *pvCompGrain;
-    /** Decompressed grain buffer for streamOptimized extents. */
-    void        *pvGrain;
-    /** Reference to the image in which this extent is used. Do not use this
-     * on a regular basis to avoid passing pImage references to functions
-     * explicitly. */
-    struct VMDKIMAGE *pImage;
-} VMDKEXTENT, *PVMDKEXTENT;
-
-/**
- * Grain table cache size. Allocated per image.
- */
-#define VMDK_GT_CACHE_SIZE 256
-
-/**
- * Grain table block size. Smaller than an actual grain table block to allow
- * more grain table blocks to be cached without having to allocate excessive
- * amounts of memory for the cache.
- */
-#define VMDK_GT_CACHELINE_SIZE 128
-
-
-/**
- * Maximum number of lines in a descriptor file. Not worth the effort of
- * making it variable. Descriptor files are generally very short (~20 lines),
- * with the exception of sparse files split in 2G chunks, which need for the
- * maximum size (almost 2T) exactly 1025 lines for the disk database.
- */
-#define VMDK_DESCRIPTOR_LINES_MAX   1100U
-
-/**
- * Parsed descriptor information. Allows easy access and update of the
- * descriptor (whether separate file or not). Free form text files suck.
- */
-typedef struct VMDKDESCRIPTOR
-{
-    /** Line number of first entry of the disk descriptor. */
-    unsigned    uFirstDesc;
-    /** Line number of first entry in the extent description. */
-    unsigned    uFirstExtent;
-    /** Line number of first disk database entry. */
-    unsigned    uFirstDDB;
-    /** Total number of lines. */
-    unsigned    cLines;
-    /** Total amount of memory available for the descriptor. */
-    size_t      cbDescAlloc;
-    /** Set if descriptor has been changed and not yet written to disk. */
-    bool        fDirty;
-    /** Array of pointers to the data in the descriptor. */
-    char        *aLines[VMDK_DESCRIPTOR_LINES_MAX];
-    /** Array of line indices pointing to the next non-comment line. */
-    unsigned    aNextLines[VMDK_DESCRIPTOR_LINES_MAX];
-} VMDKDESCRIPTOR, *PVMDKDESCRIPTOR;
-
-
-/**
- * Cache entry for translating extent/sector to a sector number in that
- * extent.
- */
-typedef struct VMDKGTCACHEENTRY
-{
-    /** Extent number for which this entry is valid. */
-    uint32_t    uExtent;
-    /** GT data block number. */
-    uint64_t    uGTBlock;
-    /** Data part of the cache entry. */
-    uint32_t    aGTData[VMDK_GT_CACHELINE_SIZE];
-} VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY;
-
-/**
- * Cache data structure for blocks of grain table entries. For now this is a
- * fixed size direct mapping cache, but this should be adapted to the size of
- * the sparse image and maybe converted to a set-associative cache. The
- * implementation below implements a write-through cache with write allocate.
- */
-typedef struct VMDKGTCACHE
-{
-    /** Cache entries. */
-    VMDKGTCACHEENTRY    aGTCache[VMDK_GT_CACHE_SIZE];
-    /** Number of cache entries (currently unused). */
-    unsigned            cEntries;
-} VMDKGTCACHE, *PVMDKGTCACHE;
-
-/**
- * Complete VMDK image data structure. Mainly a collection of extents and a few
- * extra global data fields.
- */
-typedef struct VMDKIMAGE
-{
-    /** Image name. */
-    const char        *pszFilename;
-    /** Descriptor file if applicable. */
-    PVMDKFILE         pFile;
-    /** I/O interface. */
-    PVDINTERFACE      pInterfaceIO;
-    /** I/O interface callbacks. */
-    PVDINTERFACEIOINT pInterfaceIOCallbacks;
-
-    /** Pointer to the per-disk VD interface list. */
-    PVDINTERFACE      pVDIfsDisk;
-    /** Pointer to the per-image VD interface list. */
-    PVDINTERFACE      pVDIfsImage;
-
-    /** Error interface. */
-    PVDINTERFACE      pInterfaceError;
-    /** Error interface callbacks. */
-    PVDINTERFACEERROR pInterfaceErrorCallbacks;
-
-    /** Pointer to the image extents. */
-    PVMDKEXTENT     pExtents;
-    /** Number of image extents. */
-    unsigned        cExtents;
-    /** Pointer to the files list, for opening a file referenced multiple
-     * times only once (happens mainly with raw partition access). */
-    PVMDKFILE       pFiles;
-
-    /**
-     * Pointer to an array of segment entries for async I/O.
-     * This is an optimization because the task number to submit is not known
-     * and allocating/freeing an array in the read/write functions every time
-     * is too expensive.
-     */
-    PPDMDATASEG     paSegments;
-    /** Entries available in the segments array. */
-    unsigned        cSegments;
-
-    /** Open flags passed by VBoxHD layer. */
-    unsigned        uOpenFlags;
-    /** Image flags defined during creation or determined during open. */
-    unsigned        uImageFlags;
-    /** Total size of the image. */
-    uint64_t        cbSize;
-    /** Physical geometry of this image. */
-    VDGEOMETRY      PCHSGeometry;
-    /** Logical geometry of this image. */
-    VDGEOMETRY      LCHSGeometry;
-    /** Image UUID. */
-    RTUUID          ImageUuid;
-    /** Image modification UUID. */
-    RTUUID          ModificationUuid;
-    /** Parent image UUID. */
-    RTUUID          ParentUuid;
-    /** Parent image modification UUID. */
-    RTUUID          ParentModificationUuid;
-
-    /** Pointer to grain table cache, if this image contains sparse extents. */
-    PVMDKGTCACHE    pGTCache;
-    /** Pointer to the descriptor (NULL if no separate descriptor file). */
-    char            *pDescData;
-    /** Allocation size of the descriptor file. */
-    size_t          cbDescAlloc;
-    /** Parsed descriptor file content. */
-    VMDKDESCRIPTOR  Descriptor;
-} VMDKIMAGE;
-
-
-/** State for the input/output callout of the inflate reader/deflate writer. */
-typedef struct VMDKCOMPRESSIO
-{
-    /* Image this operation relates to. */
-    PVMDKIMAGE pImage;
-    /* Current read position. */
-    ssize_t iOffset;
-    /* Size of the compressed grain buffer (available data). */
-    size_t cbCompGrain;
-    /* Pointer to the compressed grain buffer. */
-    void *pvCompGrain;
-} VMDKCOMPRESSIO;
-
-
-/** Tracks async grain allocation. */
-typedef struct VMDKGRAINALLOCASYNC
-{
-    /** Flag whether the allocation failed. */
-    bool        fIoErr;
-    /** Current number of transfers pending.
-     * If reached 0 and there is an error the old state is restored. */
-    unsigned    cIoXfersPending;
-    /** Sector number */
-    uint64_t    uSector;
-    /** Flag whether the grain table needs to be updated. */
-    bool        fGTUpdateNeeded;
-    /** Extent the allocation happens. */
-    PVMDKEXTENT pExtent;
-    /** Position of the new grain, required for the grain table update. */
-    uint64_t    uGrainOffset;
-    /** Grain table sector. */
-    uint64_t    uGTSector;
-    /** Backup grain table sector. */
-    uint64_t    uRGTSector;
-} VMDKGRAINALLOCASYNC, *PVMDKGRAINALLOCASYNC;
-
-/*******************************************************************************
-*   Static Variables                                                           *
-*******************************************************************************/
-
-/** NULL-terminated array of supported file extensions. */
-static const VDFILEEXTENSION s_aVmdkFileExtensions[] =
-{
-    {"vmdk", VDTYPE_HDD},
-    {NULL, VDTYPE_INVALID}
-};
-
-/*******************************************************************************
-*   Internal Functions                                                         *
-*******************************************************************************/
-
-static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent);
-static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                               bool fDelete);
-
-static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
-static int vmdkFlushImage(PVMDKIMAGE pImage);
-static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment);
-static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete);
-
-static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq);
-
-/**
- * Internal: signal an error to the frontend.
- */
-DECLINLINE(int) vmdkError(PVMDKIMAGE pImage, int rc, RT_SRC_POS_DECL,
-                          const char *pszFormat, ...)
-{
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
-                                                   pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: signal an informational message to the frontend.
- */
-DECLINLINE(int) vmdkMessage(PVMDKIMAGE pImage, const char *pszFormat, ...)
-{
-    int rc = VINF_SUCCESS;
-    va_list va;
-    va_start(va, pszFormat);
-    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
-        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
-                                                          pszFormat, va);
-    va_end(va);
-    return rc;
-}
-
-/**
- * Internal: open a file (using a file descriptor cache to ensure each file
- * is only opened once - anything else can cause locking problems).
- */
-static int vmdkFileOpen(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile,
-                        const char *pszFilename, uint32_t fOpen, bool fAsyncIO)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKFILE pVmdkFile;
-
-    for (pVmdkFile = pImage->pFiles;
-         pVmdkFile != NULL;
-         pVmdkFile = pVmdkFile->pNext)
-    {
-        if (!strcmp(pszFilename, pVmdkFile->pszFilename))
-        {
-            Assert(fOpen == pVmdkFile->fOpen);
-            pVmdkFile->uReferences++;
-
-            *ppVmdkFile = pVmdkFile;
-
-            return rc;
-        }
-    }
-
-    /* If we get here, there's no matching entry in the cache. */
-    pVmdkFile = (PVMDKFILE)RTMemAllocZ(sizeof(VMDKFILE));
-    if (!VALID_PTR(pVmdkFile))
-    {
-        *ppVmdkFile = NULL;
-        return VERR_NO_MEMORY;
-    }
-
-    pVmdkFile->pszFilename = RTStrDup(pszFilename);
-    if (!VALID_PTR(pVmdkFile->pszFilename))
-    {
-        RTMemFree(pVmdkFile);
-        *ppVmdkFile = NULL;
-        return VERR_NO_MEMORY;
-    }
-    pVmdkFile->fOpen = fOpen;
-    pVmdkFile->fAsyncIO = fAsyncIO;
-
-    rc = pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
-                                                pszFilename, fOpen,
-                                                &pVmdkFile->pStorage);
-    if (RT_SUCCESS(rc))
-    {
-        pVmdkFile->uReferences = 1;
-        pVmdkFile->pImage = pImage;
-        pVmdkFile->pNext = pImage->pFiles;
-        if (pImage->pFiles)
-            pImage->pFiles->pPrev = pVmdkFile;
-        pImage->pFiles = pVmdkFile;
-        *ppVmdkFile = pVmdkFile;
-    }
-    else
-    {
-        RTStrFree((char *)(void *)pVmdkFile->pszFilename);
-        RTMemFree(pVmdkFile);
-        *ppVmdkFile = NULL;
-    }
-
-    return rc;
-}
-
-/**
- * Internal: close a file, updating the file descriptor cache.
- */
-static int vmdkFileClose(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKFILE pVmdkFile = *ppVmdkFile;
-
-    AssertPtr(pVmdkFile);
-
-    pVmdkFile->fDelete |= fDelete;
-    Assert(pVmdkFile->uReferences);
-    pVmdkFile->uReferences--;
-    if (pVmdkFile->uReferences == 0)
-    {
-        PVMDKFILE pPrev;
-        PVMDKFILE pNext;
-
-        /* Unchain the element from the list. */
-        pPrev = pVmdkFile->pPrev;
-        pNext = pVmdkFile->pNext;
-
-        if (pNext)
-            pNext->pPrev = pPrev;
-        if (pPrev)
-            pPrev->pNext = pNext;
-        else
-            pImage->pFiles = pNext;
-
-        rc = pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
-                                                     pVmdkFile->pStorage);
-        if (RT_SUCCESS(rc) && pVmdkFile->fDelete)
-            rc = pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
-                                                          pVmdkFile->pszFilename);
-        RTStrFree((char *)(void *)pVmdkFile->pszFilename);
-        RTMemFree(pVmdkFile);
-    }
-
-    *ppVmdkFile = NULL;
-    return rc;
-}
-
-/**
- * Internal: rename a file (sync)
- */
-DECLINLINE(int) vmdkFileMove(PVMDKIMAGE pImage, const char *pszSrc,
-                             const char *pszDst, unsigned fMove)
-{
-    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
-                                                  pszSrc, pszDst, fMove);
-}
-
-/**
- * Internal: get the size of a file (sync/async)
- */
-DECLINLINE(int) vmdkFileGetSize(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                uint64_t *pcbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
-                                                     pVmdkFile->pStorage,
-                                                     pcbSize);
-}
-
-/**
- * Internal: set the size of a file (sync/async)
- */
-DECLINLINE(int) vmdkFileSetSize(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                uint64_t cbSize)
-{
-    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
-                                                     pVmdkFile->pStorage,
-                                                     cbSize);
-}
-
-/**
- * Internal: read from a file (sync)
- */
-DECLINLINE(int) vmdkFileReadSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                 uint64_t uOffset, void *pvBuf,
-                                 size_t cbToRead, size_t *pcbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
-                                                      pVmdkFile->pStorage, uOffset,
-                                                      pvBuf, cbToRead, pcbRead);
-}
-
-/**
- * Internal: write to a file (sync)
- */
-DECLINLINE(int) vmdkFileWriteSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                  uint64_t uOffset, const void *pvBuf,
-                                  size_t cbToWrite, size_t *pcbWritten)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
-                                                       pVmdkFile->pStorage, uOffset,
-                                                       pvBuf, cbToWrite, pcbWritten);
-}
-
-/**
- * Internal: flush a file (sync)
- */
-DECLINLINE(int) vmdkFileFlush(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
-                                                       pVmdkFile->pStorage);
-}
-
-/**
- * Internal: read user data (async)
- */
-DECLINLINE(int) vmdkFileReadUserAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                      uint64_t uOffset, PVDIOCTX pIoCtx,
-                                      size_t cbRead)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
-                                                           pVmdkFile->pStorage,
-                                                           uOffset, pIoCtx,
-                                                           cbRead);
-}
-
-/**
- * Internal: write user data (async)
- */
-DECLINLINE(int) vmdkFileWriteUserAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                       uint64_t uOffset, PVDIOCTX pIoCtx,
-                                       size_t cbWrite,
-                                       PFNVDXFERCOMPLETED pfnComplete,
-                                       void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
-                                                            pVmdkFile->pStorage,
-                                                            uOffset, pIoCtx,
-                                                            cbWrite,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-/**
- * Internal: read metadata (async)
- */
-DECLINLINE(int) vmdkFileReadMetaAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                      uint64_t uOffset, void *pvBuffer,
-                                      size_t cbBuffer, PVDIOCTX pIoCtx,
-                                      PPVDMETAXFER ppMetaXfer,
-                                      PFNVDXFERCOMPLETED pfnComplete,
-                                      void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnReadMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                           pVmdkFile->pStorage,
-                                                           uOffset, pvBuffer,
-                                                           cbBuffer, pIoCtx,
-                                                           ppMetaXfer,
-                                                           pfnComplete,
-                                                           pvCompleteUser);
-}
-
-/**
- * Internal: write metadata (async)
- */
-DECLINLINE(int) vmdkFileWriteMetaAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                       uint64_t uOffset, void *pvBuffer,
-                                       size_t cbBuffer, PVDIOCTX pIoCtx,
-                                       PFNVDXFERCOMPLETED pfnComplete,
-                                       void *pvCompleteUser)
-{
-    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
-                                                            pVmdkFile->pStorage,
-                                                            uOffset, pvBuffer,
-                                                            cbBuffer, pIoCtx,
-                                                            pfnComplete,
-                                                            pvCompleteUser);
-}
-
-/**
- * Internal: releases a metadata transfer handle (async)
- */
-DECLINLINE(void) vmdkFileMetaXferRelease(PVMDKIMAGE pImage, PVDMETAXFER pMetaXfer)
-{
-    pImage->pInterfaceIOCallbacks->pfnMetaXferRelease(pImage->pInterfaceIO->pvUser,
-                                                      pMetaXfer);
-}
-
-/**
- * Internal: flush a file (async)
- */
-DECLINLINE(int) vmdkFileFlushAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
-                                   PVDIOCTX pIoCtx)
-{
-    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
-                                                        pVmdkFile->pStorage, pIoCtx,
-                                                        NULL, NULL);
-}
-
-/**
- * Internal: sets the buffer to a specific byte (async)
- */
-DECLINLINE(int) vmdkFileIoCtxSet(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
-                                 int ch, size_t cbSet)
-{
-    return pImage->pInterfaceIOCallbacks->pfnIoCtxSet(pImage->pInterfaceIO->pvUser,
-                                                      pIoCtx, ch, cbSet);
-}
-
-
-static DECLCALLBACK(int) vmdkFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
-{
-    VMDKCOMPRESSIO *pInflateState = (VMDKCOMPRESSIO *)pvUser;
-    size_t cbInjected = 0;
-
-    Assert(cbBuf);
-    if (pInflateState->iOffset < 0)
-    {
-        *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
-        pvBuf = (uint8_t *)pvBuf + 1;
-        cbBuf--;
-        cbInjected = 1;
-        pInflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
-    }
-    if (!cbBuf)
-    {
-        if (pcbBuf)
-            *pcbBuf = cbInjected;
-        return VINF_SUCCESS;
-    }
-    cbBuf = RT_MIN(cbBuf, pInflateState->cbCompGrain - pInflateState->iOffset);
-    memcpy(pvBuf,
-           (uint8_t *)pInflateState->pvCompGrain + pInflateState->iOffset,
-           cbBuf);
-    pInflateState->iOffset += cbBuf;
-    Assert(pcbBuf);
-    *pcbBuf = cbBuf + cbInjected;
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal: read from a file and inflate the compressed data,
- * distinguishing between async and normal operation
- */
-DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                    uint64_t uOffset, void *pvBuf,
-                                    size_t cbToRead, const void *pcvMarker,
-                                    uint64_t *puLBA, uint32_t *pcbMarkerData)
-{
-    if (pExtent->pFile->fAsyncIO)
-    {
-        AssertMsgFailed(("TODO\n"));
-        return VERR_NOT_SUPPORTED;
-    }
-    else
-    {
-        int rc;
-        PRTZIPDECOMP pZip = NULL;
-        VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
-        size_t cbCompSize, cbActuallyRead;
-
-        if (!pcvMarker)
-        {
-            rc = vmdkFileReadSync(pImage, pExtent->pFile, uOffset, pMarker,
-                                  RT_OFFSETOF(VMDKMARKER, uType), NULL);
-            if (RT_FAILURE(rc))
-                return rc;
-        }
-        else
-            memcpy(pMarker, pcvMarker, RT_OFFSETOF(VMDKMARKER, uType));
-
-        cbCompSize = RT_LE2H_U32(pMarker->cbSize);
-        if (cbCompSize == 0)
-        {
-            AssertMsgFailed(("VMDK: corrupted marker\n"));
-            return VERR_VD_VMDK_INVALID_FORMAT;
-        }
-
-        /* Sanity check - the expansion ratio should be much less than 2. */
-        Assert(cbCompSize < 2 * cbToRead);
-        if (cbCompSize >= 2 * cbToRead)
-            return VERR_VD_VMDK_INVALID_FORMAT;
-
-        /* Compressed grain marker. Data follows immediately. */
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              uOffset + RT_OFFSETOF(VMDKMARKER, uType),
-                                (uint8_t *)pExtent->pvCompGrain
-                              + RT_OFFSETOF(VMDKMARKER, uType),
-                                RT_ALIGN_Z(  cbCompSize
-                                           + RT_OFFSETOF(VMDKMARKER, uType),
-                                           512)
-                              - RT_OFFSETOF(VMDKMARKER, uType), NULL);
-
-        if (puLBA)
-            *puLBA = RT_LE2H_U64(pMarker->uSector);
-        if (pcbMarkerData)
-            *pcbMarkerData = RT_ALIGN(  cbCompSize
-                                      + RT_OFFSETOF(VMDKMARKER, uType),
-                                      512);
-
-        VMDKCOMPRESSIO InflateState;
-        InflateState.pImage = pImage;
-        InflateState.iOffset = -1;
-        InflateState.cbCompGrain = cbCompSize + RT_OFFSETOF(VMDKMARKER, uType);
-        InflateState.pvCompGrain = pExtent->pvCompGrain;
-
-        rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
-        if (RT_FAILURE(rc))
-            return rc;
-        rc = RTZipDecompress(pZip, pvBuf, cbToRead, &cbActuallyRead);
-        RTZipDecompDestroy(pZip);
-        if (RT_FAILURE(rc))
-            return rc;
-        if (cbActuallyRead != cbToRead)
-            rc = VERR_VD_VMDK_INVALID_FORMAT;
-        return rc;
-    }
-}
-
-static DECLCALLBACK(int) vmdkFileDeflateHelper(void *pvUser, const void *pvBuf, size_t cbBuf)
-{
-    VMDKCOMPRESSIO *pDeflateState = (VMDKCOMPRESSIO *)pvUser;
-
-    Assert(cbBuf);
-    if (pDeflateState->iOffset < 0)
-    {
-        pvBuf = (const uint8_t *)pvBuf + 1;
-        cbBuf--;
-        pDeflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
-    }
-    if (!cbBuf)
-        return VINF_SUCCESS;
-    if (pDeflateState->iOffset + cbBuf > pDeflateState->cbCompGrain)
-        return VERR_BUFFER_OVERFLOW;
-    memcpy((uint8_t *)pDeflateState->pvCompGrain + pDeflateState->iOffset,
-           pvBuf, cbBuf);
-    pDeflateState->iOffset += cbBuf;
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal: deflate the uncompressed data and write to a file,
- * distinguishing between async and normal operation
- */
-DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                    uint64_t uOffset, const void *pvBuf,
-                                    size_t cbToWrite, uint64_t uLBA,
-                                    uint32_t *pcbMarkerData)
-{
-    if (pExtent->pFile->fAsyncIO)
-    {
-        AssertMsgFailed(("TODO\n"));
-        return VERR_NOT_SUPPORTED;
-    }
-    else
-    {
-        int rc;
-        PRTZIPCOMP pZip = NULL;
-        VMDKCOMPRESSIO DeflateState;
-
-        DeflateState.pImage = pImage;
-        DeflateState.iOffset = -1;
-        DeflateState.cbCompGrain = pExtent->cbCompGrain;
-        DeflateState.pvCompGrain = pExtent->pvCompGrain;
-
-        rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper,
-                             RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
-        if (RT_FAILURE(rc))
-            return rc;
-        rc = RTZipCompress(pZip, pvBuf, cbToWrite);
-        if (RT_SUCCESS(rc))
-            rc = RTZipCompFinish(pZip);
-        RTZipCompDestroy(pZip);
-        if (RT_SUCCESS(rc))
-        {
-            Assert(   DeflateState.iOffset > 0
-                   && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
-
-            /* pad with zeroes to get to a full sector size */
-            uint32_t uSize = DeflateState.iOffset;
-            if (uSize % 512)
-            {
-                uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
-                memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
-                       uSizeAlign - uSize);
-                uSize = uSizeAlign;
-            }
-
-            if (pcbMarkerData)
-                *pcbMarkerData = uSize;
-
-            /* Compressed grain marker. Data follows immediately. */
-            VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
-            pMarker->uSector = RT_H2LE_U64(uLBA);
-            pMarker->cbSize = RT_H2LE_U32(  DeflateState.iOffset
-                                          - RT_OFFSETOF(VMDKMARKER, uType));
-            rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOffset, pMarker,
-                                   uSize, NULL);
-            if (RT_FAILURE(rc))
-                return rc;
-        }
-        return rc;
-    }
-}
-
-
-/**
- * Internal: check if all files are closed, prevent leaking resources.
- */
-static int vmdkFileCheckAllClose(PVMDKIMAGE pImage)
-{
-    int rc = VINF_SUCCESS, rc2;
-    PVMDKFILE pVmdkFile;
-
-    Assert(pImage->pFiles == NULL);
-    for (pVmdkFile = pImage->pFiles;
-         pVmdkFile != NULL;
-         pVmdkFile = pVmdkFile->pNext)
-    {
-        LogRel(("VMDK: leaking reference to file \"%s\"\n",
-                pVmdkFile->pszFilename));
-        pImage->pFiles = pVmdkFile->pNext;
-
-        rc2 = vmdkFileClose(pImage, &pVmdkFile, pVmdkFile->fDelete);
-
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    return rc;
-}
-
-/**
- * Internal: truncate a string (at a UTF8 code point boundary) and encode the
- * critical non-ASCII characters.
- */
-static char *vmdkEncodeString(const char *psz)
-{
-    char szEnc[VMDK_ENCODED_COMMENT_MAX + 3];
-    char *pszDst = szEnc;
-
-    AssertPtr(psz);
-
-    for (; *psz; psz = RTStrNextCp(psz))
-    {
-        char *pszDstPrev = pszDst;
-        RTUNICP Cp = RTStrGetCp(psz);
-        if (Cp == '\\')
-        {
-            pszDst = RTStrPutCp(pszDst, Cp);
-            pszDst = RTStrPutCp(pszDst, Cp);
-        }
-        else if (Cp == '\n')
-        {
-            pszDst = RTStrPutCp(pszDst, '\\');
-            pszDst = RTStrPutCp(pszDst, 'n');
-        }
-        else if (Cp == '\r')
-        {
-            pszDst = RTStrPutCp(pszDst, '\\');
-            pszDst = RTStrPutCp(pszDst, 'r');
-        }
-        else
-            pszDst = RTStrPutCp(pszDst, Cp);
-        if (pszDst - szEnc >= VMDK_ENCODED_COMMENT_MAX - 1)
-        {
-            pszDst = pszDstPrev;
-            break;
-        }
-    }
-    *pszDst = '\0';
-    return RTStrDup(szEnc);
-}
-
-/**
- * Internal: decode a string and store it into the specified string.
- */
-static int vmdkDecodeString(const char *pszEncoded, char *psz, size_t cb)
-{
-    int rc = VINF_SUCCESS;
-    char szBuf[4];
-
-    if (!cb)
-        return VERR_BUFFER_OVERFLOW;
-
-    AssertPtr(psz);
-
-    for (; *pszEncoded; pszEncoded = RTStrNextCp(pszEncoded))
-    {
-        char *pszDst = szBuf;
-        RTUNICP Cp = RTStrGetCp(pszEncoded);
-        if (Cp == '\\')
-        {
-            pszEncoded = RTStrNextCp(pszEncoded);
-            RTUNICP CpQ = RTStrGetCp(pszEncoded);
-            if (CpQ == 'n')
-                RTStrPutCp(pszDst, '\n');
-            else if (CpQ == 'r')
-                RTStrPutCp(pszDst, '\r');
-            else if (CpQ == '\0')
-            {
-                rc = VERR_VD_VMDK_INVALID_HEADER;
-                break;
-            }
-            else
-                RTStrPutCp(pszDst, CpQ);
-        }
-        else
-            pszDst = RTStrPutCp(pszDst, Cp);
-
-        /* Need to leave space for terminating NUL. */
-        if ((size_t)(pszDst - szBuf) + 1 >= cb)
-        {
-            rc = VERR_BUFFER_OVERFLOW;
-            break;
-        }
-        memcpy(psz, szBuf, pszDst - szBuf);
-        psz += pszDst - szBuf;
-    }
-    *psz = '\0';
-    return rc;
-}
-
-/**
- * Internal: free all buffers associated with grain directories.
- */
-static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)
-{
-    if (pExtent->pGD)
-    {
-        RTMemFree(pExtent->pGD);
-        pExtent->pGD = NULL;
-    }
-    if (pExtent->pRGD)
-    {
-        RTMemFree(pExtent->pRGD);
-        pExtent->pRGD = NULL;
-    }
-}
-
-/**
- * Internal: allocate the compressed/uncompressed buffers for streamOptimized
- * images.
- */
-static int vmdkAllocStreamBuffers(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
-{
-    int rc = VINF_SUCCESS;
-
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        /* streamOptimized extents need a compressed grain buffer, which must
-         * be big enough to hold uncompressible data (which needs ~8 bytes
-         * more than the uncompressed data), the marker and padding. */
-        pExtent->cbCompGrain = RT_ALIGN_Z(  VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
-                                          + 8 + sizeof(VMDKMARKER), 512);
-        pExtent->pvCompGrain = RTMemAlloc(pExtent->cbCompGrain);
-        if (!pExtent->pvCompGrain)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        /* streamOptimized extents need a decompressed grain buffer. */
-        pExtent->pvGrain = RTMemAlloc(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
-        if (!pExtent->pvGrain)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeStreamBuffers(pExtent);
-    return rc;
-}
-
-/**
- * Internal: allocate all buffers associated with grain directories.
- */
-static int vmdkAllocGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
-{
-    int rc = VINF_SUCCESS;
-    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
-    uint32_t *pGD = NULL, *pRGD = NULL;
-
-    pGD = (uint32_t *)RTMemAllocZ(cbGD);
-    if (!pGD)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pExtent->pGD = pGD;
-
-    if (pExtent->uSectorRGD)
-    {
-        pRGD = (uint32_t *)RTMemAllocZ(cbGD);
-        if (!pRGD)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-        pExtent->pRGD = pRGD;
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeGrainDirectory(pExtent);
-    return rc;
-}
-
-static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
-{
-    int rc = VINF_SUCCESS;
-    unsigned i;
-    uint32_t *pGDTmp, *pRGDTmp;
-    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
-
-    if (pExtent->enmType != VMDKETYPE_HOSTED_SPARSE)
-        goto out;
-
-    if (   pExtent->uSectorGD == VMDK_GD_AT_END
-        || pExtent->uSectorRGD == VMDK_GD_AT_END)
-    {
-        rc = VERR_INTERNAL_ERROR;
-        goto out;
-    }
-
-    rc = vmdkAllocGrainDirectory(pImage, pExtent);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* The VMDK 1.1 spec seems to talk about compressed grain directories,
-     * but in reality they are not compressed. */
-    rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                          VMDK_SECTOR2BYTE(pExtent->uSectorGD),
-                          pExtent->pGD, cbGD, NULL);
-    AssertRC(rc);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not read grain directory in '%s': %Rrc"), pExtent->pszFullname);
-        goto out;
-    }
-    for (i = 0, pGDTmp = pExtent->pGD; i < pExtent->cGDEntries; i++, pGDTmp++)
-        *pGDTmp = RT_LE2H_U32(*pGDTmp);
-
-    if (pExtent->uSectorRGD)
-    {
-        /* The VMDK 1.1 spec seems to talk about compressed grain directories,
-         * but in reality they are not compressed. */
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
-                              pExtent->pRGD, cbGD, NULL);
-        AssertRC(rc);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not read redundant grain directory in '%s'"), pExtent->pszFullname);
-            goto out;
-        }
-        for (i = 0, pRGDTmp = pExtent->pRGD; i < pExtent->cGDEntries; i++, pRGDTmp++)
-            *pRGDTmp = RT_LE2H_U32(*pRGDTmp);
-
-        /* Check grain table and redundant grain table for consistency. */
-        size_t cbGT = pExtent->cGTEntries * sizeof(uint32_t);
-        uint32_t *pTmpGT1 = (uint32_t *)RTMemTmpAlloc(cbGT);
-        if (!pTmpGT1)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-        uint32_t *pTmpGT2 = (uint32_t *)RTMemTmpAlloc(cbGT);
-        if (!pTmpGT2)
-        {
-            RTMemTmpFree(pTmpGT1);
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        for (i = 0, pGDTmp = pExtent->pGD, pRGDTmp = pExtent->pRGD;
-             i < pExtent->cGDEntries;
-             i++, pGDTmp++, pRGDTmp++)
-        {
-            /* If no grain table is allocated skip the entry. */
-            if (*pGDTmp == 0 && *pRGDTmp == 0)
-                continue;
-
-            if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
-            {
-                /* Just one grain directory entry refers to a not yet allocated
-                 * grain table or both grain directory copies refer to the same
-                 * grain table. Not allowed. */
-                RTMemTmpFree(pTmpGT1);
-                RTMemTmpFree(pTmpGT2);
-                rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);
-                goto out;
-            }
-            /* The VMDK 1.1 spec seems to talk about compressed grain tables,
-             * but in reality they are not compressed. */
-            rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                  VMDK_SECTOR2BYTE(*pGDTmp),
-                                  pTmpGT1, cbGT, NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);
-                RTMemTmpFree(pTmpGT1);
-                RTMemTmpFree(pTmpGT2);
-                goto out;
-            }
-            /* The VMDK 1.1 spec seems to talk about compressed grain tables,
-             * but in reality they are not compressed. */
-            rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                  VMDK_SECTOR2BYTE(*pRGDTmp),
-                                  pTmpGT2, cbGT, NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading backup grain table in '%s'"), pExtent->pszFullname);
-                RTMemTmpFree(pTmpGT1);
-                RTMemTmpFree(pTmpGT2);
-                goto out;
-            }
-            if (memcmp(pTmpGT1, pTmpGT2, cbGT))
-            {
-                RTMemTmpFree(pTmpGT1);
-                RTMemTmpFree(pTmpGT2);
-                rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistency between grain table and backup grain table in '%s'"), pExtent->pszFullname);
-                goto out;
-            }
-        }
-
-        /** @todo figure out what to do for unclean VMDKs. */
-        RTMemTmpFree(pTmpGT1);
-        RTMemTmpFree(pTmpGT2);
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeGrainDirectory(pExtent);
-    return rc;
-}
-
-static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                    uint64_t uStartSector, bool fPreAlloc)
-{
-    int rc = VINF_SUCCESS;
-    unsigned i;
-    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
-    size_t cbGDRounded = RT_ALIGN_64(pExtent->cGDEntries * sizeof(uint32_t), 512);
-    size_t cbGTRounded;
-    uint64_t cbOverhead;
-
-    if (fPreAlloc)
-    {
-        cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
-        cbOverhead =   VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded
-                     + cbGTRounded;
-    }
-    else
-    {
-        /* Use a dummy start sector for layout computation. */
-        if (uStartSector == VMDK_GD_AT_END)
-            uStartSector = 1;
-        cbGTRounded = 0;
-        cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded;
-    }
-
-    /* For streamOptimized extents there is only one grain directory,
-     * and for all others take redundant grain directory into account. */
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        cbOverhead = RT_ALIGN_64(cbOverhead,
-                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
-    }
-    else
-    {
-        cbOverhead += cbGDRounded + cbGTRounded;
-        cbOverhead = RT_ALIGN_64(cbOverhead,
-                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
-        rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead);
-    }
-    if (RT_FAILURE(rc))
-        goto out;
-    pExtent->uAppendPosition = cbOverhead;
-    pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);
-
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        pExtent->uSectorRGD = 0;
-        pExtent->uSectorGD = uStartSector;
-    }
-    else
-    {
-        pExtent->uSectorRGD = uStartSector;
-        pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded);
-    }
-
-    rc = vmdkAllocStreamBuffers(pImage, pExtent);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = vmdkAllocGrainDirectory(pImage, pExtent);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    if (fPreAlloc)
-    {
-        uint32_t uGTSectorLE;
-        uint64_t uOffsetSectors;
-
-        if (pExtent->pRGD)
-        {
-            uOffsetSectors = pExtent->uSectorRGD + VMDK_BYTE2SECTOR(cbGDRounded);
-            for (i = 0; i < pExtent->cGDEntries; i++)
-            {
-                pExtent->pRGD[i] = uOffsetSectors;
-                uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
-                /* Write the redundant grain directory entry to disk. */
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                       VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE),
-                                       &uGTSectorLE, sizeof(uGTSectorLE), NULL);
-                if (RT_FAILURE(rc))
-                {
-                    rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);
-                    goto out;
-                }
-                uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
-            }
-        }
-
-        uOffsetSectors = pExtent->uSectorGD + VMDK_BYTE2SECTOR(cbGDRounded);
-        for (i = 0; i < pExtent->cGDEntries; i++)
-        {
-            pExtent->pGD[i] = uOffsetSectors;
-            uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
-            /* Write the grain directory entry to disk. */
-            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE),
-                                   &uGTSectorLE, sizeof(uGTSectorLE), NULL);
-            if (RT_FAILURE(rc))
-            {
-                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);
-                goto out;
-            }
-            uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
-        }
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeGrainDirectory(pExtent);
-    return rc;
-}
-
-static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr,
-                             char **ppszUnquoted, char **ppszNext)
-{
-    char *pszQ;
-    char *pszUnquoted;
-
-    /* Skip over whitespace. */
-    while (*pszStr == ' ' || *pszStr == '\t')
-        pszStr++;
-
-    if (*pszStr != '"')
-    {
-        pszQ = (char *)pszStr;
-        while (*pszQ && *pszQ != ' ' && *pszQ != '\t')
-            pszQ++;
-    }
-    else
-    {
-        pszStr++;
-        pszQ = (char *)strchr(pszStr, '"');
-        if (pszQ == NULL)
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);
-    }
-
-    pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);
-    if (!pszUnquoted)
-        return VERR_NO_MEMORY;
-    memcpy(pszUnquoted, pszStr, pszQ - pszStr);
-    pszUnquoted[pszQ - pszStr] = '\0';
-    *ppszUnquoted = pszUnquoted;
-    if (ppszNext)
-        *ppszNext = pszQ + 1;
-    return VINF_SUCCESS;
-}
-
-static int vmdkDescInitStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                           const char *pszLine)
-{
-    char *pEnd = pDescriptor->aLines[pDescriptor->cLines];
-    ssize_t cbDiff = strlen(pszLine) + 1;
-
-    if (    pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1
-        &&  pEnd - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
-        return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
-
-    memcpy(pEnd, pszLine, cbDiff);
-    pDescriptor->cLines++;
-    pDescriptor->aLines[pDescriptor->cLines] = pEnd + cbDiff;
-    pDescriptor->fDirty = true;
-
-    return VINF_SUCCESS;
-}
-
-static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,
-                           const char *pszKey, const char **ppszValue)
-{
-    size_t cbKey = strlen(pszKey);
-    const char *pszValue;
-
-    while (uStart != 0)
-    {
-        if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
-        {
-            /* Key matches, check for a '=' (preceded by whitespace). */
-            pszValue = pDescriptor->aLines[uStart] + cbKey;
-            while (*pszValue == ' ' || *pszValue == '\t')
-                pszValue++;
-            if (*pszValue == '=')
-            {
-                *ppszValue = pszValue + 1;
-                break;
-            }
-        }
-        uStart = pDescriptor->aNextLines[uStart];
-    }
-    return !!uStart;
-}
-
-static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                          unsigned uStart,
-                          const char *pszKey, const char *pszValue)
-{
-    char *pszTmp;
-    size_t cbKey = strlen(pszKey);
-    unsigned uLast = 0;
-
-    while (uStart != 0)
-    {
-        if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
-        {
-            /* Key matches, check for a '=' (preceded by whitespace). */
-            pszTmp = pDescriptor->aLines[uStart] + cbKey;
-            while (*pszTmp == ' ' || *pszTmp == '\t')
-                pszTmp++;
-            if (*pszTmp == '=')
-            {
-                pszTmp++;
-                while (*pszTmp == ' ' || *pszTmp == '\t')
-                    pszTmp++;
-                break;
-            }
-        }
-        if (!pDescriptor->aNextLines[uStart])
-            uLast = uStart;
-        uStart = pDescriptor->aNextLines[uStart];
-    }
-    if (uStart)
-    {
-        if (pszValue)
-        {
-            /* Key already exists, replace existing value. */
-            size_t cbOldVal = strlen(pszTmp);
-            size_t cbNewVal = strlen(pszValue);
-            ssize_t cbDiff = cbNewVal - cbOldVal;
-            /* Check for buffer overflow. */
-            if (    pDescriptor->aLines[pDescriptor->cLines]
-                -   pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
-                return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
-
-            memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,
-                    pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);
-            memcpy(pszTmp, pszValue, cbNewVal + 1);
-            for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
-                pDescriptor->aLines[i] += cbDiff;
-        }
-        else
-        {
-            memmove(pDescriptor->aLines[uStart], pDescriptor->aLines[uStart+1],
-                    pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uStart+1] + 1);
-            for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
-            {
-                pDescriptor->aLines[i-1] = pDescriptor->aLines[i];
-                if (pDescriptor->aNextLines[i])
-                    pDescriptor->aNextLines[i-1] = pDescriptor->aNextLines[i] - 1;
-                else
-                    pDescriptor->aNextLines[i-1] = 0;
-            }
-            pDescriptor->cLines--;
-            /* Adjust starting line numbers of following descriptor sections. */
-            if (uStart < pDescriptor->uFirstExtent)
-                pDescriptor->uFirstExtent--;
-            if (uStart < pDescriptor->uFirstDDB)
-                pDescriptor->uFirstDDB--;
-        }
-    }
-    else
-    {
-        /* Key doesn't exist, append after the last entry in this category. */
-        if (!pszValue)
-        {
-            /* Key doesn't exist, and it should be removed. Simply a no-op. */
-            return VINF_SUCCESS;
-        }
-        cbKey = strlen(pszKey);
-        size_t cbValue = strlen(pszValue);
-        ssize_t cbDiff = cbKey + 1 + cbValue + 1;
-        /* Check for buffer overflow. */
-        if (   (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
-            || (  pDescriptor->aLines[pDescriptor->cLines]
-                - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
-            return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
-        for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
-        {
-            pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
-            if (pDescriptor->aNextLines[i - 1])
-                pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
-            else
-                pDescriptor->aNextLines[i] = 0;
-        }
-        uStart = uLast + 1;
-        pDescriptor->aNextLines[uLast] = uStart;
-        pDescriptor->aNextLines[uStart] = 0;
-        pDescriptor->cLines++;
-        pszTmp = pDescriptor->aLines[uStart];
-        memmove(pszTmp + cbDiff, pszTmp,
-                pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
-        memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);
-        pDescriptor->aLines[uStart][cbKey] = '=';
-        memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);
-        for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
-            pDescriptor->aLines[i] += cbDiff;
-
-        /* Adjust starting line numbers of following descriptor sections. */
-        if (uStart <= pDescriptor->uFirstExtent)
-            pDescriptor->uFirstExtent++;
-        if (uStart <= pDescriptor->uFirstDDB)
-            pDescriptor->uFirstDDB++;
-    }
-    pDescriptor->fDirty = true;
-    return VINF_SUCCESS;
-}
-
-static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,
-                              uint32_t *puValue)
-{
-    const char *pszValue;
-
-    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
-                        &pszValue))
-        return VERR_VD_VMDK_VALUE_NOT_FOUND;
-    return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);
-}
-
-static int vmdkDescBaseGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                              const char *pszKey, const char **ppszValue)
-{
-    const char *pszValue;
-    char *pszValueUnquoted;
-
-    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
-                        &pszValue))
-        return VERR_VD_VMDK_VALUE_NOT_FOUND;
-    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    *ppszValue = pszValueUnquoted;
-    return rc;
-}
-
-static int vmdkDescBaseSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                              const char *pszKey, const char *pszValue)
-{
-    char *pszValueQuoted;
-
-    RTStrAPrintf(&pszValueQuoted, "\"%s\"", pszValue);
-    if (!pszValueQuoted)
-        return VERR_NO_STR_MEMORY;
-    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, pszKey,
-                            pszValueQuoted);
-    RTStrFree(pszValueQuoted);
-    return rc;
-}
-
-static void vmdkDescExtRemoveDummy(PVMDKIMAGE pImage,
-                                   PVMDKDESCRIPTOR pDescriptor)
-{
-    unsigned uEntry = pDescriptor->uFirstExtent;
-    ssize_t cbDiff;
-
-    if (!uEntry)
-        return;
-
-    cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;
-    /* Move everything including \0 in the entry marking the end of buffer. */
-    memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],
-            pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);
-    for (unsigned i = uEntry + 1; i <= pDescriptor->cLines; i++)
-    {
-        pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;
-        if (pDescriptor->aNextLines[i])
-            pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;
-        else
-            pDescriptor->aNextLines[i - 1] = 0;
-    }
-    pDescriptor->cLines--;
-    if (pDescriptor->uFirstDDB)
-        pDescriptor->uFirstDDB--;
-
-    return;
-}
-
-static int vmdkDescExtInsert(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                             VMDKACCESS enmAccess, uint64_t cNominalSectors,
-                             VMDKETYPE enmType, const char *pszBasename,
-                             uint64_t uSectorOffset)
-{
-    static const char *apszAccess[] = { "NOACCESS", "RDONLY", "RW" };
-    static const char *apszType[] = { "", "SPARSE", "FLAT", "ZERO", "VMFS" };
-    char *pszTmp;
-    unsigned uStart = pDescriptor->uFirstExtent, uLast = 0;
-    char szExt[1024];
-    ssize_t cbDiff;
-
-    Assert((unsigned)enmAccess < RT_ELEMENTS(apszAccess));
-    Assert((unsigned)enmType < RT_ELEMENTS(apszType));
-
-    /* Find last entry in extent description. */
-    while (uStart)
-    {
-        if (!pDescriptor->aNextLines[uStart])
-            uLast = uStart;
-        uStart = pDescriptor->aNextLines[uStart];
-    }
-
-    if (enmType == VMDKETYPE_ZERO)
-    {
-        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s ", apszAccess[enmAccess],
-                    cNominalSectors, apszType[enmType]);
-    }
-    else if (enmType == VMDKETYPE_FLAT)
-    {
-        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\" %llu",
-                    apszAccess[enmAccess], cNominalSectors,
-                    apszType[enmType], pszBasename, uSectorOffset);
-    }
-    else
-    {
-        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\"",
-                    apszAccess[enmAccess], cNominalSectors,
-                    apszType[enmType], pszBasename);
-    }
-    cbDiff = strlen(szExt) + 1;
-
-    /* Check for buffer overflow. */
-    if (   (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
-        || (  pDescriptor->aLines[pDescriptor->cLines]
-            - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
-        return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
-
-    for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
-    {
-        pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
-        if (pDescriptor->aNextLines[i - 1])
-            pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
-        else
-            pDescriptor->aNextLines[i] = 0;
-    }
-    uStart = uLast + 1;
-    pDescriptor->aNextLines[uLast] = uStart;
-    pDescriptor->aNextLines[uStart] = 0;
-    pDescriptor->cLines++;
-    pszTmp = pDescriptor->aLines[uStart];
-    memmove(pszTmp + cbDiff, pszTmp,
-            pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
-    memcpy(pDescriptor->aLines[uStart], szExt, cbDiff);
-    for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
-        pDescriptor->aLines[i] += cbDiff;
-
-    /* Adjust starting line numbers of following descriptor sections. */
-    if (uStart <= pDescriptor->uFirstDDB)
-        pDescriptor->uFirstDDB++;
-
-    pDescriptor->fDirty = true;
-    return VINF_SUCCESS;
-}
-
-static int vmdkDescDDBGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                             const char *pszKey, const char **ppszValue)
-{
-    const char *pszValue;
-    char *pszValueUnquoted;
-
-    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                        &pszValue))
-        return VERR_VD_VMDK_VALUE_NOT_FOUND;
-    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    *ppszValue = pszValueUnquoted;
-    return rc;
-}
-
-static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                             const char *pszKey, uint32_t *puValue)
-{
-    const char *pszValue;
-    char *pszValueUnquoted;
-
-    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                        &pszValue))
-        return VERR_VD_VMDK_VALUE_NOT_FOUND;
-    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);
-    RTMemTmpFree(pszValueUnquoted);
-    return rc;
-}
-
-static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                              const char *pszKey, PRTUUID pUuid)
-{
-    const char *pszValue;
-    char *pszValueUnquoted;
-
-    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                        &pszValue))
-        return VERR_VD_VMDK_VALUE_NOT_FOUND;
-    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = RTUuidFromStr(pUuid, pszValueUnquoted);
-    RTMemTmpFree(pszValueUnquoted);
-    return rc;
-}
-
-static int vmdkDescDDBSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                             const char *pszKey, const char *pszVal)
-{
-    int rc;
-    char *pszValQuoted;
-
-    if (pszVal)
-    {
-        RTStrAPrintf(&pszValQuoted, "\"%s\"", pszVal);
-        if (!pszValQuoted)
-            return VERR_NO_STR_MEMORY;
-    }
-    else
-        pszValQuoted = NULL;
-    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                        pszValQuoted);
-    if (pszValQuoted)
-        RTStrFree(pszValQuoted);
-    return rc;
-}
-
-static int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                              const char *pszKey, PCRTUUID pUuid)
-{
-    char *pszUuid;
-
-    RTStrAPrintf(&pszUuid, "\"%RTuuid\"", pUuid);
-    if (!pszUuid)
-        return VERR_NO_STR_MEMORY;
-    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                            pszUuid);
-    RTStrFree(pszUuid);
-    return rc;
-}
-
-static int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
-                             const char *pszKey, uint32_t uValue)
-{
-    char *pszValue;
-
-    RTStrAPrintf(&pszValue, "\"%d\"", uValue);
-    if (!pszValue)
-        return VERR_NO_STR_MEMORY;
-    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
-                            pszValue);
-    RTStrFree(pszValue);
-    return rc;
-}
-
-static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData,
-                                    size_t cbDescData,
-                                    PVMDKDESCRIPTOR pDescriptor)
-{
-    int rc = VINF_SUCCESS;
-    unsigned cLine = 0, uLastNonEmptyLine = 0;
-    char *pTmp = pDescData;
-
-    pDescriptor->cbDescAlloc = cbDescData;
-    while (*pTmp != '\0')
-    {
-        pDescriptor->aLines[cLine++] = pTmp;
-        if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)
-        {
-            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-
-        while (*pTmp != '\0' && *pTmp != '\n')
-        {
-            if (*pTmp == '\r')
-            {
-                if (*(pTmp + 1) != '\n')
-                {
-                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor in '%s'"), pImage->pszFilename);
-                    goto out;
-                }
-                else
-                {
-                    /* Get rid of CR character. */
-                    *pTmp = '\0';
-                }
-            }
-            pTmp++;
-        }
-        /* Get rid of LF character. */
-        if (*pTmp == '\n')
-        {
-            *pTmp = '\0';
-            pTmp++;
-        }
-    }
-    pDescriptor->cLines = cLine;
-    /* Pointer right after the end of the used part of the buffer. */
-    pDescriptor->aLines[cLine] = pTmp;
-
-    if (    strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile")
-        &&  strcmp(pDescriptor->aLines[0], "# Disk Descriptor File"))
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Initialize those, because we need to be able to reopen an image. */
-    pDescriptor->uFirstDesc = 0;
-    pDescriptor->uFirstExtent = 0;
-    pDescriptor->uFirstDDB = 0;
-    for (unsigned i = 0; i < cLine; i++)
-    {
-        if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
-        {
-            if (    !strncmp(pDescriptor->aLines[i], "RW", 2)
-                ||  !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
-                ||  !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
-            {
-                /* An extent descriptor. */
-                if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
-                {
-                    /* Incorrect ordering of entries. */
-                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
-                    goto out;
-                }
-                if (!pDescriptor->uFirstExtent)
-                {
-                    pDescriptor->uFirstExtent = i;
-                    uLastNonEmptyLine = 0;
-                }
-            }
-            else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
-            {
-                /* A disk database entry. */
-                if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
-                {
-                    /* Incorrect ordering of entries. */
-                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
-                    goto out;
-                }
-                if (!pDescriptor->uFirstDDB)
-                {
-                    pDescriptor->uFirstDDB = i;
-                    uLastNonEmptyLine = 0;
-                }
-            }
-            else
-            {
-                /* A normal entry. */
-                if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
-                {
-                    /* Incorrect ordering of entries. */
-                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
-                    goto out;
-                }
-                if (!pDescriptor->uFirstDesc)
-                {
-                    pDescriptor->uFirstDesc = i;
-                    uLastNonEmptyLine = 0;
-                }
-            }
-            if (uLastNonEmptyLine)
-                pDescriptor->aNextLines[uLastNonEmptyLine] = i;
-            uLastNonEmptyLine = i;
-        }
-    }
-
-out:
-    return rc;
-}
-
-static int vmdkDescSetPCHSGeometry(PVMDKIMAGE pImage,
-                                   PCVDGEOMETRY pPCHSGeometry)
-{
-    int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_CYLINDERS,
-                           pPCHSGeometry->cCylinders);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_HEADS,
-                           pPCHSGeometry->cHeads);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_SECTORS,
-                           pPCHSGeometry->cSectors);
-    return rc;
-}
-
-static int vmdkDescSetLCHSGeometry(PVMDKIMAGE pImage,
-                                   PCVDGEOMETRY pLCHSGeometry)
-{
-    int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_CYLINDERS,
-                           pLCHSGeometry->cCylinders);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_HEADS,
-
-                           pLCHSGeometry->cHeads);
-    if (RT_FAILURE(rc))
-        return rc;
-    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_SECTORS,
-                           pLCHSGeometry->cSectors);
-    return rc;
-}
-
-static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData,
-                                size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
-{
-    int rc;
-
-    pDescriptor->uFirstDesc = 0;
-    pDescriptor->uFirstExtent = 0;
-    pDescriptor->uFirstDDB = 0;
-    pDescriptor->cLines = 0;
-    pDescriptor->cbDescAlloc = cbDescData;
-    pDescriptor->fDirty = false;
-    pDescriptor->aLines[pDescriptor->cLines] = pDescData;
-    memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));
-
-    rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");
-    if (RT_FAILURE(rc))
-        goto out;
-    pDescriptor->uFirstDesc = pDescriptor->cLines - 1;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");
-    if (RT_FAILURE(rc))
-        goto out;
-    pDescriptor->uFirstExtent = pDescriptor->cLines - 1;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "");
-    if (RT_FAILURE(rc))
-        goto out;
-    /* The trailing space is created by VMware, too. */
-    rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "");
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");
-    if (RT_FAILURE(rc))
-        goto out;
-    pDescriptor->uFirstDDB = pDescriptor->cLines - 1;
-
-    /* Now that the framework is in place, use the normal functions to insert
-     * the remaining keys. */
-    char szBuf[9];
-    RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());
-    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
-                        "CID", szBuf);
-    if (RT_FAILURE(rc))
-        goto out;
-    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
-                        "parentCID", "ffffffff");
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");
-    if (RT_FAILURE(rc))
-        goto out;
-
-out:
-    return rc;
-}
-
-static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData,
-                               size_t cbDescData)
-{
-    int rc;
-    unsigned cExtents;
-    unsigned uLine;
-    unsigned i;
-
-    rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData,
-                                  &pImage->Descriptor);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /* Check version, must be 1. */
-    uint32_t uVersion;
-    rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);
-    if (uVersion != 1)
-        return vmdkError(pImage, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);
-
-    /* Get image creation type and determine image flags. */
-    const char *pszCreateType = NULL;   /* initialized to make gcc shut up */
-    rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",
-                            &pszCreateType);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);
-    if (    !strcmp(pszCreateType, "twoGbMaxExtentSparse")
-        ||  !strcmp(pszCreateType, "twoGbMaxExtentFlat"))
-        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
-    else if (   !strcmp(pszCreateType, "partitionedDevice")
-             || !strcmp(pszCreateType, "fullDevice"))
-        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_RAWDISK;
-    else if (!strcmp(pszCreateType, "streamOptimized"))
-        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
-    else if (!strcmp(pszCreateType, "vmfs"))
-        pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_ESX;
-    RTStrFree((char *)(void *)pszCreateType);
-
-    /* Count the number of extent config entries. */
-    for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
-         uLine != 0;
-         uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
-        /* nothing */;
-
-    if (!pImage->pDescData && cExtents != 1)
-    {
-        /* Monolithic image, must have only one extent (already opened). */
-        return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent in '%s'"), pImage->pszFilename);
-    }
-
-    if (pImage->pDescData)
-    {
-        /* Non-monolithic image, extents need to be allocated. */
-        rc = vmdkCreateExtents(pImage, cExtents);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    for (i = 0, uLine = pImage->Descriptor.uFirstExtent;
-         i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
-    {
-        char *pszLine = pImage->Descriptor.aLines[uLine];
-
-        /* Access type of the extent. */
-        if (!strncmp(pszLine, "RW", 2))
-        {
-            pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
-            pszLine += 2;
-        }
-        else if (!strncmp(pszLine, "RDONLY", 6))
-        {
-            pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
-            pszLine += 6;
-        }
-        else if (!strncmp(pszLine, "NOACCESS", 8))
-        {
-            pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
-            pszLine += 8;
-        }
-        else
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-        if (*pszLine++ != ' ')
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-
-        /* Nominal size of the extent. */
-        rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
-                             &pImage->pExtents[i].cNominalSectors);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-        if (*pszLine++ != ' ')
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-
-        /* Type of the extent. */
-#ifdef VBOX_WITH_VMDK_ESX
-        /** @todo Add the ESX extent types. Not necessary for now because
-         * the ESX extent types are only used inside an ESX server. They are
-         * automatically converted if the VMDK is exported. */
-#endif /* VBOX_WITH_VMDK_ESX */
-        if (!strncmp(pszLine, "SPARSE", 6))
-        {
-            pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
-            pszLine += 6;
-        }
-        else if (!strncmp(pszLine, "FLAT", 4))
-        {
-            pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
-            pszLine += 4;
-        }
-        else if (!strncmp(pszLine, "ZERO", 4))
-        {
-            pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
-            pszLine += 4;
-        }
-        else if (!strncmp(pszLine, "VMFS", 4))
-        {
-            pImage->pExtents[i].enmType = VMDKETYPE_VMFS;
-            pszLine += 4;
-        }
-        else
-            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-
-        if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
-        {
-            /* This one has no basename or offset. */
-            if (*pszLine == ' ')
-                pszLine++;
-            if (*pszLine != '\0')
-                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-            pImage->pExtents[i].pszBasename = NULL;
-        }
-        else
-        {
-            /* All other extent types have basename and optional offset. */
-            if (*pszLine++ != ' ')
-                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-
-            /* Basename of the image. Surrounded by quotes. */
-            char *pszBasename;
-            rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);
-            if (RT_FAILURE(rc))
-                return rc;
-            pImage->pExtents[i].pszBasename = pszBasename;
-            if (*pszLine == ' ')
-            {
-                pszLine++;
-                if (*pszLine != '\0')
-                {
-                    /* Optional offset in extent specified. */
-                    rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
-                                         &pImage->pExtents[i].uSectorOffset);
-                    if (RT_FAILURE(rc))
-                        return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-                }
-            }
-
-            if (*pszLine != '\0')
-                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
-        }
-    }
-
-    /* Determine PCHS geometry (autogenerate if necessary). */
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_CYLINDERS,
-                           &pImage->PCHSGeometry.cCylinders);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->PCHSGeometry.cCylinders = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_HEADS,
-                           &pImage->PCHSGeometry.cHeads);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->PCHSGeometry.cHeads = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_PCHS_SECTORS,
-                           &pImage->PCHSGeometry.cSectors);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->PCHSGeometry.cSectors = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    if (    pImage->PCHSGeometry.cCylinders == 0
-        ||  pImage->PCHSGeometry.cHeads == 0
-        ||  pImage->PCHSGeometry.cHeads > 16
-        ||  pImage->PCHSGeometry.cSectors == 0
-        ||  pImage->PCHSGeometry.cSectors > 63)
-    {
-        /* Mark PCHS geometry as not yet valid (can't do the calculation here
-         * as the total image size isn't known yet). */
-        pImage->PCHSGeometry.cCylinders = 0;
-        pImage->PCHSGeometry.cHeads = 16;
-        pImage->PCHSGeometry.cSectors = 63;
-    }
-
-    /* Determine LCHS geometry (set to 0 if not specified). */
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_CYLINDERS,
-                           &pImage->LCHSGeometry.cCylinders);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->LCHSGeometry.cCylinders = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_HEADS,
-                           &pImage->LCHSGeometry.cHeads);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->LCHSGeometry.cHeads = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
-                           VMDK_DDB_GEO_LCHS_SECTORS,
-                           &pImage->LCHSGeometry.cSectors);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-        pImage->LCHSGeometry.cSectors = 0;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
-    if (    pImage->LCHSGeometry.cCylinders == 0
-        ||  pImage->LCHSGeometry.cHeads == 0
-        ||  pImage->LCHSGeometry.cSectors == 0)
-    {
-        pImage->LCHSGeometry.cCylinders = 0;
-        pImage->LCHSGeometry.cHeads = 0;
-        pImage->LCHSGeometry.cSectors = 0;
-    }
-
-    /* Get image UUID. */
-    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID,
-                            &pImage->ImageUuid);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-    {
-        /* Image without UUID. Probably created by VMware and not yet used
-         * by VirtualBox. Can only be added for images opened in read/write
-         * mode, so don't bother producing a sensible UUID otherwise. */
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            RTUuidClear(&pImage->ImageUuid);
-        else
-        {
-            rc = RTUuidCreate(&pImage->ImageUuid);
-            if (RT_FAILURE(rc))
-                return rc;
-            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                    VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
-        }
-    }
-    else if (RT_FAILURE(rc))
-        return rc;
-
-    /* Get image modification UUID. */
-    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_MODIFICATION_UUID,
-                            &pImage->ModificationUuid);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-    {
-        /* Image without UUID. Probably created by VMware and not yet used
-         * by VirtualBox. Can only be added for images opened in read/write
-         * mode, so don't bother producing a sensible UUID otherwise. */
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            RTUuidClear(&pImage->ModificationUuid);
-        else
-        {
-            rc = RTUuidCreate(&pImage->ModificationUuid);
-            if (RT_FAILURE(rc))
-                return rc;
-            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                    VMDK_DDB_MODIFICATION_UUID,
-                                    &pImage->ModificationUuid);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);
-        }
-    }
-    else if (RT_FAILURE(rc))
-        return rc;
-
-    /* Get UUID of parent image. */
-    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID,
-                            &pImage->ParentUuid);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-    {
-        /* Image without UUID. Probably created by VMware and not yet used
-         * by VirtualBox. Can only be added for images opened in read/write
-         * mode, so don't bother producing a sensible UUID otherwise. */
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            RTUuidClear(&pImage->ParentUuid);
-        else
-        {
-            rc = RTUuidClear(&pImage->ParentUuid);
-            if (RT_FAILURE(rc))
-                return rc;
-            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                    VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);
-        }
-    }
-    else if (RT_FAILURE(rc))
-        return rc;
-
-    /* Get parent image modification UUID. */
-    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_PARENT_MODIFICATION_UUID,
-                            &pImage->ParentModificationUuid);
-    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-    {
-        /* Image without UUID. Probably created by VMware and not yet used
-         * by VirtualBox. Can only be added for images opened in read/write
-         * mode, so don't bother producing a sensible UUID otherwise. */
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            RTUuidClear(&pImage->ParentModificationUuid);
-        else
-        {
-            rc = RTUuidCreate(&pImage->ParentModificationUuid);
-            if (RT_FAILURE(rc))
-                return rc;
-            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                    VMDK_DDB_PARENT_MODIFICATION_UUID,
-                                    &pImage->ParentModificationUuid);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in descriptor in '%s'"), pImage->pszFilename);
-        }
-    }
-    else if (RT_FAILURE(rc))
-        return rc;
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal : Prepares the descriptor to write to the image.
- */
-static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
-                                 void **ppvData, size_t *pcbData)
-{
-    int rc = VINF_SUCCESS;
-
-    /*
-     * Allocate temporary descriptor buffer.
-     * In case there is no limit allocate a default
-     * and increase if required.
-     */
-    size_t cbDescriptor = cbLimit ? cbLimit : 4 * _1K;
-    char *pszDescriptor = (char *)RTMemAllocZ(cbDescriptor);
-    unsigned offDescriptor = 0;
-
-    if (!pszDescriptor)
-        return VERR_NO_MEMORY;
-
-    for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
-    {
-        const char *psz = pImage->Descriptor.aLines[i];
-        size_t cb = strlen(psz);
-
-        /*
-         * Increase the descriptor if there is no limit and
-         * there is not enough room left for this line.
-         */
-        if (offDescriptor + cb + 1 > cbDescriptor)
-        {
-            if (cbLimit)
-            {
-                rc = vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);
-                break;
-            }
-            else
-            {
-                char *pszDescriptorNew = NULL;
-                LogFlow(("Increasing descriptor cache\n"));
-
-                pszDescriptorNew = (char *)RTMemRealloc(pszDescriptor, cbDescriptor + cb + 4 * _1K);
-                if (!pszDescriptorNew)
-                {
-                    RTMemFree(pszDescriptor);
-                    rc = VERR_NO_MEMORY;
-                    break;
-                }
-                pszDescriptor = pszDescriptorNew;
-                cbDescriptor += cb + 4 * _1K;
-            }
-        }
-
-        if (cb > 0)
-        {
-            memcpy(pszDescriptor + offDescriptor, psz, cb);
-            offDescriptor += cb;
-        }
-
-        memcpy(pszDescriptor + offDescriptor, "\n", 1);
-        offDescriptor++;
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        *ppvData = pszDescriptor;
-        *pcbData = offDescriptor;
-    }
-
-    return rc;
-}
-
-/**
- * Internal: write/update the descriptor part of the image.
- */
-static int vmdkWriteDescriptor(PVMDKIMAGE pImage)
-{
-    int rc = VINF_SUCCESS;
-    uint64_t cbLimit;
-    uint64_t uOffset;
-    PVMDKFILE pDescFile;
-    void *pvDescriptor;
-    size_t cbDescriptor;
-
-    if (pImage->pDescData)
-    {
-        /* Separate descriptor file. */
-        uOffset = 0;
-        cbLimit = 0;
-        pDescFile = pImage->pFile;
-    }
-    else
-    {
-        /* Embedded descriptor file. */
-        uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
-        cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
-        pDescFile = pImage->pExtents[0].pFile;
-    }
-    /* Bail out if there is no file to write to. */
-    if (pDescFile == NULL)
-        return VERR_INVALID_PARAMETER;
-
-    rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
-    if (RT_SUCCESS(rc))
-    {
-        rc = vmdkFileWriteSync(pImage, pDescFile, uOffset, pvDescriptor, cbLimit ? cbLimit : cbDescriptor, NULL);
-        if (RT_FAILURE(rc))
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
-    }
-
-    if (RT_SUCCESS(rc) && !cbLimit)
-    {
-        rc = vmdkFileSetSize(pImage, pDescFile, cbDescriptor);
-        if (RT_FAILURE(rc))
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
-    }
-
-    if (RT_SUCCESS(rc))
-        pImage->Descriptor.fDirty = false;
-
-    RTMemFree(pvDescriptor);
-    return rc;
-}
-
-/**
- * Internal: write/update the descriptor part of the image - async version.
- */
-static int vmdkWriteDescriptorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
-{
-    int rc = VINF_SUCCESS;
-    uint64_t cbLimit;
-    uint64_t uOffset;
-    PVMDKFILE pDescFile;
-    void *pvDescriptor;
-    size_t cbDescriptor;
-
-    if (pImage->pDescData)
-    {
-        /* Separate descriptor file. */
-        uOffset = 0;
-        cbLimit = 0;
-        pDescFile = pImage->pFile;
-    }
-    else
-    {
-        /* Embedded descriptor file. */
-        uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
-        cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
-        pDescFile = pImage->pExtents[0].pFile;
-    }
-    /* Bail out if there is no file to write to. */
-    if (pDescFile == NULL)
-        return VERR_INVALID_PARAMETER;
-
-    rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
-    if (RT_SUCCESS(rc))
-    {
-        rc = vmdkFileWriteMetaAsync(pImage, pDescFile, uOffset, pvDescriptor, cbLimit ? cbLimit : cbDescriptor, pIoCtx, NULL, NULL);
-        if (   RT_FAILURE(rc)
-            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
-    }
-
-    if (RT_SUCCESS(rc) && !cbLimit)
-    {
-        rc = vmdkFileSetSize(pImage, pDescFile, cbDescriptor);
-        if (RT_FAILURE(rc))
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
-    }
-
-    if (RT_SUCCESS(rc))
-        pImage->Descriptor.fDirty = false;
-
-    RTMemFree(pvDescriptor);
-    return rc;
-
-}
-
-/**
- * Internal: validate the consistency check values in a binary header.
- */
-static int vmdkValidateHeader(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, const SparseExtentHeader *pHeader)
-{
-    int rc = VINF_SUCCESS;
-    if (RT_LE2H_U32(pHeader->magicNumber) != VMDK_SPARSE_MAGICNUMBER)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic in sparse extent header in '%s'"), pExtent->pszFullname);
-        return rc;
-    }
-    if (RT_LE2H_U32(pHeader->version) != 1 && RT_LE2H_U32(pHeader->version) != 3)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: incorrect version in sparse extent header in '%s', not a VMDK 1.0/1.1 conforming file"), pExtent->pszFullname);
-        return rc;
-    }
-    if (    (RT_LE2H_U32(pHeader->flags) & 1)
-        &&  (   pHeader->singleEndLineChar != '\n'
-             || pHeader->nonEndLineChar != ' '
-             || pHeader->doubleEndLineChar1 != '\r'
-             || pHeader->doubleEndLineChar2 != '\n') )
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);
-        return rc;
-    }
-    return rc;
-}
-
-/**
- * Internal: read metadata belonging to an extent with binary header, i.e.
- * as found in monolithic files.
- */
-static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                    bool fMagicAlreadyRead)
-{
-    SparseExtentHeader Header;
-    uint64_t cSectorsPerGDE;
-    uint64_t cbFile = 0;
-    int rc;
-
-    if (!fMagicAlreadyRead)
-        rc = vmdkFileReadSync(pImage, pExtent->pFile, 0, &Header,
-                              sizeof(Header), NULL);
-    else
-    {
-        Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              RT_OFFSETOF(SparseExtentHeader, version),
-                              &Header.version,
-                                sizeof(Header)
-                              - RT_OFFSETOF(SparseExtentHeader, version),
-                              NULL);
-    }
-    AssertRC(rc);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-    rc = vmdkValidateHeader(pImage, pExtent, &Header);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    if (    RT_LE2H_U32(Header.flags & RT_BIT(17))
-        &&  RT_LE2H_U64(Header.gdOffset) == VMDK_GD_AT_END)
-        pExtent->fFooter = true;
-
-    if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        || (   pExtent->fFooter
-            && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbFile);
-        AssertRC(rc);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname);
-            goto out;
-        }
-    }
-
-    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        pExtent->uAppendPosition = RT_ALIGN_64(cbFile, 512);
-
-    if (   pExtent->fFooter
-        && (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        /* Read the footer, which comes before the end-of-stream marker. */
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              cbFile - 2*512, &Header,
-                              sizeof(Header), NULL);
-        AssertRC(rc);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent footer in '%s'"), pExtent->pszFullname);
-            goto out;
-        }
-        rc = vmdkValidateHeader(pImage, pExtent, &Header);
-        if (RT_FAILURE(rc))
-            goto out;
-        /* Prohibit any writes to this extent. */
-        pExtent->uAppendPosition = 0;
-    }
-
-    pExtent->uVersion = RT_LE2H_U32(Header.version);
-    pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; /* Just dummy value, changed later. */
-    pExtent->cSectors = RT_LE2H_U64(Header.capacity);
-    pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
-    pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
-    pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
-    if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-    pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
-    if (RT_LE2H_U32(Header.flags) & RT_BIT(1))
-    {
-        pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
-        pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
-    }
-    else
-    {
-        pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
-        pExtent->uSectorRGD = 0;
-    }
-    if (   (   pExtent->uSectorGD == VMDK_GD_AT_END
-            || pExtent->uSectorRGD == VMDK_GD_AT_END)
-        && (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot resolve grain directory offset in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-    pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);
-    pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
-    pExtent->uCompression = RT_LE2H_U16(Header.compressAlgorithm);
-    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
-    if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-    pExtent->cSectorsPerGDE = cSectorsPerGDE;
-    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
-
-    /* Fix up the number of descriptor sectors, as some flat images have
-     * really just one, and this causes failures when inserting the UUID
-     * values and other extra information. */
-    if (pExtent->cDescriptorSectors != 0 && pExtent->cDescriptorSectors < 4)
-    {
-        /* Do it the easy way - just fix it for flat images which have no
-         * other complicated metadata which needs space too. */
-        if (    pExtent->uDescriptorSector + 4 < pExtent->cOverheadSectors
-            &&  pExtent->cGTEntries * pExtent->cGDEntries == 0)
-            pExtent->cDescriptorSectors = 4;
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeExtentData(pImage, pExtent, false);
-
-    return rc;
-}
-
-/**
- * Internal: read additional metadata belonging to an extent. For those
- * extents which have no additional metadata just verify the information.
- */
-static int vmdkReadMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
-{
-    int rc = VINF_SUCCESS;
-
-/* disabled the check as there are too many truncated vmdk images out there */
-#ifdef VBOX_WITH_VMDK_STRICT_SIZE_CHECK
-    uint64_t cbExtentSize;
-    /* The image must be a multiple of a sector in size and contain the data
-     * area (flat images only). If not, it means the image is at least
-     * truncated, or even seriously garbled. */
-    rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-    if (    cbExtentSize != RT_ALIGN_64(cbExtentSize, 512)
-        &&  (pExtent->enmType != VMDKETYPE_FLAT || pExtent->cNominalSectors + pExtent->uSectorOffset > VMDK_BYTE2SECTOR(cbExtentSize)))
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: file size is not a multiple of 512 in '%s', file is truncated or otherwise garbled"), pExtent->pszFullname);
-        goto out;
-    }
-#endif /* VBOX_WITH_VMDK_STRICT_SIZE_CHECK */
-    if (pExtent->enmType != VMDKETYPE_HOSTED_SPARSE)
-        goto out;
-
-    /* The spec says that this must be a power of two and greater than 8,
-     * but probably they meant not less than 8. */
-    if (    (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
-        ||  pExtent->cSectorsPerGrain < 8)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
-        goto out;
-    }
-
-    /* This code requires that a grain table must hold a power of two multiple
-     * of the number of entries per GT cache entry. */
-    if (    (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
-        ||  pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
-    {
-        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);
-        goto out;
-    }
-
-    rc = vmdkAllocStreamBuffers(pImage, pExtent);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Prohibit any writes to this streamOptimized extent. */
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        pExtent->uAppendPosition = 0;
-
-    if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
-        rc = vmdkReadGrainDirectory(pImage, pExtent);
-    else
-    {
-        pExtent->uGrainSectorAbs = pExtent->cOverheadSectors;
-        pExtent->cbGrainStreamRead = 0;
-    }
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeExtentData(pImage, pExtent, false);
-
-    return rc;
-}
-
-/**
- * Internal: write/update the metadata for a sparse extent.
- */
-static int vmdkWriteMetaSparseExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                     uint64_t uOffset)
-{
-    SparseExtentHeader Header;
-
-    memset(&Header, '\0', sizeof(Header));
-    Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
-    Header.version = RT_H2LE_U32(pExtent->uVersion);
-    Header.flags = RT_H2LE_U32(RT_BIT(0));
-    if (pExtent->pRGD)
-        Header.flags |= RT_H2LE_U32(RT_BIT(1));
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
-    Header.capacity = RT_H2LE_U64(pExtent->cSectors);
-    Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
-    Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
-    Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
-    Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
-    if (pExtent->fFooter && uOffset == 0)
-    {
-        if (pExtent->pRGD)
-        {
-            Assert(pExtent->uSectorRGD);
-            Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-        }
-        else
-        {
-            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-        }
-    }
-    else
-    {
-        if (pExtent->pRGD)
-        {
-            Assert(pExtent->uSectorRGD);
-            Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
-            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
-        }
-        else
-        {
-            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
-        }
-    }
-    Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
-    Header.uncleanShutdown = pExtent->fUncleanShutdown;
-    Header.singleEndLineChar = '\n';
-    Header.nonEndLineChar = ' ';
-    Header.doubleEndLineChar1 = '\r';
-    Header.doubleEndLineChar2 = '\n';
-    Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
-
-    int rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOffset, &Header, sizeof(Header), NULL);
-    AssertRC(rc);
-    if (RT_FAILURE(rc))
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
-    return rc;
-}
-
-/**
- * Internal: write/update the metadata for a sparse extent - async version.
- */
-static int vmdkWriteMetaSparseExtentAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                          uint64_t uOffset, PVDIOCTX pIoCtx)
-{
-    SparseExtentHeader Header;
-
-    memset(&Header, '\0', sizeof(Header));
-    Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
-    Header.version = RT_H2LE_U32(pExtent->uVersion);
-    Header.flags = RT_H2LE_U32(RT_BIT(0));
-    if (pExtent->pRGD)
-        Header.flags |= RT_H2LE_U32(RT_BIT(1));
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
-    Header.capacity = RT_H2LE_U64(pExtent->cSectors);
-    Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
-    Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
-    Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
-    Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
-    if (pExtent->fFooter && uOffset == 0)
-    {
-        if (pExtent->pRGD)
-        {
-            Assert(pExtent->uSectorRGD);
-            Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-        }
-        else
-        {
-            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
-        }
-    }
-    else
-    {
-        if (pExtent->pRGD)
-        {
-            Assert(pExtent->uSectorRGD);
-            Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
-            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
-        }
-        else
-        {
-            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
-        }
-    }
-    Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
-    Header.uncleanShutdown = pExtent->fUncleanShutdown;
-    Header.singleEndLineChar = '\n';
-    Header.nonEndLineChar = ' ';
-    Header.doubleEndLineChar1 = '\r';
-    Header.doubleEndLineChar2 = '\n';
-    Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
-
-    int rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                    uOffset, &Header, sizeof(Header),
-                                    pIoCtx, NULL, NULL);
-    if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
-    return rc;
-}
-
-#ifdef VBOX_WITH_VMDK_ESX
-/**
- * Internal: unused code to read the metadata of a sparse ESX extent.
- *
- * Such extents never leave ESX server, so this isn't ever used.
- */
-static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
-{
-    COWDisk_Header Header;
-    uint64_t cSectorsPerGDE;
-
-    int rc = vmdkFileReadSync(pImage, pExtent->pFile, 0, &Header, sizeof(Header), NULL);
-    AssertRC(rc);
-    if (RT_FAILURE(rc))
-        goto out;
-    if (    RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER
-        ||  RT_LE2H_U32(Header.version) != 1
-        ||  RT_LE2H_U32(Header.flags) != 3)
-    {
-        rc = VERR_VD_VMDK_INVALID_HEADER;
-        goto out;
-    }
-    pExtent->enmType = VMDKETYPE_ESX_SPARSE;
-    pExtent->cSectors = RT_LE2H_U32(Header.numSectors);
-    pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);
-    /* The spec says that this must be between 1 sector and 1MB. This code
-     * assumes it's a power of two, so check that requirement, too. */
-    if (    (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
-        ||  pExtent->cSectorsPerGrain == 0
-        ||  pExtent->cSectorsPerGrain > 2048)
-    {
-        rc = VERR_VD_VMDK_INVALID_HEADER;
-        goto out;
-    }
-    pExtent->uDescriptorSector = 0;
-    pExtent->cDescriptorSectors = 0;
-    pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);
-    pExtent->uSectorRGD = 0;
-    pExtent->cOverheadSectors = 0;
-    pExtent->cGTEntries = 4096;
-    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
-    if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
-    {
-        rc = VERR_VD_VMDK_INVALID_HEADER;
-        goto out;
-    }
-    pExtent->cSectorsPerGDE = cSectorsPerGDE;
-    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
-    if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))
-    {
-        /* Inconsistency detected. Computed number of GD entries doesn't match
-         * stored value. Better be safe than sorry. */
-        rc = VERR_VD_VMDK_INVALID_HEADER;
-        goto out;
-    }
-    pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);
-    pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
-
-    rc = vmdkReadGrainDirectory(pImage, pExtent);
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeExtentData(pImage, pExtent, false);
-
-    return rc;
-}
-#endif /* VBOX_WITH_VMDK_ESX */
-
-/**
- * Internal: free the buffers used for streamOptimized images.
- */
-static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent)
-{
-    if (pExtent->pvCompGrain)
-    {
-        RTMemFree(pExtent->pvCompGrain);
-        pExtent->pvCompGrain = NULL;
-    }
-    if (pExtent->pvGrain)
-    {
-        RTMemFree(pExtent->pvGrain);
-        pExtent->pvGrain = NULL;
-    }
-}
-
-/**
- * Internal: free the memory used by the extent data structure, optionally
- * deleting the referenced files.
- */
-static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                               bool fDelete)
-{
-    vmdkFreeGrainDirectory(pExtent);
-    if (pExtent->pDescData)
-    {
-        RTMemFree(pExtent->pDescData);
-        pExtent->pDescData = NULL;
-    }
-    if (pExtent->pFile != NULL)
-    {
-        /* Do not delete raw extents, these have full and base names equal. */
-        vmdkFileClose(pImage, &pExtent->pFile,
-                         fDelete
-                      && pExtent->pszFullname
-                      && strcmp(pExtent->pszFullname, pExtent->pszBasename));
-    }
-    if (pExtent->pszBasename)
-    {
-        RTMemTmpFree((void *)pExtent->pszBasename);
-        pExtent->pszBasename = NULL;
-    }
-    if (pExtent->pszFullname)
-    {
-        RTStrFree((char *)(void *)pExtent->pszFullname);
-        pExtent->pszFullname = NULL;
-    }
-    vmdkFreeStreamBuffers(pExtent);
-}
-
-/**
- * Internal: allocate grain table cache if necessary for this image.
- */
-static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)
-{
-    PVMDKEXTENT pExtent;
-
-    /* Allocate grain table cache if any sparse extent is present. */
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (    pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
-#ifdef VBOX_WITH_VMDK_ESX
-            ||  pExtent->enmType == VMDKETYPE_ESX_SPARSE
-#endif /* VBOX_WITH_VMDK_ESX */
-           )
-        {
-            /* Allocate grain table cache. */
-            pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
-            if (!pImage->pGTCache)
-                return VERR_NO_MEMORY;
-            for (unsigned j = 0; j < VMDK_GT_CACHE_SIZE; j++)
-            {
-                PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[j];
-                pGCE->uExtent = UINT32_MAX;
-            }
-            pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
-            break;
-        }
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal: allocate the given number of extents.
- */
-static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
-    if (pExtents)
-    {
-        for (unsigned i = 0; i < cExtents; i++)
-        {
-            pExtents[i].pFile = NULL;
-            pExtents[i].pszBasename = NULL;
-            pExtents[i].pszFullname = NULL;
-            pExtents[i].pGD = NULL;
-            pExtents[i].pRGD = NULL;
-            pExtents[i].pDescData = NULL;
-            pExtents[i].uVersion = 1;
-            pExtents[i].uCompression = VMDK_COMPRESSION_NONE;
-            pExtents[i].uExtent = i;
-            pExtents[i].pImage = pImage;
-        }
-        pImage->pExtents = pExtents;
-        pImage->cExtents = cExtents;
-    }
-    else
-        rc = VERR_NO_MEMORY;
-
-    return rc;
-}
-
-/**
- * Internal: Open an image, constructing all necessary data structures.
- */
-static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
-{
-    int rc;
-    uint32_t u32Magic;
-    PVMDKFILE pFile;
-    PVMDKEXTENT pExtent;
-
-    pImage->uOpenFlags = uOpenFlags;
-
-    /* Try to get error interface. */
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    /*
-     * Open the image.
-     * We don't have to check for asynchronous access because
-     * we only support raw access and the opened file is a description
-     * file were no data is stored.
-     */
-
-    rc = vmdkFileOpen(pImage, &pFile, pImage->pszFilename,
-                      VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
-                      false /* fAsyncIO */);
-    if (RT_FAILURE(rc))
-    {
-        /* Do NOT signal an appropriate error here, as the VD layer has the
-         * choice of retrying the open if it failed. */
-        goto out;
-    }
-    pImage->pFile = pFile;
-
-    /* Read magic (if present). */
-    rc = vmdkFileReadSync(pImage, pFile, 0, &u32Magic, sizeof(u32Magic), NULL);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    /* Handle the file according to its magic number. */
-    if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
-    {
-        /* It's a hosted single-extent image. */
-        rc = vmdkCreateExtents(pImage, 1);
-        if (RT_FAILURE(rc))
-            goto out;
-        /* The opened file is passed to the extent. No separate descriptor
-         * file, so no need to keep anything open for the image. */
-        pExtent = &pImage->pExtents[0];
-        pExtent->pFile = pFile;
-        pImage->pFile = NULL;
-        pExtent->pszFullname = RTPathAbsDup(pImage->pszFilename);
-        if (!pExtent->pszFullname)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-        rc = vmdkReadBinaryMetaExtent(pImage, pExtent, true /* fMagicAlreadyRead */);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        /* As we're dealing with a monolithic image here, there must
-         * be a descriptor embedded in the image file. */
-        if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)
-        {
-            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-        /* HACK: extend the descriptor if it is unusually small and it fits in
-         * the unused space after the image header. Allows opening VMDK files
-         * with extremely small descriptor in read/write mode. */
-        if (    !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            &&  pExtent->cDescriptorSectors < 3
-            &&  (int64_t)pExtent->uSectorGD - pExtent->uDescriptorSector >= 4
-            &&  (!pExtent->uSectorRGD || (int64_t)pExtent->uSectorRGD - pExtent->uDescriptorSector >= 4))
-        {
-            pExtent->cDescriptorSectors = 4;
-            pExtent->fMetaDirty = true;
-        }
-        /* Read the descriptor from the extent. */
-        pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
-        if (!pExtent->pDescData)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
-                              pExtent->pDescData,
-                              VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
-        AssertRC(rc);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname);
-            goto out;
-        }
-
-        rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
-                                 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
-        if (RT_FAILURE(rc))
-            goto out;
-
-        rc = vmdkReadMetaExtent(pImage, pExtent);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        /* Mark the extent as unclean if opened in read-write mode. */
-        if (   !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-        {
-            pExtent->fUncleanShutdown = true;
-            pExtent->fMetaDirty = true;
-        }
-    }
-    else
-    {
-        /* Allocate at least 10K, and make sure that there is 5K free space
-         * in case new entries need to be added to the descriptor. Never
-         * allocate more than 128K, because that's no valid descriptor file
-         * and will result in the correct "truncated read" error handling. */
-        uint64_t cbFileSize;
-        rc = vmdkFileGetSize(pImage, pFile, &cbFileSize);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        uint64_t cbSize = cbFileSize;
-        if (cbSize % VMDK_SECTOR2BYTE(10))
-            cbSize += VMDK_SECTOR2BYTE(20) - cbSize % VMDK_SECTOR2BYTE(10);
-        else
-            cbSize += VMDK_SECTOR2BYTE(10);
-        cbSize = RT_MIN(cbSize, _128K);
-        pImage->cbDescAlloc = RT_MAX(VMDK_SECTOR2BYTE(20), cbSize);
-        pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
-        if (!pImage->pDescData)
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-
-        size_t cbRead;
-        rc = vmdkFileReadSync(pImage, pImage->pFile, 0, pImage->pDescData,
-                              RT_MIN(pImage->cbDescAlloc, cbFileSize),
-                              &cbRead);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-        if (cbRead == pImage->cbDescAlloc)
-        {
-            /* Likely the read is truncated. Better fail a bit too early
-             * (normally the descriptor is much smaller than our buffer). */
-            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-
-        rc = vmdkParseDescriptor(pImage, pImage->pDescData,
-                                 pImage->cbDescAlloc);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        /*
-         * We have to check for the asynchronous open flag. The
-         * extents are parsed and the type of all are known now.
-         * Check if every extent is either FLAT or ZERO.
-         */
-        if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
-        {
-            unsigned cFlatExtents = 0;
-
-            for (unsigned i = 0; i < pImage->cExtents; i++)
-            {
-                pExtent = &pImage->pExtents[i];
-
-                if ((    pExtent->enmType != VMDKETYPE_FLAT
-                     &&  pExtent->enmType != VMDKETYPE_ZERO
-                     &&  pExtent->enmType != VMDKETYPE_VMFS)
-                    || ((pImage->pExtents[i].enmType == VMDKETYPE_FLAT) && (cFlatExtents > 0)))
-                {
-                    /*
-                     * Opened image contains at least one none flat or zero extent.
-                     * Return error but don't set error message as the caller
-                     * has the chance to open in non async I/O mode.
-                     */
-                    rc = VERR_NOT_SUPPORTED;
-                    goto out;
-                }
-                if (pExtent->enmType == VMDKETYPE_FLAT)
-                    cFlatExtents++;
-            }
-        }
-
-        for (unsigned i = 0; i < pImage->cExtents; i++)
-        {
-            pExtent = &pImage->pExtents[i];
-
-            if (pExtent->pszBasename)
-            {
-                /* Hack to figure out whether the specified name in the
-                 * extent descriptor is absolute. Doesn't always work, but
-                 * should be good enough for now. */
-                char *pszFullname;
-                /** @todo implement proper path absolute check. */
-                if (pExtent->pszBasename[0] == RTPATH_SLASH)
-                {
-                    pszFullname = RTStrDup(pExtent->pszBasename);
-                    if (!pszFullname)
-                    {
-                        rc = VERR_NO_MEMORY;
-                        goto out;
-                    }
-                }
-                else
-                {
-                    char *pszDirname = RTStrDup(pImage->pszFilename);
-                    if (!pszDirname)
-                    {
-                        rc = VERR_NO_MEMORY;
-                        goto out;
-                    }
-                    RTPathStripFilename(pszDirname);
-                    pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
-                    RTStrFree(pszDirname);
-                    if (!pszFullname)
-                    {
-                        rc = VERR_NO_STR_MEMORY;
-                        goto out;
-                    }
-                }
-                pExtent->pszFullname = pszFullname;
-            }
-            else
-                pExtent->pszFullname = NULL;
-
-            switch (pExtent->enmType)
-            {
-                case VMDKETYPE_HOSTED_SPARSE:
-                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                                      VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                                 false /* fCreate */),
-                                      false /* fAsyncIO */);
-                    if (RT_FAILURE(rc))
-                    {
-                        /* Do NOT signal an appropriate error here, as the VD
-                         * layer has the choice of retrying the open if it
-                         * failed. */
-                        goto out;
-                    }
-                    rc = vmdkReadBinaryMetaExtent(pImage, pExtent,
-                                                  false /* fMagicAlreadyRead */);
-                    if (RT_FAILURE(rc))
-                        goto out;
-                    rc = vmdkReadMetaExtent(pImage, pExtent);
-                    if (RT_FAILURE(rc))
-                        goto out;
-
-                    /* Mark extent as unclean if opened in read-write mode. */
-                    if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
-                    {
-                        pExtent->fUncleanShutdown = true;
-                        pExtent->fMetaDirty = true;
-                    }
-                    break;
-                case VMDKETYPE_VMFS:
-                case VMDKETYPE_FLAT:
-                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                                      VDOpenFlagsToFileOpenFlags(uOpenFlags,
-                                                                 false /* fCreate */),
-                                      true /* fAsyncIO */);
-                    if (RT_FAILURE(rc))
-                    {
-                        /* Do NOT signal an appropriate error here, as the VD
-                         * layer has the choice of retrying the open if it
-                         * failed. */
-                        goto out;
-                    }
-                    break;
-                case VMDKETYPE_ZERO:
-                    /* Nothing to do. */
-                    break;
-                default:
-                    AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
-            }
-        }
-    }
-
-    /* Make sure this is not reached accidentally with an error status. */
-    AssertRC(rc);
-
-    /* Determine PCHS geometry if not set. */
-    if (pImage->PCHSGeometry.cCylinders == 0)
-    {
-        uint64_t cCylinders =   VMDK_BYTE2SECTOR(pImage->cbSize)
-                              / pImage->PCHSGeometry.cHeads
-                              / pImage->PCHSGeometry.cSectors;
-        pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383);
-        if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-            && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-        {
-            rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry);
-            AssertRC(rc);
-        }
-    }
-
-    /* Update the image metadata now in case has changed. */
-    rc = vmdkFlushImage(pImage);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Figure out a few per-image constants from the extents. */
-    pImage->cbSize = 0;
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (    pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
-#ifdef VBOX_WITH_VMDK_ESX
-            ||  pExtent->enmType == VMDKETYPE_ESX_SPARSE
-#endif /* VBOX_WITH_VMDK_ESX */
-           )
-        {
-            /* Here used to be a check whether the nominal size of an extent
-             * is a multiple of the grain size. The spec says that this is
-             * always the case, but unfortunately some files out there in the
-             * wild violate the spec (e.g. ReactOS 0.3.1). */
-        }
-        pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
-    }
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (    pImage->pExtents[i].enmType == VMDKETYPE_FLAT
-            ||  pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
-        {
-            pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
-            break;
-        }
-    }
-
-    if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
-        rc = vmdkAllocateGrainTableCache(pImage);
-
-out:
-    if (RT_FAILURE(rc))
-        vmdkFreeImage(pImage, false);
-    return rc;
-}
-
-/**
- * Internal: create VMDK images for raw disk/partition access.
- */
-static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
-                              uint64_t cbSize)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKEXTENT pExtent;
-
-    if (pRaw->fRawDisk)
-    {
-        /* Full raw disk access. This requires setting up a descriptor
-         * file and open the (flat) raw disk. */
-        rc = vmdkCreateExtents(pImage, 1);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
-        pExtent = &pImage->pExtents[0];
-        /* Create raw disk descriptor file. */
-        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
-                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                     true /* fCreate */),
-                          false /* fAsyncIO */);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
-
-        /* Set up basename for extent description. Cannot use StrDup. */
-        size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
-        char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
-        if (!pszBasename)
-            return VERR_NO_MEMORY;
-        memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
-        pExtent->pszBasename = pszBasename;
-        /* For raw disks the full name is identical to the base name. */
-        pExtent->pszFullname = RTStrDup(pszBasename);
-        if (!pExtent->pszFullname)
-            return VERR_NO_MEMORY;
-        pExtent->enmType = VMDKETYPE_FLAT;
-        pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
-        pExtent->uSectorOffset = 0;
-        pExtent->enmAccess = VMDKACCESS_READWRITE;
-        pExtent->fMetaDirty = false;
-
-        /* Open flat image, the raw disk. */
-        rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                                     false /* fCreate */),
-                          false /* fAsyncIO */);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
-    }
-    else
-    {
-        /* Raw partition access. This requires setting up a descriptor
-         * file, write the partition information to a flat extent and
-         * open all the (flat) raw disk partitions. */
-
-        /* First pass over the partition data areas to determine how many
-         * extents we need. One data area can require up to 2 extents, as
-         * it might be necessary to skip over unpartitioned space. */
-        unsigned cExtents = 0;
-        uint64_t uStart = 0;
-        for (unsigned i = 0; i < pRaw->cPartDescs; i++)
-        {
-            PVBOXHDDRAWPARTDESC pPart = &pRaw->pPartDescs[i];
-            if (uStart > pPart->uStart)
-                return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: incorrect partition data area ordering set up by the caller in '%s'"), pImage->pszFilename);
-
-            if (uStart < pPart->uStart)
-                cExtents++;
-            uStart = pPart->uStart + pPart->cbData;
-            cExtents++;
-        }
-        /* Another extent for filling up the rest of the image. */
-        if (uStart != cbSize)
-            cExtents++;
-
-        rc = vmdkCreateExtents(pImage, cExtents);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
-
-        /* Create raw partition descriptor file. */
-        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
-                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                     true /* fCreate */),
-                          false /* fAsyncIO */);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
-
-        /* Create base filename for the partition table extent. */
-        /** @todo remove fixed buffer without creating memory leaks. */
-        char pszPartition[1024];
-        const char *pszBase = RTPathFilename(pImage->pszFilename);
-        const char *pszExt = RTPathExt(pszBase);
-        if (pszExt == NULL)
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename);
-        char *pszBaseBase = RTStrDup(pszBase);
-        if (!pszBaseBase)
-            return VERR_NO_MEMORY;
-        RTPathStripExt(pszBaseBase);
-        RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s",
-                    pszBaseBase, pszExt);
-        RTStrFree(pszBaseBase);
-
-        /* Second pass over the partitions, now define all extents. */
-        uint64_t uPartOffset = 0;
-        cExtents = 0;
-        uStart = 0;
-        for (unsigned i = 0; i < pRaw->cPartDescs; i++)
-        {
-            PVBOXHDDRAWPARTDESC pPart = &pRaw->pPartDescs[i];
-            pExtent = &pImage->pExtents[cExtents++];
-
-            if (uStart < pPart->uStart)
-            {
-                pExtent->pszBasename = NULL;
-                pExtent->pszFullname = NULL;
-                pExtent->enmType = VMDKETYPE_ZERO;
-                pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uStart - uStart);
-                pExtent->uSectorOffset = 0;
-                pExtent->enmAccess = VMDKACCESS_READWRITE;
-                pExtent->fMetaDirty = false;
-                /* go to next extent */
-                pExtent = &pImage->pExtents[cExtents++];
-            }
-            uStart = pPart->uStart + pPart->cbData;
-
-            if (pPart->pvPartitionData)
-            {
-                /* Set up basename for extent description. Can't use StrDup. */
-                size_t cbBasename = strlen(pszPartition) + 1;
-                char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
-                if (!pszBasename)
-                    return VERR_NO_MEMORY;
-                memcpy(pszBasename, pszPartition, cbBasename);
-                pExtent->pszBasename = pszBasename;
-
-                /* Set up full name for partition extent. */
-                char *pszDirname = RTStrDup(pImage->pszFilename);
-                if (!pszDirname)
-                    return VERR_NO_STR_MEMORY;
-                RTPathStripFilename(pszDirname);
-                char *pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
-                RTStrFree(pszDirname);
-                if (!pszDirname)
-                    return VERR_NO_STR_MEMORY;
-                pExtent->pszFullname = pszFullname;
-                pExtent->enmType = VMDKETYPE_FLAT;
-                pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
-                pExtent->uSectorOffset = uPartOffset;
-                pExtent->enmAccess = VMDKACCESS_READWRITE;
-                pExtent->fMetaDirty = false;
-
-                /* Create partition table flat image. */
-                rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                                  VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                             true /* fCreate */),
-                                  false /* fAsyncIO */);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                       VMDK_SECTOR2BYTE(uPartOffset),
-                                       pPart->pvPartitionData,
-                                       pPart->cbData, NULL);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
-                uPartOffset += VMDK_BYTE2SECTOR(pPart->cbData);
-            }
-            else
-            {
-                if (pPart->pszRawDevice)
-                {
-                    /* Set up basename for extent descr. Can't use StrDup. */
-                    size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
-                    char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
-                    if (!pszBasename)
-                        return VERR_NO_MEMORY;
-                    memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
-                    pExtent->pszBasename = pszBasename;
-                    /* For raw disks full name is identical to base name. */
-                    pExtent->pszFullname = RTStrDup(pszBasename);
-                    if (!pExtent->pszFullname)
-                        return VERR_NO_MEMORY;
-                    pExtent->enmType = VMDKETYPE_FLAT;
-                    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
-                    pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uStartOffset);
-                    pExtent->enmAccess = VMDKACCESS_READWRITE;
-                    pExtent->fMetaDirty = false;
-
-                    /* Open flat image, the raw partition. */
-                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                                      VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
-                                                                 false /* fCreate */),
-                                      false /* fAsyncIO */);
-                    if (RT_FAILURE(rc))
-                        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
-                }
-                else
-                {
-                    pExtent->pszBasename = NULL;
-                    pExtent->pszFullname = NULL;
-                    pExtent->enmType = VMDKETYPE_ZERO;
-                    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
-                    pExtent->uSectorOffset = 0;
-                    pExtent->enmAccess = VMDKACCESS_READWRITE;
-                    pExtent->fMetaDirty = false;
-                }
-            }
-        }
-        /* Another extent for filling up the rest of the image. */
-        if (uStart != cbSize)
-        {
-            pExtent = &pImage->pExtents[cExtents++];
-            pExtent->pszBasename = NULL;
-            pExtent->pszFullname = NULL;
-            pExtent->enmType = VMDKETYPE_ZERO;
-            pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
-            pExtent->uSectorOffset = 0;
-            pExtent->enmAccess = VMDKACCESS_READWRITE;
-            pExtent->fMetaDirty = false;
-        }
-    }
-
-    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
-                            pRaw->fRawDisk ?
-                            "fullDevice" : "partitionedDevice");
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
-    return rc;
-}
-
-/**
- * Internal: create a regular (i.e. file-backed) VMDK image.
- */
-static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
-                                  unsigned uImageFlags,
-                                  PFNVDPROGRESS pfnProgress, void *pvUser,
-                                  unsigned uPercentStart, unsigned uPercentSpan)
-{
-    int rc = VINF_SUCCESS;
-    unsigned cExtents = 1;
-    uint64_t cbOffset = 0;
-    uint64_t cbRemaining = cbSize;
-
-    if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
-    {
-        cExtents = cbSize / VMDK_2G_SPLIT_SIZE;
-        /* Do proper extent computation: need one smaller extent if the total
-         * size isn't evenly divisible by the split size. */
-        if (cbSize % VMDK_2G_SPLIT_SIZE)
-            cExtents++;
-    }
-    rc = vmdkCreateExtents(pImage, cExtents);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
-
-    /* Basename strings needed for constructing the extent names. */
-    char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
-    AssertPtr(pszBasenameSubstr);
-    size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
-
-    /* Create separate descriptor file if necessary. */
-    if (cExtents != 1 || (uImageFlags & VD_IMAGE_FLAGS_FIXED))
-    {
-        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
-                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                     true /* fCreate */),
-                          false /* fAsyncIO */);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
-    }
-    else
-        pImage->pFile = NULL;
-
-    /* Set up all extents. */
-    for (unsigned i = 0; i < cExtents; i++)
-    {
-        PVMDKEXTENT pExtent = &pImage->pExtents[i];
-        uint64_t cbExtent = cbRemaining;
-
-        /* Set up fullname/basename for extent description. Cannot use StrDup
-         * for basename, as it is not guaranteed that the memory can be freed
-         * with RTMemTmpFree, which must be used as in other code paths
-         * StrDup is not usable. */
-        if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        {
-            char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
-            if (!pszBasename)
-                return VERR_NO_MEMORY;
-            memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
-            pExtent->pszBasename = pszBasename;
-        }
-        else
-        {
-            char *pszBasenameExt = RTPathExt(pszBasenameSubstr);
-            char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
-            RTPathStripExt(pszBasenameBase);
-            char *pszTmp;
-            size_t cbTmp;
-            if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-            {
-                if (cExtents == 1)
-                    RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase,
-                                 pszBasenameExt);
-                else
-                    RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
-                                 i+1, pszBasenameExt);
-            }
-            else
-                RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1,
-                             pszBasenameExt);
-            RTStrFree(pszBasenameBase);
-            if (!pszTmp)
-                return VERR_NO_STR_MEMORY;
-            cbTmp = strlen(pszTmp) + 1;
-            char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
-            if (!pszBasename)
-                return VERR_NO_MEMORY;
-            memcpy(pszBasename, pszTmp, cbTmp);
-            RTStrFree(pszTmp);
-            pExtent->pszBasename = pszBasename;
-            if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
-                cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE);
-        }
-        char *pszBasedirectory = RTStrDup(pImage->pszFilename);
-        if (!pszBasedirectory)
-            return VERR_NO_STR_MEMORY;
-        RTPathStripFilename(pszBasedirectory);
-        char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
-        RTStrFree(pszBasedirectory);
-        if (!pszFullname)
-            return VERR_NO_STR_MEMORY;
-        pExtent->pszFullname = pszFullname;
-
-        /* Create file for extent. */
-        rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                     true /* fCreate */),
-                          false /* fAsyncIO */);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
-        if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        {
-            rc = vmdkFileSetSize(pImage, pExtent->pFile, cbExtent);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
-
-            /* Fill image with zeroes. We do this for every fixed-size image since on some systems
-             * (for example Windows Vista), it takes ages to write a block near the end of a sparse
-             * file and the guest could complain about an ATA timeout. */
-
-            /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
-             *        Currently supported file systems are ext4 and ocfs2. */
-
-            /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
-            const size_t cbBuf = 128 * _1K;
-            void *pvBuf = RTMemTmpAllocZ(cbBuf);
-            if (!pvBuf)
-                return VERR_NO_MEMORY;
-
-            uint64_t uOff = 0;
-            /* Write data to all image blocks. */
-            while (uOff < cbExtent)
-            {
-                unsigned cbChunk = (unsigned)RT_MIN(cbExtent, cbBuf);
-
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOff, pvBuf, cbChunk, NULL);
-                if (RT_FAILURE(rc))
-                {
-                    RTMemFree(pvBuf);
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: writing block failed for '%s'"), pImage->pszFilename);
-                }
-
-                uOff += cbChunk;
-
-                if (pfnProgress)
-                {
-                    rc = pfnProgress(pvUser,
-                                     uPercentStart + uOff * uPercentSpan / cbExtent);
-                    if (RT_FAILURE(rc))
-                    {
-                        RTMemFree(pvBuf);
-                        return rc;
-                    }
-                }
-            }
-            RTMemTmpFree(pvBuf);
-        }
-
-        /* Place descriptor file information (where integrated). */
-        if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        {
-            pExtent->uDescriptorSector = 1;
-            pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
-            /* The descriptor is part of the (only) extent. */
-            pExtent->pDescData = pImage->pDescData;
-            pImage->pDescData = NULL;
-        }
-
-        if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        {
-            uint64_t cSectorsPerGDE, cSectorsPerGD;
-            pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
-            pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, _64K));
-            pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
-            pExtent->cGTEntries = 512;
-            cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
-            pExtent->cSectorsPerGDE = cSectorsPerGDE;
-            pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
-            cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
-            if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-            {
-                /* The spec says version is 1 for all VMDKs, but the vast
-                 * majority of streamOptimized VMDKs actually contain
-                 * version 3 - so go with the majority. Both are accepted. */
-                pExtent->uVersion = 3;
-                pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
-            }
-        }
-        else
-        {
-            if (uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
-                pExtent->enmType = VMDKETYPE_VMFS;
-            else
-                pExtent->enmType = VMDKETYPE_FLAT;
-        }
-
-        pExtent->enmAccess = VMDKACCESS_READWRITE;
-        pExtent->fUncleanShutdown = true;
-        pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent);
-        pExtent->uSectorOffset = 0;
-        pExtent->fMetaDirty = true;
-
-        if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-        {
-            /* fPreAlloc should never be false because VMware can't use such images. */
-            rc = vmdkCreateGrainDirectory(pImage, pExtent,
-                                          RT_MAX(  pExtent->uDescriptorSector
-                                                 + pExtent->cDescriptorSectors,
-                                                 1),
-                                          true /* fPreAlloc */);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
-        }
-
-        if (RT_SUCCESS(rc) && pfnProgress)
-            pfnProgress(pvUser, uPercentStart + i * uPercentSpan / cExtents);
-
-        cbRemaining -= cbExtent;
-        cbOffset += cbExtent;
-    }
-
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
-    {
-        /* VirtualBox doesn't care, but VMWare ESX freaks out if the wrong
-         * controller type is set in an image. */
-        rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, "ddb.adapterType", "lsilogic");
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set controller type to lsilogic in '%s'"), pImage->pszFilename);
-    }
-
-    const char *pszDescType = NULL;
-    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-    {
-        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
-            pszDescType = "vmfs";
-        else
-            pszDescType =   (cExtents == 1)
-                          ? "monolithicFlat" : "twoGbMaxExtentFlat";
-    }
-    else
-    {
-        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-            pszDescType = "streamOptimized";
-        else
-        {
-            pszDescType =   (cExtents == 1)
-                          ? "monolithicSparse" : "twoGbMaxExtentSparse";
-        }
-    }
-    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
-                            pszDescType);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
-    return rc;
-}
-
-/**
- * Internal: Create a real stream optimized VMDK using only linear writes.
- */
-static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize,
-                                 unsigned uImageFlags,
-                                 PFNVDPROGRESS pfnProgress, void *pvUser,
-                                 unsigned uPercentStart, unsigned uPercentSpan)
-{
-    int rc;
-
-    rc = vmdkCreateExtents(pImage, 1);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
-
-    /* Basename strings needed for constructing the extent names. */
-    const char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
-    AssertPtr(pszBasenameSubstr);
-    size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
-
-    /* No separate descriptor file. */
-    pImage->pFile = NULL;
-
-    /* Set up all extents. */
-    PVMDKEXTENT pExtent = &pImage->pExtents[0];
-
-    /* Set up fullname/basename for extent description. Cannot use StrDup
-     * for basename, as it is not guaranteed that the memory can be freed
-     * with RTMemTmpFree, which must be used as in other code paths
-     * StrDup is not usable. */
-    char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
-    if (!pszBasename)
-        return VERR_NO_MEMORY;
-    memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
-    pExtent->pszBasename = pszBasename;
-
-    char *pszBasedirectory = RTStrDup(pImage->pszFilename);
-    RTPathStripFilename(pszBasedirectory);
-    char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
-    RTStrFree(pszBasedirectory);
-    if (!pszFullname)
-        return VERR_NO_STR_MEMORY;
-    pExtent->pszFullname = pszFullname;
-
-    /* Create file for extent. Make it write only, no reading allowed. */
-    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
-                        VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
-                                                   true /* fCreate */)
-                      & ~RTFILE_O_READ,
-                      false /* fAsyncIO */);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
-
-    /* Place descriptor file information. */
-    pExtent->uDescriptorSector = 1;
-    pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
-    /* The descriptor is part of the (only) extent. */
-    pExtent->pDescData = pImage->pDescData;
-    pImage->pDescData = NULL;
-
-    uint64_t cSectorsPerGDE, cSectorsPerGD;
-    pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
-    pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));
-    pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
-    pExtent->cGTEntries = 512;
-    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
-    pExtent->cSectorsPerGDE = cSectorsPerGDE;
-    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
-    cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
-
-    /* The spec says version is 1 for all VMDKs, but the vast
-     * majority of streamOptimized VMDKs actually contain
-     * version 3 - so go with the majority. Both are accepted. */
-    pExtent->uVersion = 3;
-    pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
-    pExtent->fFooter = true;
-
-    pExtent->enmAccess = VMDKACCESS_READONLY;
-    pExtent->fUncleanShutdown = false;
-    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
-    pExtent->uSectorOffset = 0;
-    pExtent->fMetaDirty = true;
-
-    /* Create grain directory, without preallocating it straight away. It will
-     * be constructed on the fly when writing out the data and written when
-     * closing the image. The end effect is that the full grain directory is
-     * allocated, which is a requirement of the VMDK specs. */
-    rc = vmdkCreateGrainDirectory(pImage, pExtent, VMDK_GD_AT_END,
-                                  false /* fPreAlloc */);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
-
-    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
-                            "streamOptimized");
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
-
-    return rc;
-}
-
-/**
- * Internal: The actual code for creating any VMDK variant currently in
- * existence on hosted environments.
- */
-static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
-                           unsigned uImageFlags, const char *pszComment,
-                           PCVDGEOMETRY pPCHSGeometry,
-                           PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
-                           PFNVDPROGRESS pfnProgress, void *pvUser,
-                           unsigned uPercentStart, unsigned uPercentSpan)
-{
-    int rc;
-
-    pImage->uImageFlags = uImageFlags;
-
-    /* Try to get error interface. */
-    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
-    if (pImage->pInterfaceError)
-        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
-
-    /* Get I/O interface. */
-    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
-    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
-    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
-    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
-
-    rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,
-                              &pImage->Descriptor);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    if (    (uImageFlags & VD_IMAGE_FLAGS_FIXED)
-        &&  (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
-    {
-        /* Raw disk image (includes raw partition). */
-        const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment;
-        /* As the comment is misused, zap it so that no garbage comment
-         * is set below. */
-        pszComment = NULL;
-        rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
-    }
-    else
-    {
-        if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        {
-            /* Stream optimized sparse image (monolithic). */
-            rc = vmdkCreateStreamImage(pImage, cbSize, uImageFlags,
-                                       pfnProgress, pvUser, uPercentStart,
-                                       uPercentSpan * 95 / 100);
-        }
-        else
-        {
-            /* Regular fixed or sparse image (monolithic or split). */
-            rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags,
-                                        pfnProgress, pvUser, uPercentStart,
-                                        uPercentSpan * 95 / 100);
-        }
-    }
-
-    if (RT_FAILURE(rc))
-        goto out;
-
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
-
-    pImage->cbSize = cbSize;
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        PVMDKEXTENT pExtent = &pImage->pExtents[i];
-
-        rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
-                               pExtent->cNominalSectors, pExtent->enmType,
-                               pExtent->pszBasename, pExtent->uSectorOffset);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-    }
-    vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
-
-    if (    pPCHSGeometry->cCylinders != 0
-        &&  pPCHSGeometry->cHeads != 0
-        &&  pPCHSGeometry->cSectors != 0)
-    {
-        rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
-        if (RT_FAILURE(rc))
-            goto out;
-    }
-    if (    pLCHSGeometry->cCylinders != 0
-        &&  pLCHSGeometry->cHeads != 0
-        &&  pLCHSGeometry->cSectors != 0)
-    {
-        rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
-        if (RT_FAILURE(rc))
-            goto out;
-    }
-
-    pImage->LCHSGeometry = *pLCHSGeometry;
-    pImage->PCHSGeometry = *pPCHSGeometry;
-
-    pImage->ImageUuid = *pUuid;
-    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    RTUuidClear(&pImage->ParentUuid);
-    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    RTUuidClear(&pImage->ModificationUuid);
-    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_MODIFICATION_UUID,
-                            &pImage->ModificationUuid);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-    RTUuidClear(&pImage->ParentModificationUuid);
-    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                            VMDK_DDB_PARENT_MODIFICATION_UUID,
-                            &pImage->ParentModificationUuid);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    rc = vmdkAllocateGrainTableCache(pImage);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    rc = vmdkSetImageComment(pImage, pszComment);
-    if (RT_FAILURE(rc))
-    {
-        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);
-        goto out;
-    }
-
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan * 99 / 100);
-
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        /* streamOptimized is a bit special, we cannot trigger the flush
-         * until all data has been written. So we write the necessary
-         * information explicitly. */
-        pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(  pImage->Descriptor.aLines[pImage->Descriptor.cLines]
-                                                                              - pImage->Descriptor.aLines[0], 512));
-        rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-
-        rc = vmdkWriteDescriptor(pImage);
-        if (RT_FAILURE(rc))
-        {
-            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);
-            goto out;
-        }
-    }
-    else
-        rc = vmdkFlushImage(pImage);
-
-out:
-    if (RT_SUCCESS(rc) && pfnProgress)
-        pfnProgress(pvUser, uPercentStart + uPercentSpan);
-
-    if (RT_FAILURE(rc))
-        vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
-    return rc;
-}
-
-/**
- * Internal: Update image comment.
- */
-static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
-{
-    char *pszCommentEncoded;
-    if (pszComment)
-    {
-        pszCommentEncoded = vmdkEncodeString(pszComment);
-        if (!pszCommentEncoded)
-            return VERR_NO_MEMORY;
-    }
-    else
-        pszCommentEncoded = NULL;
-    int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
-                               "ddb.comment", pszCommentEncoded);
-    if (pszComment)
-        RTStrFree(pszCommentEncoded);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal. Clear the grain table buffer for real stream optimized writing.
- */
-static void vmdkStreamClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
-{
-    uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
-    for (uint32_t i = 0; i < cCacheLines; i++)
-        memset(&pImage->pGTCache->aGTCache[i].aGTData[0], '\0',
-               VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
-}
-
-/**
- * Internal. Flush the grain table buffer for real stream optimized writing.
- */
-static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                             uint32_t uGDEntry)
-{
-    int rc = VINF_SUCCESS;
-    uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
-
-    /* VMware does not write out completely empty grain tables in the case
-     * of streamOptimized images, which according to my interpretation of
-     * the VMDK 1.1 spec is bending the rules. Since they do it and we can
-     * handle it without problems do it the same way and save some bytes. */
-    bool fAllZero = true;
-    for (uint32_t i = 0; i < cCacheLines; i++)
-    {
-        /* Convert the grain table to little endian in place, as it will not
-         * be used at all after this function has been called. */
-        uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
-        for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
-            if (*pGTTmp)
-            {
-                fAllZero = false;
-                break;
-            }
-        if (!fAllZero)
-            break;
-    }
-    if (fAllZero)
-        return VINF_SUCCESS;
-
-    uint64_t uFileOffset = pExtent->uAppendPosition;
-    if (!uFileOffset)
-        return VERR_INTERNAL_ERROR;
-    /* Align to sector, as the previous write could have been any size. */
-    uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-
-    /* Grain table marker. */
-    uint8_t aMarker[512];
-    PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
-    memset(pMarker, '\0', sizeof(aMarker));
-    pMarker->uSector = RT_H2LE_U64(VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t)));
-    pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT);
-    rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                           aMarker, sizeof(aMarker), NULL);
-    AssertRC(rc);
-    uFileOffset += 512;
-
-    if (!pExtent->pGD || pExtent->pGD[uGDEntry])
-        return VERR_INTERNAL_ERROR;
-
-    pExtent->pGD[uGDEntry] = VMDK_BYTE2SECTOR(uFileOffset);
-
-    for (uint32_t i = 0; i < cCacheLines; i++)
-    {
-        /* Convert the grain table to little endian in place, as it will not
-         * be used at all after this function has been called. */
-        uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
-        for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
-            *pGTTmp = RT_H2LE_U32(*pGTTmp);
-
-        rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                               &pImage->pGTCache->aGTCache[i].aGTData[0],
-                               VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t),
-                               NULL);
-        uFileOffset += VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t);
-        if (RT_FAILURE(rc))
-            break;
-    }
-    Assert(!(uFileOffset % 512));
-    pExtent->uAppendPosition = RT_ALIGN_64(uFileOffset, 512);
-    return rc;
-}
-
-/**
- * Internal. Free all allocated space for representing an image, and optionally
- * delete the image from disk.
- */
-static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
-{
-    int rc = VINF_SUCCESS;
-
-    /* Freeing a never allocated image (e.g. because the open failed) is
-     * not signalled as an error. After all nothing bad happens. */
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-            {
-                /* Check if all extents are clean. */
-                for (unsigned i = 0; i < pImage->cExtents; i++)
-                {
-                    Assert(!pImage->pExtents[i].fUncleanShutdown);
-                }
-            }
-            else
-            {
-                /* Mark all extents as clean. */
-                for (unsigned i = 0; i < pImage->cExtents; i++)
-                {
-                    if (   (   pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
-#ifdef VBOX_WITH_VMDK_ESX
-                            || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
-#endif /* VBOX_WITH_VMDK_ESX */
-                           )
-                        && pImage->pExtents[i].fUncleanShutdown)
-                    {
-                        pImage->pExtents[i].fUncleanShutdown = false;
-                        pImage->pExtents[i].fMetaDirty = true;
-                    }
-
-                    /* From now on it's not safe to append any more data. */
-                    pImage->pExtents[i].uAppendPosition = 0;
-                }
-            }
-        }
-
-        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        {
-            /* No need to write any pending data if the file will be deleted
-             * or if the new file wasn't successfully created. */
-            if (   !fDelete && pImage->pExtents
-                && pImage->pExtents[0].cGTEntries
-                && pImage->pExtents[0].uAppendPosition)
-            {
-                PVMDKEXTENT pExtent = &pImage->pExtents[0];
-                uint32_t uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
-                if (uLastGDEntry != pExtent->cGDEntries - 1)
-                {
-                    rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
-                    AssertRC(rc);
-                    vmdkStreamClearGT(pImage, pExtent);
-                    for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++)
-                    {
-                        rc = vmdkStreamFlushGT(pImage, pExtent, i);
-                        AssertRC(rc);
-                    }
-                }
-
-                uint64_t uFileOffset = pExtent->uAppendPosition;
-                if (!uFileOffset)
-                    return VERR_INTERNAL_ERROR;
-                uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-
-                /* From now on it's not safe to append any more data. */
-                pExtent->uAppendPosition = 0;
-
-                /* Grain directory marker. */
-                uint8_t aMarker[512];
-                PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
-                memset(pMarker, '\0', sizeof(aMarker));
-                pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64(pExtent->cGDEntries * sizeof(uint32_t)), 512));
-                pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                                       aMarker, sizeof(aMarker), NULL);
-                AssertRC(rc);
-                uFileOffset += 512;
-
-                /* Write grain directory in little endian style. The array will
-                 * not be used after this, so convert in place. */
-                uint32_t *pGDTmp = pExtent->pGD;
-                for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++)
-                    *pGDTmp = RT_H2LE_U32(*pGDTmp);
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                                       pExtent->pGD,
-                                       pExtent->cGDEntries * sizeof(uint32_t),
-                                       NULL);
-                AssertRC(rc);
-
-                pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);
-                pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset);
-                uFileOffset = RT_ALIGN_64(  uFileOffset
-                                          + pExtent->cGDEntries * sizeof(uint32_t),
-                                          512);
-
-                /* Footer marker. */
-                memset(pMarker, '\0', sizeof(aMarker));
-                pMarker->uSector = VMDK_BYTE2SECTOR(512);
-                pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                                       aMarker, sizeof(aMarker), NULL);
-                AssertRC(rc);
-
-                uFileOffset += 512;
-                rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
-                AssertRC(rc);
-
-                uFileOffset += 512;
-                /* End-of-stream marker. */
-                memset(pMarker, '\0', sizeof(aMarker));
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                                       aMarker, sizeof(aMarker), NULL);
-                AssertRC(rc);
-            }
-        }
-        else
-            vmdkFlushImage(pImage);
-
-        if (pImage->pExtents != NULL)
-        {
-            for (unsigned i = 0 ; i < pImage->cExtents; i++)
-                vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
-            RTMemFree(pImage->pExtents);
-            pImage->pExtents = NULL;
-        }
-        pImage->cExtents = 0;
-        if (pImage->pFile != NULL)
-            vmdkFileClose(pImage, &pImage->pFile, fDelete);
-        vmdkFileCheckAllClose(pImage);
-
-        if (pImage->pGTCache)
-        {
-            RTMemFree(pImage->pGTCache);
-            pImage->pGTCache = NULL;
-        }
-        if (pImage->pDescData)
-        {
-            RTMemFree(pImage->pDescData);
-            pImage->pDescData = NULL;
-        }
-    }
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Internal. Flush image data (and metadata) to disk.
- */
-static int vmdkFlushImage(PVMDKIMAGE pImage)
-{
-    PVMDKEXTENT pExtent;
-    int rc = VINF_SUCCESS;
-
-    /* Update descriptor if changed. */
-    if (pImage->Descriptor.fDirty)
-    {
-        rc = vmdkWriteDescriptor(pImage);
-        if (RT_FAILURE(rc))
-            goto out;
-    }
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
-        {
-            switch (pExtent->enmType)
-            {
-                case VMDKETYPE_HOSTED_SPARSE:
-                    if (!pExtent->fFooter)
-                    {
-                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0);
-                        if (RT_FAILURE(rc))
-                            goto out;
-                    }
-                    else
-                    {
-                        uint64_t uFileOffset = pExtent->uAppendPosition;
-                        /* Simply skip writing anything if the streamOptimized
-                         * image hasn't been just created. */
-                        if (!uFileOffset)
-                            break;
-                        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent,
-                                                       uFileOffset);
-                        if (RT_FAILURE(rc))
-                            goto out;
-                    }
-                    break;
-#ifdef VBOX_WITH_VMDK_ESX
-                case VMDKETYPE_ESX_SPARSE:
-                    /** @todo update the header. */
-                    break;
-#endif /* VBOX_WITH_VMDK_ESX */
-                case VMDKETYPE_VMFS:
-                case VMDKETYPE_FLAT:
-                    /* Nothing to do. */
-                    break;
-                case VMDKETYPE_ZERO:
-                default:
-                    AssertMsgFailed(("extent with type %d marked as dirty\n",
-                                     pExtent->enmType));
-                    break;
-            }
-        }
-        switch (pExtent->enmType)
-        {
-            case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-            case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            case VMDKETYPE_VMFS:
-            case VMDKETYPE_FLAT:
-                /** @todo implement proper path absolute check. */
-                if (   pExtent->pFile != NULL
-                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                    && !(pExtent->pszBasename[0] == RTPATH_SLASH))
-                    rc = vmdkFileFlush(pImage, pExtent->pFile);
-                break;
-            case VMDKETYPE_ZERO:
-                /* No need to do anything for this extent. */
-                break;
-            default:
-                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
-                break;
-        }
-    }
-
-out:
-    return rc;
-}
-
-/**
- * Internal. Flush image data (and metadata) to disk - async version.
- */
-static int vmdkFlushImageAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
-{
-    PVMDKEXTENT pExtent;
-    int rc = VINF_SUCCESS;
-
-    /* Update descriptor if changed. */
-    if (pImage->Descriptor.fDirty)
-    {
-        rc = vmdkWriteDescriptorAsync(pImage, pIoCtx);
-        if (   RT_FAILURE(rc)
-            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
-            goto out;
-    }
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
-        {
-            switch (pExtent->enmType)
-            {
-                case VMDKETYPE_HOSTED_SPARSE:
-                    AssertMsgFailed(("Async I/O not supported for sparse images\n"));
-                    break;
-#ifdef VBOX_WITH_VMDK_ESX
-                case VMDKETYPE_ESX_SPARSE:
-                    /** @todo update the header. */
-                    break;
-#endif /* VBOX_WITH_VMDK_ESX */
-                case VMDKETYPE_VMFS:
-                case VMDKETYPE_FLAT:
-                    /* Nothing to do. */
-                    break;
-                case VMDKETYPE_ZERO:
-                default:
-                    AssertMsgFailed(("extent with type %d marked as dirty\n",
-                                     pExtent->enmType));
-                    break;
-            }
-        }
-        switch (pExtent->enmType)
-        {
-            case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-            case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            case VMDKETYPE_VMFS:
-            case VMDKETYPE_FLAT:
-                /** @todo implement proper path absolute check. */
-                if (   pExtent->pFile != NULL
-                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                    && !(pExtent->pszBasename[0] == RTPATH_SLASH))
-                    rc = vmdkFileFlushAsync(pImage, pExtent->pFile, pIoCtx);
-                break;
-            case VMDKETYPE_ZERO:
-                /* No need to do anything for this extent. */
-                break;
-            default:
-                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
-                break;
-        }
-    }
-
-out:
-    return rc;
-}
-
-/**
- * Internal. Find extent corresponding to the sector number in the disk.
- */
-static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector,
-                          PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
-{
-    PVMDKEXTENT pExtent = NULL;
-    int rc = VINF_SUCCESS;
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        if (offSector < pImage->pExtents[i].cNominalSectors)
-        {
-            pExtent = &pImage->pExtents[i];
-            *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
-            break;
-        }
-        offSector -= pImage->pExtents[i].cNominalSectors;
-    }
-
-    if (pExtent)
-        *ppExtent = pExtent;
-    else
-        rc = VERR_IO_SECTOR_NOT_FOUND;
-
-    return rc;
-}
-
-/**
- * Internal. Hash function for placing the grain table hash entries.
- */
-static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
-                                unsigned uExtent)
-{
-    /** @todo this hash function is quite simple, maybe use a better one which
-     * scrambles the bits better. */
-    return (uSector + uExtent) % pCache->cEntries;
-}
-
-/**
- * Internal. Get sector number in the extent file from the relative sector
- * number in the extent.
- */
-static int vmdkGetSector(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                         uint64_t uSector, uint64_t *puExtentSector)
-{
-    PVMDKGTCACHE pCache = pImage->pGTCache;
-    uint64_t uGDIndex, uGTSector, uGTBlock;
-    uint32_t uGTHash, uGTBlockIndex;
-    PVMDKGTCACHEENTRY pGTCacheEntry;
-    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
-    int rc;
-
-    /* For newly created and readonly/sequentially opened streamOptimized
-     * images this must be a no-op, as the grain directory is not there. */
-    if (   (   pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
-            && pExtent->uAppendPosition)
-        || (   pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
-            && pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY
-            && pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
-    {
-        *puExtentSector = 0;
-        return VINF_SUCCESS;
-    }
-
-    uGDIndex = uSector / pExtent->cSectorsPerGDE;
-    if (uGDIndex >= pExtent->cGDEntries)
-        return VERR_OUT_OF_RANGE;
-    uGTSector = pExtent->pGD[uGDIndex];
-    if (!uGTSector)
-    {
-        /* There is no grain table referenced by this grain directory
-         * entry. So there is absolutely no data in this area. */
-        *puExtentSector = 0;
-        return VINF_SUCCESS;
-    }
-
-    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
-    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
-    pGTCacheEntry = &pCache->aGTCache[uGTHash];
-    if (    pGTCacheEntry->uExtent != pExtent->uExtent
-        ||  pGTCacheEntry->uGTBlock != uGTBlock)
-    {
-        /* Cache miss, fetch data from disk. */
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                              aGTDataTmp, sizeof(aGTDataTmp), NULL);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname);
-        pGTCacheEntry->uExtent = pExtent->uExtent;
-        pGTCacheEntry->uGTBlock = uGTBlock;
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
-    }
-    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
-    uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
-    if (uGrainSector)
-        *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
-    else
-        *puExtentSector = 0;
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal. Get sector number in the extent file from the relative sector
- * number in the extent - version for async access.
- */
-static int vmdkGetSectorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
-                              PVMDKEXTENT pExtent, uint64_t uSector,
-                              uint64_t *puExtentSector)
-{
-    PVMDKGTCACHE pCache = pImage->pGTCache;
-    uint64_t uGDIndex, uGTSector, uGTBlock;
-    uint32_t uGTHash, uGTBlockIndex;
-    PVMDKGTCACHEENTRY pGTCacheEntry;
-    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
-    int rc;
-
-    uGDIndex = uSector / pExtent->cSectorsPerGDE;
-    if (uGDIndex >= pExtent->cGDEntries)
-        return VERR_OUT_OF_RANGE;
-    uGTSector = pExtent->pGD[uGDIndex];
-    if (!uGTSector)
-    {
-        /* There is no grain table referenced by this grain directory
-         * entry. So there is absolutely no data in this area. */
-        *puExtentSector = 0;
-        return VINF_SUCCESS;
-    }
-
-    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
-    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
-    pGTCacheEntry = &pCache->aGTCache[uGTHash];
-    if (    pGTCacheEntry->uExtent != pExtent->uExtent
-        ||  pGTCacheEntry->uGTBlock != uGTBlock)
-    {
-        /* Cache miss, fetch data from disk. */
-        PVDMETAXFER pMetaXfer;
-        rc = vmdkFileReadMetaAsync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                                   aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
-        if (RT_FAILURE(rc))
-            return rc;
-        /* We can release the metadata transfer immediately. */
-        vmdkFileMetaXferRelease(pImage, pMetaXfer);
-        pGTCacheEntry->uExtent = pExtent->uExtent;
-        pGTCacheEntry->uGTBlock = uGTBlock;
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
-    }
-    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
-    uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
-    if (uGrainSector)
-        *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
-    else
-        *puExtentSector = 0;
-    return VINF_SUCCESS;
-}
-
-/**
- * Internal. Allocates a new grain table (if necessary), writes the grain
- * and updates the grain table. The cache is also updated by this operation.
- * This is separate from vmdkGetSector, because that should be as fast as
- * possible. Most code from vmdkGetSector also appears here.
- */
-static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                          uint64_t uSector, const void *pvBuf,
-                          uint64_t cbWrite)
-{
-    PVMDKGTCACHE pCache = pImage->pGTCache;
-    uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
-    uint64_t uFileOffset;
-    uint32_t uGTHash, uGTBlockIndex;
-    PVMDKGTCACHEENTRY pGTCacheEntry;
-    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
-    int rc;
-
-    uGDIndex = uSector / pExtent->cSectorsPerGDE;
-    if (uGDIndex >= pExtent->cGDEntries)
-        return VERR_OUT_OF_RANGE;
-    uGTSector = pExtent->pGD[uGDIndex];
-    if (pExtent->pRGD)
-        uRGTSector = pExtent->pRGD[uGDIndex];
-    else
-        uRGTSector = 0; /**< avoid compiler warning */
-    if (!uGTSector)
-    {
-        /* There is no grain table referenced by this grain directory
-         * entry. So there is absolutely no data in this area. Allocate
-         * a new grain table and put the reference to it in the GDs. */
-        uFileOffset = pExtent->uAppendPosition;
-        if (!uFileOffset)
-            return VERR_INTERNAL_ERROR;
-        Assert(!(uFileOffset % 512));
-        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-        uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
-        pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
-
-        /* Normally the grain table is preallocated for hosted sparse extents
-         * that support more than 32 bit sector numbers. So this shouldn't
-         * ever happen on a valid extent. */
-        if (uGTSector > UINT32_MAX)
-            return VERR_VD_VMDK_INVALID_HEADER;
-
-        /* Write grain table by writing the required number of grain table
-         * cache chunks. Avoids dynamic memory allocation, but is a bit
-         * slower. But as this is a pretty infrequently occurring case it
-         * should be acceptable. */
-        memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
-        for (unsigned i = 0;
-             i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
-             i++)
-        {
-            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp),
-                                   aGTDataTmp, sizeof(aGTDataTmp), NULL);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
-        }
-        pExtent->uAppendPosition = RT_ALIGN_64(  pExtent->uAppendPosition
-                                               + pExtent->cGTEntries * sizeof(uint32_t),
-                                               512);
-
-        if (pExtent->pRGD)
-        {
-            AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
-            uFileOffset = pExtent->uAppendPosition;
-            if (!uFileOffset)
-                return VERR_INTERNAL_ERROR;
-            Assert(!(uFileOffset % 512));
-            uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
-            pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
-
-            /* Normally the redundant grain table is preallocated for hosted
-             * sparse extents that support more than 32 bit sector numbers. So
-             * this shouldn't ever happen on a valid extent. */
-            if (uRGTSector > UINT32_MAX)
-                return VERR_VD_VMDK_INVALID_HEADER;
-
-            /* Write backup grain table by writing the required number of grain
-             * table cache chunks. Avoids dynamic memory allocation, but is a
-             * bit slower. But as this is a pretty infrequently occurring case
-             * it should be acceptable. */
-            for (unsigned i = 0;
-                 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
-                 i++)
-            {
-                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                       VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp),
-                                       aGTDataTmp, sizeof(aGTDataTmp), NULL);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
-            }
-
-            pExtent->uAppendPosition =   pExtent->uAppendPosition
-                                       + pExtent->cGTEntries * sizeof(uint32_t);
-        }
-
-        /* Update the grain directory on disk (doing it before writing the
-         * grain table will result in a garbled extent if the operation is
-         * aborted for some reason. Otherwise the worst that can happen is
-         * some unused sectors in the extent. */
-        uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
-        rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                               VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
-                               &uGTSectorLE, sizeof(uGTSectorLE), NULL);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
-        if (pExtent->pRGD)
-        {
-            uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
-            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE),
-                                   &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
-            if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
-        }
-
-        /* As the final step update the in-memory copy of the GDs. */
-        pExtent->pGD[uGDIndex] = uGTSector;
-        if (pExtent->pRGD)
-            pExtent->pRGD[uGDIndex] = uRGTSector;
-    }
-
-    uFileOffset = pExtent->uAppendPosition;
-    if (!uFileOffset)
-        return VERR_INTERNAL_ERROR;
-    Assert(!(uFileOffset % 512));
-
-    /* Write the data. Always a full grain, or we're in big trouble. */
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
-            return vmdkError(pImage, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
-
-        /* Invalidate cache, just in case some code incorrectly allows mixing
-         * of reads and writes. Normally shouldn't be needed. */
-        pExtent->uGrainSectorAbs = 0;
-
-        /* Write compressed data block and the markers. */
-        uint32_t cbGrain = 0;
-        rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
-                                 pvBuf, cbWrite, uSector, &cbGrain);
-        if (RT_FAILURE(rc))
-        {
-            AssertRC(rc);
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
-        }
-        pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
-        pExtent->uAppendPosition += cbGrain;
-    }
-    else
-    {
-        rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
-                               pvBuf, cbWrite, NULL);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
-        pExtent->uAppendPosition += cbWrite;
-    }
-
-    /* Update the grain table (and the cache). */
-    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
-    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
-    pGTCacheEntry = &pCache->aGTCache[uGTHash];
-    if (    pGTCacheEntry->uExtent != pExtent->uExtent
-        ||  pGTCacheEntry->uGTBlock != uGTBlock)
-    {
-        /* Cache miss, fetch data from disk. */
-        rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                              VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                              aGTDataTmp, sizeof(aGTDataTmp), NULL);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
-        pGTCacheEntry->uExtent = pExtent->uExtent;
-        pGTCacheEntry->uGTBlock = uGTBlock;
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
-    }
-    else
-    {
-        /* Cache hit. Convert grain table block back to disk format, otherwise
-         * the code below will write garbage for all but the updated entry. */
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
-    }
-    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
-    aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(uFileOffset));
-    pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(uFileOffset);
-    /* Update grain table on disk. */
-    rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                           VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                           aGTDataTmp, sizeof(aGTDataTmp), NULL);
-    if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
-    if (pExtent->pRGD)
-    {
-        /* Update backup grain table on disk. */
-        rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                               VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                               aGTDataTmp, sizeof(aGTDataTmp), NULL);
-        if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
-    }
-#ifdef VBOX_WITH_VMDK_ESX
-    if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
-    {
-        pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
-        pExtent->fMetaDirty = true;
-    }
-#endif /* VBOX_WITH_VMDK_ESX */
-    return rc;
-}
-
-/**
- * Internal. Writes the grain and also if necessary the grain tables.
- * Uses the grain table cache as a true grain table.
- */
-static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                uint64_t uSector, const void *pvBuf,
-                                uint64_t cbWrite)
-{
-    uint32_t uGrain;
-    uint32_t uGDEntry, uLastGDEntry;
-    uint32_t cbGrain = 0;
-    uint32_t uCacheLine, uCacheEntry;
-    const void *pData = pvBuf;
-    int rc;
-
-    /* Very strict requirements: always write at least one full grain, with
-     * proper alignment. Everything else would require reading of already
-     * written data, which we don't support for obvious reasons. The only
-     * exception is the last grain, and only if the image size specifies
-     * that only some portion holds data. In any case the write must be
-     * within the image limits, no "overshoot" allowed. */
-    if (   cbWrite == 0
-        || (   cbWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
-            && pExtent->cNominalSectors - uSector >= pExtent->cSectorsPerGrain)
-        || uSector % pExtent->cSectorsPerGrain
-        || uSector + VMDK_BYTE2SECTOR(cbWrite) > pExtent->cNominalSectors)
-        return VERR_INVALID_PARAMETER;
-
-    /* Clip write range to at most the rest of the grain. */
-    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain));
-
-    /* Do not allow to go back. */
-    uGrain = uSector / pExtent->cSectorsPerGrain;
-    uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
-    uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE;
-    uGDEntry = uGrain / pExtent->cGTEntries;
-    uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
-    if (uGrain < pExtent->uLastGrainAccess)
-        return VERR_VD_VMDK_INVALID_WRITE;
-
-    /* Zero byte write optimization. Since we don't tell VBoxHDD that we need
-     * to allocate something, we also need to detect the situation ourself. */
-    if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
-        && ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbWrite * 8) == -1)
-        return VINF_SUCCESS;
-
-    if (uGDEntry != uLastGDEntry)
-    {
-        rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
-        if (RT_FAILURE(rc))
-            return rc;
-        vmdkStreamClearGT(pImage, pExtent);
-        for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++)
-        {
-            rc = vmdkStreamFlushGT(pImage, pExtent, i);
-            if (RT_FAILURE(rc))
-                return rc;
-        }
-    }
-
-    uint64_t uFileOffset;
-    uFileOffset = pExtent->uAppendPosition;
-    if (!uFileOffset)
-        return VERR_INTERNAL_ERROR;
-    /* Align to sector, as the previous write could have been any size. */
-    uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-
-    /* Paranoia check: extent type, grain table buffer presence and
-     * grain table buffer space. Also grain table entry must be clear. */
-    if (   pExtent->enmType != VMDKETYPE_HOSTED_SPARSE
-        || !pImage->pGTCache
-        || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE
-        || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry])
-        return VERR_INTERNAL_ERROR;
-
-    /* Update grain table entry. */
-    pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset);
-
-    if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
-    {
-        memcpy(pExtent->pvGrain, pvBuf, cbWrite);
-        memset((char *)pExtent->pvGrain + cbWrite, '\0',
-               VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite);
-        pData = pExtent->pvGrain;
-    }
-    rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
-                             VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
-                             uSector, &cbGrain);
-    if (RT_FAILURE(rc))
-    {
-        pExtent->uGrainSectorAbs = 0;
-        AssertRC(rc);
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
-    }
-    pExtent->uLastGrainAccess = uGrain;
-    pExtent->uAppendPosition += cbGrain;
-
-    return rc;
-}
-
-/**
- * Internal: Updates the grain table during a async grain allocation.
- */
-static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                       PVDIOCTX pIoCtx,
-                                       PVMDKGRAINALLOCASYNC pGrainAlloc)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKGTCACHE pCache = pImage->pGTCache;
-    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
-    uint32_t uGTHash, uGTBlockIndex;
-    uint64_t uGTSector, uRGTSector, uGTBlock;
-    uint64_t uSector = pGrainAlloc->uSector;
-    PVMDKGTCACHEENTRY pGTCacheEntry;
-
-    LogFlowFunc(("pImage=%#p pExtent=%#p pCache=%#p pIoCtx=%#p pGrainAlloc=%#p\n",
-                 pImage, pExtent, pCache, pIoCtx, pGrainAlloc));
-
-    uGTSector = pGrainAlloc->uGTSector;
-    uRGTSector = pGrainAlloc->uRGTSector;
-    LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
-
-    /* Update the grain table (and the cache). */
-    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
-    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
-    pGTCacheEntry = &pCache->aGTCache[uGTHash];
-    if (    pGTCacheEntry->uExtent != pExtent->uExtent
-        ||  pGTCacheEntry->uGTBlock != uGTBlock)
-    {
-        /* Cache miss, fetch data from disk. */
-        LogFlow(("Cache miss, fetch data from disk\n"));
-        PVDMETAXFER pMetaXfer = NULL;
-        rc = vmdkFileReadMetaAsync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                                   aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
-                                   &pMetaXfer, vmdkAllocGrainAsyncComplete, pGrainAlloc);
-        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        {
-            pGrainAlloc->cIoXfersPending++;
-            pGrainAlloc->fGTUpdateNeeded = true;
-            /* Leave early, we will be called  again after the read completed. */
-            LogFlowFunc(("Metadata read in progress, leaving\n"));
-            return rc;
-        }
-        else if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
-        vmdkFileMetaXferRelease(pImage, pMetaXfer);
-        pGTCacheEntry->uExtent = pExtent->uExtent;
-        pGTCacheEntry->uGTBlock = uGTBlock;
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
-    }
-    else
-    {
-        /* Cache hit. Convert grain table block back to disk format, otherwise
-         * the code below will write garbage for all but the updated entry. */
-        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
-            aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
-    }
-    pGrainAlloc->fGTUpdateNeeded = false;
-    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
-    aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset));
-    pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset);
-    /* Update grain table on disk. */
-    rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                                aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
-                                vmdkAllocGrainAsyncComplete, pGrainAlloc);
-    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        pGrainAlloc->cIoXfersPending++;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
-    if (pExtent->pRGD)
-    {
-        /* Update backup grain table on disk. */
-        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                    VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
-                                    aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
-                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
-        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-            pGrainAlloc->cIoXfersPending++;
-        else if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
-    }
-#ifdef VBOX_WITH_VMDK_ESX
-    if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
-    {
-        pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
-        pExtent->fMetaDirty = true;
-    }
-#endif /* VBOX_WITH_VMDK_ESX */
-
-    LogFlowFunc(("leaving rc=%Rrc\n", rc));
-
-    return rc;
-}
-
-/**
- * Internal - complete the grain allocation by updating disk grain table if required.
- */
-static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
-{
-    int rc = VINF_SUCCESS;
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKGRAINALLOCASYNC pGrainAlloc = (PVMDKGRAINALLOCASYNC)pvUser;
-    PVMDKEXTENT pExtent = pGrainAlloc->pExtent;
-
-    LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc\n",
-                 pBackendData, pIoCtx, pvUser, rcReq));
-
-    pGrainAlloc->cIoXfersPending--;
-    if (!pGrainAlloc->cIoXfersPending && pGrainAlloc->fGTUpdateNeeded)
-        rc = vmdkAllocGrainAsyncGTUpdate(pImage, pGrainAlloc->pExtent,
-                                         pIoCtx, pGrainAlloc);
-
-    if (!pGrainAlloc->cIoXfersPending)
-    {
-        /* Grain allocation completed. */
-        RTMemFree(pGrainAlloc);
-    }
-
-    LogFlowFunc(("Leaving rc=%Rrc\n", rc));
-    return rc;
-}
-
-/**
- * Internal. Allocates a new grain table (if necessary) - async version.
- */
-static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                               PVDIOCTX pIoCtx, uint64_t uSector,
-                               uint64_t cbWrite)
-{
-    PVMDKGTCACHE pCache = pImage->pGTCache;
-    uint64_t uGDIndex, uGTSector, uRGTSector;
-    uint64_t uFileOffset;
-    PVMDKGRAINALLOCASYNC pGrainAlloc = NULL;
-    int rc;
-
-    LogFlowFunc(("pCache=%#p pExtent=%#p pIoCtx=%#p uSector=%llu cbWrite=%llu\n",
-                 pCache, pExtent, pIoCtx, uSector, cbWrite));
-
-    AssertReturn(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), VERR_NOT_SUPPORTED);
-
-    pGrainAlloc = (PVMDKGRAINALLOCASYNC)RTMemAllocZ(sizeof(VMDKGRAINALLOCASYNC));
-    if (!pGrainAlloc)
-        return VERR_NO_MEMORY;
-
-    pGrainAlloc->pExtent = pExtent;
-    pGrainAlloc->uSector = uSector;
-
-    uGDIndex = uSector / pExtent->cSectorsPerGDE;
-    if (uGDIndex >= pExtent->cGDEntries)
-    {
-        RTMemFree(pGrainAlloc);
-        return VERR_OUT_OF_RANGE;
-    }
-    uGTSector = pExtent->pGD[uGDIndex];
-    if (pExtent->pRGD)
-        uRGTSector = pExtent->pRGD[uGDIndex];
-    else
-        uRGTSector = 0; /**< avoid compiler warning */
-    if (!uGTSector)
-    {
-        LogFlow(("Allocating new grain table\n"));
-
-        /* There is no grain table referenced by this grain directory
-         * entry. So there is absolutely no data in this area. Allocate
-         * a new grain table and put the reference to it in the GDs. */
-        uFileOffset = pExtent->uAppendPosition;
-        if (!uFileOffset)
-            return VERR_INTERNAL_ERROR;
-        Assert(!(uFileOffset % 512));
-
-        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-        uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
-        /* Normally the grain table is preallocated for hosted sparse extents
-         * that support more than 32 bit sector numbers. So this shouldn't
-         * ever happen on a valid extent. */
-        if (uGTSector > UINT32_MAX)
-            return VERR_VD_VMDK_INVALID_HEADER;
-
-        /* Write grain table by writing the required number of grain table
-         * cache chunks. Allocate memory dynamically here or we flood the
-         * metadata cache with very small entries. */
-        size_t cbGTDataTmp = pExtent->cGTEntries * sizeof(uint32_t);
-        uint32_t *paGTDataTmp = (uint32_t *)RTMemTmpAllocZ(cbGTDataTmp);
-
-        if (!paGTDataTmp)
-            return VERR_NO_MEMORY;
-
-        memset(paGTDataTmp, '\0', cbGTDataTmp);
-        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                    VMDK_SECTOR2BYTE(uGTSector),
-                                    paGTDataTmp, cbGTDataTmp, pIoCtx,
-                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
-        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-            pGrainAlloc->cIoXfersPending++;
-        else if (RT_FAILURE(rc))
-        {
-            RTMemTmpFree(paGTDataTmp);
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
-        }
-        pExtent->uAppendPosition = RT_ALIGN_64(  pExtent->uAppendPosition
-                                               + cbGTDataTmp, 512);
-
-        if (pExtent->pRGD)
-        {
-            AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
-            uFileOffset = pExtent->uAppendPosition;
-            if (!uFileOffset)
-                return VERR_INTERNAL_ERROR;
-            Assert(!(uFileOffset % 512));
-            uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
-
-            /* Normally the redundant grain table is preallocated for hosted
-             * sparse extents that support more than 32 bit sector numbers. So
-             * this shouldn't ever happen on a valid extent. */
-            if (uRGTSector > UINT32_MAX)
-            {
-                RTMemTmpFree(paGTDataTmp);
-                return VERR_VD_VMDK_INVALID_HEADER;
-            }
-
-            /* Write grain table by writing the required number of grain table
-             * cache chunks. Allocate memory dynamically here or we flood the
-             * metadata cache with very small entries. */
-            rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                        VMDK_SECTOR2BYTE(uRGTSector),
-                                        paGTDataTmp, cbGTDataTmp, pIoCtx,
-                                        vmdkAllocGrainAsyncComplete, pGrainAlloc);
-            if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                pGrainAlloc->cIoXfersPending++;
-            else if (RT_FAILURE(rc))
-            {
-                RTMemTmpFree(paGTDataTmp);
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
-            }
-
-            pExtent->uAppendPosition = pExtent->uAppendPosition + cbGTDataTmp;
-        }
-
-        RTMemTmpFree(paGTDataTmp);
-
-        /* Update the grain directory on disk (doing it before writing the
-         * grain table will result in a garbled extent if the operation is
-         * aborted for some reason. Otherwise the worst that can happen is
-         * some unused sectors in the extent. */
-        uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
-        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                    VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
-                                    &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
-                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
-        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-            pGrainAlloc->cIoXfersPending++;
-        else if (RT_FAILURE(rc))
-            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
-        if (pExtent->pRGD)
-        {
-            uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
-            rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
-                                        VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
-                                        &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
-                                        vmdkAllocGrainAsyncComplete, pGrainAlloc);
-            if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-                pGrainAlloc->cIoXfersPending++;
-            else if (RT_FAILURE(rc))
-                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
-        }
-
-        /* As the final step update the in-memory copy of the GDs. */
-        pExtent->pGD[uGDIndex] = uGTSector;
-        if (pExtent->pRGD)
-            pExtent->pRGD[uGDIndex] = uRGTSector;
-    }
-
-    LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
-    pGrainAlloc->uGTSector = uGTSector;
-    pGrainAlloc->uRGTSector = uRGTSector;
-
-    uFileOffset = pExtent->uAppendPosition;
-    if (!uFileOffset)
-        return VERR_INTERNAL_ERROR;
-    Assert(!(uFileOffset % 512));
-
-    pGrainAlloc->uGrainOffset = uFileOffset;
-
-    /* Write the data. Always a full grain, or we're in big trouble. */
-    rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
-                                uFileOffset, pIoCtx, cbWrite,
-                                vmdkAllocGrainAsyncComplete, pGrainAlloc);
-    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
-        pGrainAlloc->cIoXfersPending++;
-    else if (RT_FAILURE(rc))
-        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
-
-    rc = vmdkAllocGrainAsyncGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
-
-    if (!pGrainAlloc->cIoXfersPending)
-    {
-        /* Grain allocation completed. */
-        RTMemFree(pGrainAlloc);
-    }
-
-    LogFlowFunc(("leaving rc=%Rrc\n", rc));
-
-    return rc;
-}
-
-/**
- * Internal. Reads the contents by sequentially going over the compressed
- * grains (hoping that they are in sequence).
- */
-static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
-                                    uint64_t uSector, void *pvBuf,
-                                    uint64_t cbRead)
-{
-    int rc;
-
-    /* Do not allow to go back. */
-    uint32_t uGrain = uSector / pExtent->cSectorsPerGrain;
-    if (uGrain < pExtent->uLastGrainAccess)
-        return VERR_VD_VMDK_INVALID_STATE;
-    pExtent->uLastGrainAccess = uGrain;
-
-    /* After a previous error do not attempt to recover, as it would need
-     * seeking (in the general case backwards which is forbidden). */
-    if (!pExtent->uGrainSectorAbs)
-        return VERR_VD_VMDK_INVALID_STATE;
-
-    /* Check if we need to read something from the image or if what we have
-     * in the buffer is good to fulfill the request. */
-    if (!pExtent->cbGrainStreamRead || uGrain > pExtent->uGrain)
-    {
-        uint32_t uGrainSectorAbs =   pExtent->uGrainSectorAbs
-                                   + VMDK_BYTE2SECTOR(pExtent->cbGrainStreamRead);
-
-        /* Get the marker from the next data block - and skip everything which
-         * is not a compressed grain. If it's a compressed grain which is for
-         * the requested sector (or after), read it. */
-        VMDKMARKER Marker;
-        do
-        {
-            RT_ZERO(Marker);
-            rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                  VMDK_SECTOR2BYTE(uGrainSectorAbs),
-                                  &Marker, RT_OFFSETOF(VMDKMARKER, uType),
-                                  NULL);
-            if (RT_FAILURE(rc))
-                return rc;
-            Marker.uSector = RT_LE2H_U64(Marker.uSector);
-            Marker.cbSize = RT_LE2H_U32(Marker.cbSize);
-
-            if (Marker.cbSize == 0)
-            {
-                /* A marker for something else than a compressed grain. */
-                rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                        VMDK_SECTOR2BYTE(uGrainSectorAbs)
-                                      + RT_OFFSETOF(VMDKMARKER, uType),
-                                      &Marker.uType, sizeof(Marker.uType),
-                                      NULL);
-                if (RT_FAILURE(rc))
-                    return rc;
-                Marker.uType = RT_LE2H_U32(Marker.uType);
-                switch (Marker.uType)
-                {
-                    case VMDK_MARKER_EOS:
-                        uGrainSectorAbs++;
-                        /* Read (or mostly skip) to the end of file. Uses the
-                         * Marker (LBA sector) as it is unused anyway. This
-                         * makes sure that really everything is read in the
-                         * success case. If this read fails it means the image
-                         * is truncated, but this is harmless so ignore. */
-                        vmdkFileReadSync(pImage, pExtent->pFile,
-                                           VMDK_SECTOR2BYTE(uGrainSectorAbs)
-                                         + 511,
-                                         &Marker.uSector, 1, NULL);
-                        break;
-                    case VMDK_MARKER_GT:
-                        uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
-                        break;
-                    case VMDK_MARKER_GD:
-                        uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(RT_ALIGN(pExtent->cGDEntries * sizeof(uint32_t), 512));
-                        break;
-                    case VMDK_MARKER_FOOTER:
-                        uGrainSectorAbs += 2;
-                        break;
-                    default:
-                        AssertMsgFailed(("VMDK: corrupted marker, type=%#x\n", Marker.uType));
-                        pExtent->uGrainSectorAbs = 0;
-                        return VERR_VD_VMDK_INVALID_STATE;
-                }
-                pExtent->cbGrainStreamRead = 0;
-            }
-            else
-            {
-                /* A compressed grain marker. If it is at/after what we're
-                 * interested in read and decompress data. */
-                if (uSector > Marker.uSector + pExtent->cSectorsPerGrain)
-                {
-                    uGrainSectorAbs += VMDK_BYTE2SECTOR(RT_ALIGN(Marker.cbSize + RT_OFFSETOF(VMDKMARKER, uType), 512));
-                    continue;
-                }
-                uint64_t uLBA;
-                uint32_t cbGrainStreamRead = 0;
-                rc = vmdkFileInflateSync(pImage, pExtent,
-                                         VMDK_SECTOR2BYTE(uGrainSectorAbs),
-                                         pExtent->pvGrain,
-                                         VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
-                                         &Marker, &uLBA, &cbGrainStreamRead);
-                if (RT_FAILURE(rc))
-                {
-                    pExtent->uGrainSectorAbs = 0;
-                    return rc;
-                }
-                if (   pExtent->uGrain
-                    && uLBA / pExtent->cSectorsPerGrain <= pExtent->uGrain)
-                {
-                    pExtent->uGrainSectorAbs = 0;
-                    return VERR_VD_VMDK_INVALID_STATE;
-                }
-                pExtent->uGrain = uLBA / pExtent->cSectorsPerGrain;
-                pExtent->cbGrainStreamRead = cbGrainStreamRead;
-                break;
-            }
-        } while (Marker.uType != VMDK_MARKER_EOS);
-
-        pExtent->uGrainSectorAbs = uGrainSectorAbs;
-
-        if (!pExtent->cbGrainStreamRead && Marker.uType == VMDK_MARKER_EOS)
-        {
-            pExtent->uGrain = UINT32_MAX;
-            /* Must set a non-zero value for pExtent->cbGrainStreamRead or
-             * the next read would try to get more data, and we're at EOF. */
-            pExtent->cbGrainStreamRead = 1;
-        }
-    }
-
-    if (pExtent->uGrain > uSector / pExtent->cSectorsPerGrain)
-    {
-        /* The next data block we have is not for this area, so just return
-         * that there is no data. */
-        return VERR_VD_BLOCK_FREE;
-    }
-
-    uint32_t uSectorInGrain = uSector % pExtent->cSectorsPerGrain;
-    memcpy(pvBuf,
-           (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
-           cbRead);
-    return VINF_SUCCESS;
-}
-
-/**
- * Replaces a fragment of a string with the specified string.
- *
- * @returns Pointer to the allocated UTF-8 string.
- * @param   pszWhere        UTF-8 string to search in.
- * @param   pszWhat         UTF-8 string to search for.
- * @param   pszByWhat       UTF-8 string to replace the found string with.
- */
-static char *vmdkStrReplace(const char *pszWhere, const char *pszWhat,
-                            const char *pszByWhat)
-{
-    AssertPtr(pszWhere);
-    AssertPtr(pszWhat);
-    AssertPtr(pszByWhat);
-    const char *pszFoundStr = strstr(pszWhere, pszWhat);
-    if (!pszFoundStr)
-        return NULL;
-    size_t cFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
-    char *pszNewStr = (char *)RTMemAlloc(cFinal);
-    if (pszNewStr)
-    {
-        char *pszTmp = pszNewStr;
-        memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
-        pszTmp += pszFoundStr - pszWhere;
-        memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
-        pszTmp += strlen(pszByWhat);
-        strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
-    }
-    return pszNewStr;
-}
-
-
-/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
-static int vmdkCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
-                            PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
-{
-    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
-                 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
-    int rc = VINF_SUCCESS;
-    PVMDKIMAGE pImage;
-
-    if (   !pszFilename
-        || !*pszFilename
-        || strchr(pszFilename, '"'))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pFile = NULL;
-    pImage->pExtents = NULL;
-    pImage->pFiles = NULL;
-    pImage->pGTCache = NULL;
-    pImage->pDescData = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-    /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
-     * much as possible in vmdkOpenImage. */
-    rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
-    vmdkFreeImage(pImage, false);
-    RTMemFree(pImage);
-
-    if (RT_SUCCESS(rc))
-        *penmType = VDTYPE_HDD;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnOpen */
-static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
-                    PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                    VDTYPE enmType, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
-    int rc;
-    PVMDKIMAGE pImage;
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename
-        || strchr(pszFilename, '"'))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pFile = NULL;
-    pImage->pExtents = NULL;
-    pImage->pFiles = NULL;
-    pImage->pGTCache = NULL;
-    pImage->pDescData = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-
-    rc = vmdkOpenImage(pImage, uOpenFlags);
-    if (RT_SUCCESS(rc))
-        *ppBackendData = pImage;
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnCreate */
-static int vmdkCreate(const char *pszFilename, uint64_t cbSize,
-                      unsigned uImageFlags, const char *pszComment,
-                      PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
-                      PCRTUUID pUuid, unsigned uOpenFlags,
-                      unsigned uPercentStart, unsigned uPercentSpan,
-                      PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
-                      PVDINTERFACE pVDIfsOperation, void **ppBackendData)
-{
-    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
-    int rc;
-    PVMDKIMAGE pImage;
-
-    PFNVDPROGRESS pfnProgress = NULL;
-    void *pvUser = NULL;
-    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
-                                              VDINTERFACETYPE_PROGRESS);
-    PVDINTERFACEPROGRESS pCbProgress = NULL;
-    if (pIfProgress)
-    {
-        pCbProgress = VDGetInterfaceProgress(pIfProgress);
-        pfnProgress = pCbProgress->pfnProgress;
-        pvUser = pIfProgress->pvUser;
-    }
-
-    /* Check open flags. All valid flags are supported. */
-    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Check size. Maximum 2TB-64K for sparse images, otherwise unlimited. */
-    if (    !cbSize
-        ||  (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 2 - _64K))
-    {
-        rc = VERR_VD_INVALID_SIZE;
-        goto out;
-    }
-
-    /* Check remaining arguments. */
-    if (   !VALID_PTR(pszFilename)
-        || !*pszFilename
-        || strchr(pszFilename, '"')
-        || !VALID_PTR(pPCHSGeometry)
-        || !VALID_PTR(pLCHSGeometry)
-#ifndef VBOX_WITH_VMDK_ESX
-        || (    uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX
-            &&  !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
-#endif
-        || (   (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-            && (uImageFlags & ~(VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_IMAGE_FLAGS_DIFF))))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
-    if (!pImage)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-    pImage->pszFilename = pszFilename;
-    pImage->pFile = NULL;
-    pImage->pExtents = NULL;
-    pImage->pFiles = NULL;
-    pImage->pGTCache = NULL;
-    pImage->pDescData = NULL;
-    pImage->pVDIfsDisk = pVDIfsDisk;
-    pImage->pVDIfsImage = pVDIfsImage;
-    /* Descriptors for split images can be pretty large, especially if the
-     * filename is long. So prepare for the worst, and allocate quite some
-     * memory for the descriptor in this case. */
-    if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
-        pImage->cbDescAlloc = VMDK_SECTOR2BYTE(200);
-    else
-        pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
-    pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
-    if (!pImage->pDescData)
-    {
-        RTMemFree(pImage);
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    rc = vmdkCreateImage(pImage, cbSize, uImageFlags, pszComment,
-                         pPCHSGeometry, pLCHSGeometry, pUuid,
-                         pfnProgress, pvUser, uPercentStart, uPercentSpan);
-    if (RT_SUCCESS(rc))
-    {
-        /* So far the image is opened in read/write mode. Make sure the
-         * image is opened in read-only mode if the caller requested that. */
-        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            vmdkFreeImage(pImage, false);
-            rc = vmdkOpenImage(pImage, uOpenFlags);
-            if (RT_FAILURE(rc))
-                goto out;
-        }
-        *ppBackendData = pImage;
-    }
-    else
-    {
-        RTMemFree(pImage->pDescData);
-        RTMemFree(pImage);
-    }
-
-out:
-    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRename */
-static int vmdkRename(void *pBackendData, const char *pszFilename)
-{
-    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
-
-    PVMDKIMAGE  pImage  = (PVMDKIMAGE)pBackendData;
-    int               rc = VINF_SUCCESS;
-    char   **apszOldName = NULL;
-    char   **apszNewName = NULL;
-    char  **apszNewLines = NULL;
-    char *pszOldDescName = NULL;
-    bool     fImageFreed = false;
-    bool   fEmbeddedDesc = false;
-    unsigned    cExtents = pImage->cExtents;
-    char *pszNewBaseName = NULL;
-    char *pszOldBaseName = NULL;
-    char *pszNewFullName = NULL;
-    char *pszOldFullName = NULL;
-    const char *pszOldImageName;
-    unsigned i, line;
-    VMDKDESCRIPTOR DescriptorCopy;
-    VMDKEXTENT ExtentCopy;
-
-    memset(&DescriptorCopy, 0, sizeof(DescriptorCopy));
-
-    /* Check arguments. */
-    if (   !pImage
-        || (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
-        || !VALID_PTR(pszFilename)
-        || !*pszFilename)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /*
-     * Allocate an array to store both old and new names of renamed files
-     * in case we have to roll back the changes. Arrays are initialized
-     * with zeros. We actually save stuff when and if we change it.
-     */
-    apszOldName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
-    apszNewName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
-    apszNewLines = (char **)RTMemTmpAllocZ((cExtents) * sizeof(char*));
-    if (!apszOldName || !apszNewName || !apszNewLines)
-    {
-        rc = VERR_NO_MEMORY;
-        goto out;
-    }
-
-    /* Save the descriptor size and position. */
-    if (pImage->pDescData)
-    {
-        /* Separate descriptor file. */
-        fEmbeddedDesc = false;
-    }
-    else
-    {
-        /* Embedded descriptor file. */
-        ExtentCopy = pImage->pExtents[0];
-        fEmbeddedDesc = true;
-    }
-    /* Save the descriptor content. */
-    DescriptorCopy.cLines = pImage->Descriptor.cLines;
-    for (i = 0; i < DescriptorCopy.cLines; i++)
-    {
-        DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
-        if (!DescriptorCopy.aLines[i])
-        {
-            rc = VERR_NO_MEMORY;
-            goto out;
-        }
-    }
-
-    /* Prepare both old and new base names used for string replacement. */
-    pszNewBaseName = RTStrDup(RTPathFilename(pszFilename));
-    RTPathStripExt(pszNewBaseName);
-    pszOldBaseName = RTStrDup(RTPathFilename(pImage->pszFilename));
-    RTPathStripExt(pszOldBaseName);
-    /* Prepare both old and new full names used for string replacement. */
-    pszNewFullName = RTStrDup(pszFilename);
-    RTPathStripExt(pszNewFullName);
-    pszOldFullName = RTStrDup(pImage->pszFilename);
-    RTPathStripExt(pszOldFullName);
-
-    /* --- Up to this point we have not done any damage yet. --- */
-
-    /* Save the old name for easy access to the old descriptor file. */
-    pszOldDescName = RTStrDup(pImage->pszFilename);
-    /* Save old image name. */
-    pszOldImageName = pImage->pszFilename;
-
-    /* Update the descriptor with modified extent names. */
-    for (i = 0, line = pImage->Descriptor.uFirstExtent;
-        i < cExtents;
-        i++, line = pImage->Descriptor.aNextLines[line])
-    {
-        /* Assume that vmdkStrReplace will fail. */
-        rc = VERR_NO_MEMORY;
-        /* Update the descriptor. */
-        apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
-            pszOldBaseName, pszNewBaseName);
-        if (!apszNewLines[i])
-            goto rollback;
-        pImage->Descriptor.aLines[line] = apszNewLines[i];
-    }
-    /* Make sure the descriptor gets written back. */
-    pImage->Descriptor.fDirty = true;
-    /* Flush the descriptor now, in case it is embedded. */
-    vmdkFlushImage(pImage);
-
-    /* Close and rename/move extents. */
-    for (i = 0; i < cExtents; i++)
-    {
-        PVMDKEXTENT pExtent = &pImage->pExtents[i];
-        /* Compose new name for the extent. */
-        apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
-            pszOldFullName, pszNewFullName);
-        if (!apszNewName[i])
-            goto rollback;
-        /* Close the extent file. */
-        vmdkFileClose(pImage, &pExtent->pFile, false);
-        /* Rename the extent file. */
-        rc = vmdkFileMove(pImage, pExtent->pszFullname, apszNewName[i], 0);
-        if (RT_FAILURE(rc))
-            goto rollback;
-        /* Remember the old name. */
-        apszOldName[i] = RTStrDup(pExtent->pszFullname);
-    }
-    /* Release all old stuff. */
-    vmdkFreeImage(pImage, false);
-
-    fImageFreed = true;
-
-    /* Last elements of new/old name arrays are intended for
-     * storing descriptor's names.
-     */
-    apszNewName[cExtents] = RTStrDup(pszFilename);
-    /* Rename the descriptor file if it's separate. */
-    if (!fEmbeddedDesc)
-    {
-        rc = vmdkFileMove(pImage, pImage->pszFilename, apszNewName[cExtents], 0);
-        if (RT_FAILURE(rc))
-            goto rollback;
-        /* Save old name only if we may need to change it back. */
-        apszOldName[cExtents] = RTStrDup(pszFilename);
-    }
-
-    /* Update pImage with the new information. */
-    pImage->pszFilename = pszFilename;
-
-    /* Open the new image. */
-    rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
-    if (RT_SUCCESS(rc))
-        goto out;
-
-rollback:
-    /* Roll back all changes in case of failure. */
-    if (RT_FAILURE(rc))
-    {
-        int rrc;
-        if (!fImageFreed)
-        {
-            /*
-             * Some extents may have been closed, close the rest. We will
-             * re-open the whole thing later.
-             */
-            vmdkFreeImage(pImage, false);
-        }
-        /* Rename files back. */
-        for (i = 0; i <= cExtents; i++)
-        {
-            if (apszOldName[i])
-            {
-                rrc = vmdkFileMove(pImage, apszNewName[i], apszOldName[i], 0);
-                AssertRC(rrc);
-            }
-        }
-        /* Restore the old descriptor. */
-        PVMDKFILE pFile;
-        rrc = vmdkFileOpen(pImage, &pFile, pszOldDescName,
-                           VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_NORMAL,
-                                                      false /* fCreate */),
-                           false /* fAsyncIO */);
-        AssertRC(rrc);
-        if (fEmbeddedDesc)
-        {
-            ExtentCopy.pFile = pFile;
-            pImage->pExtents = &ExtentCopy;
-        }
-        else
-        {
-            /* Shouldn't be null for separate descriptor.
-             * There will be no access to the actual content.
-             */
-            pImage->pDescData = pszOldDescName;
-            pImage->pFile = pFile;
-        }
-        pImage->Descriptor = DescriptorCopy;
-        vmdkWriteDescriptor(pImage);
-        vmdkFileClose(pImage, &pFile, false);
-        /* Get rid of the stuff we implanted. */
-        pImage->pExtents = NULL;
-        pImage->pFile = NULL;
-        pImage->pDescData = NULL;
-        /* Re-open the image back. */
-        pImage->pszFilename = pszOldImageName;
-        rrc = vmdkOpenImage(pImage, pImage->uOpenFlags);
-        AssertRC(rrc);
-    }
-
-out:
-    for (i = 0; i < DescriptorCopy.cLines; i++)
-        if (DescriptorCopy.aLines[i])
-            RTStrFree(DescriptorCopy.aLines[i]);
-    if (apszOldName)
-    {
-        for (i = 0; i <= cExtents; i++)
-            if (apszOldName[i])
-                RTStrFree(apszOldName[i]);
-        RTMemTmpFree(apszOldName);
-    }
-    if (apszNewName)
-    {
-        for (i = 0; i <= cExtents; i++)
-            if (apszNewName[i])
-                RTStrFree(apszNewName[i]);
-        RTMemTmpFree(apszNewName);
-    }
-    if (apszNewLines)
-    {
-        for (i = 0; i < cExtents; i++)
-            if (apszNewLines[i])
-                RTStrFree(apszNewLines[i]);
-        RTMemTmpFree(apszNewLines);
-    }
-    if (pszOldDescName)
-        RTStrFree(pszOldDescName);
-    if (pszOldBaseName)
-        RTStrFree(pszOldBaseName);
-    if (pszNewBaseName)
-        RTStrFree(pszNewBaseName);
-    if (pszOldFullName)
-        RTStrFree(pszOldFullName);
-    if (pszNewFullName)
-        RTStrFree(pszNewFullName);
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnClose */
-static int vmdkClose(void *pBackendData, bool fDelete)
-{
-    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    rc = vmdkFreeImage(pImage, fDelete);
-    RTMemFree(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnRead */
-static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
-                    size_t cbToRead, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKEXTENT pExtent;
-    uint64_t uSectorExtentRel;
-    uint64_t uSectorExtentAbs;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToRead % 512 == 0);
-
-    if (   uOffset + cbToRead > pImage->cbSize
-        || cbToRead == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorExtentRel);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Check access permissions as defined in the extent descriptor. */
-    if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
-    {
-        rc = VERR_VD_VMDK_INVALID_STATE;
-        goto out;
-    }
-
-    /* Clip read range to remain in this extent. */
-    cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-
-    /* Handle the read according to the current extent type. */
-    switch (pExtent->enmType)
-    {
-        case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-        case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
-                               &uSectorExtentAbs);
-            if (RT_FAILURE(rc))
-                goto out;
-            /* Clip read range to at most the rest of the grain. */
-            cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
-            Assert(!(cbToRead % 512));
-            if (uSectorExtentAbs == 0)
-            {
-                if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-                    || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-                    || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
-                    rc = VERR_VD_BLOCK_FREE;
-                else
-                    rc = vmdkStreamReadSequential(pImage, pExtent,
-                                                  uSectorExtentRel,
-                                                  pvBuf, cbToRead);
-            }
-            else
-            {
-                if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-                {
-                    uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
-                    uSectorExtentAbs -= uSectorInGrain;
-                    uint64_t uLBA;
-                    if (pExtent->uGrainSectorAbs != uSectorExtentAbs)
-                    {
-                        rc = vmdkFileInflateSync(pImage, pExtent,
-                                                 VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                                 pExtent->pvGrain,
-                                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
-                                                 NULL, &uLBA, NULL);
-                        if (RT_FAILURE(rc))
-                        {
-                            pExtent->uGrainSectorAbs = 0;
-                            AssertRC(rc);
-                            goto out;
-                        }
-                        pExtent->uGrainSectorAbs = uSectorExtentAbs;
-                        pExtent->uGrain = uSectorExtentRel / pExtent->cSectorsPerGrain;
-                        Assert(uLBA == uSectorExtentRel);
-                    }
-                    memcpy(pvBuf, (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), cbToRead);
-                }
-                else
-                {
-                    rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                          VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                          pvBuf, cbToRead, NULL);
-                }
-            }
-            break;
-        case VMDKETYPE_VMFS:
-        case VMDKETYPE_FLAT:
-            rc = vmdkFileReadSync(pImage, pExtent->pFile,
-                                  VMDK_SECTOR2BYTE(uSectorExtentRel),
-                                  pvBuf, cbToRead, NULL);
-            break;
-        case VMDKETYPE_ZERO:
-            memset(pvBuf, '\0', cbToRead);
-            break;
-    }
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbToRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnWrite */
-static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
-                     size_t cbToWrite, size_t *pcbWriteProcess,
-                     size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKEXTENT pExtent;
-    uint64_t uSectorExtentRel;
-    uint64_t uSectorExtentAbs;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbToWrite % 512 == 0);
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    if (cbToWrite == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* No size check here, will do that later when the extent is located.
-     * There are sparse images out there which according to the spec are
-     * invalid, because the total size is not a multiple of the grain size.
-     * Also for sparse images which are stitched together in odd ways (not at
-     * grain boundaries, and with the nominal size not being a multiple of the
-     * grain size), this would prevent writing to the last grain. */
-
-    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorExtentRel);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Check access permissions as defined in the extent descriptor. */
-    if (   pExtent->enmAccess != VMDKACCESS_READWRITE
-        && (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-            && !pImage->pExtents[0].uAppendPosition
-            && pExtent->enmAccess != VMDKACCESS_READONLY))
-    {
-        rc = VERR_VD_VMDK_INVALID_STATE;
-        goto out;
-    }
-
-    /* Handle the write according to the current extent type. */
-    switch (pExtent->enmType)
-    {
-        case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-        case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
-                               &uSectorExtentAbs);
-            if (RT_FAILURE(rc))
-                goto out;
-            /* Clip write range to at most the rest of the grain. */
-            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
-            if (    pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
-                &&  uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
-            {
-                rc = VERR_VD_VMDK_INVALID_WRITE;
-                goto out;
-            }
-            if (uSectorExtentAbs == 0)
-            {
-                if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-                {
-                    if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
-                    {
-                        /* Full block write to a previously unallocated block.
-                         * Check if the caller wants feedback. */
-                        if (!(fWrite & VD_WRITE_NO_ALLOC))
-                        {
-                            /* Allocate GT and store the grain. */
-                            rc = vmdkAllocGrain(pImage, pExtent,
-                                                uSectorExtentRel,
-                                                pvBuf, cbToWrite);
-                        }
-                        else
-                            rc = VERR_VD_BLOCK_FREE;
-                        *pcbPreRead = 0;
-                        *pcbPostRead = 0;
-                    }
-                    else
-                    {
-                        /* Clip write range to remain in this extent. */
-                        cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-                        *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
-                        *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
-                        rc = VERR_VD_BLOCK_FREE;
-                    }
-                }
-                else
-                {
-                    rc = vmdkStreamAllocGrain(pImage, pExtent,
-                                              uSectorExtentRel,
-                                              pvBuf, cbToWrite);
-                }
-            }
-            else
-            {
-                if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-                {
-                    /* A partial write to a streamOptimized image is simply
-                     * invalid. It requires rewriting already compressed data
-                     * which is somewhere between expensive and impossible. */
-                    rc = VERR_VD_VMDK_INVALID_STATE;
-                    pExtent->uGrainSectorAbs = 0;
-                    AssertRC(rc);
-                }
-                else
-                {
-                    rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                           VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                           pvBuf, cbToWrite, NULL);
-                }
-            }
-            break;
-        case VMDKETYPE_VMFS:
-        case VMDKETYPE_FLAT:
-            /* Clip write range to remain in this extent. */
-            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
-                                   VMDK_SECTOR2BYTE(uSectorExtentRel),
-                                   pvBuf, cbToWrite, NULL);
-            break;
-        case VMDKETYPE_ZERO:
-            /* Clip write range to remain in this extent. */
-            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-            break;
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbToWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnFlush */
-static int vmdkFlush(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc = VINF_SUCCESS;
-
-    AssertPtr(pImage);
-
-    if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-        rc = vmdkFlushImage(pImage);
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
-static unsigned vmdkGetVersion(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        return VMDK_IMAGE_VERSION;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetSize */
-static uint64_t vmdkGetSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        return pImage->cbSize;
-    else
-        return 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
-static uint64_t vmdkGetFileSize(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    uint64_t cb = 0;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        uint64_t cbFile;
-        if (pImage->pFile != NULL)
-        {
-            int rc = vmdkFileGetSize(pImage, pImage->pFile, &cbFile);
-            if (RT_SUCCESS(rc))
-                cb += cbFile;
-        }
-        for (unsigned i = 0; i < pImage->cExtents; i++)
-        {
-            if (pImage->pExtents[i].pFile != NULL)
-            {
-                int rc = vmdkFileGetSize(pImage, pImage->pExtents[i].pFile, &cbFile);
-                if (RT_SUCCESS(rc))
-                    cb += cbFile;
-            }
-        }
-    }
-
-    LogFlowFunc(("returns %lld\n", cb));
-    return cb;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
-static int vmdkGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->PCHSGeometry.cCylinders)
-        {
-            *pPCHSGeometry = pImage->PCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
-static int vmdkSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-        if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        {
-            rc = VERR_NOT_SUPPORTED;
-            goto out;
-        }
-        rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        pImage->PCHSGeometry = *pPCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
-static int vmdkGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
-{
-     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->LCHSGeometry.cCylinders)
-        {
-            *pLCHSGeometry = pImage->LCHSGeometry;
-            rc = VINF_SUCCESS;
-        }
-        else
-            rc = VERR_VD_GEOMETRY_NOT_SET;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
-static int vmdkSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
-{
-    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-        {
-            rc = VERR_VD_IMAGE_READ_ONLY;
-            goto out;
-        }
-        if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-        {
-            rc = VERR_NOT_SUPPORTED;
-            goto out;
-        }
-        rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
-        if (RT_FAILURE(rc))
-            goto out;
-
-        pImage->LCHSGeometry = *pLCHSGeometry;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
-static unsigned vmdkGetImageFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    unsigned uImageFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uImageFlags = pImage->uImageFlags;
-    else
-        uImageFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uImageFlags));
-    return uImageFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
-static unsigned vmdkGetOpenFlags(void *pBackendData)
-{
-    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    unsigned uOpenFlags;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-        uOpenFlags = pImage->uOpenFlags;
-    else
-        uOpenFlags = 0;
-
-    LogFlowFunc(("returns %#x\n", uOpenFlags));
-    return uOpenFlags;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
-static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
-{
-    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    /* Image must be opened and the new flags must be valid. */
-    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* StreamOptimized images need special treatment: reopen is prohibited. */
-    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        if (pImage->uOpenFlags == uOpenFlags)
-            rc = VINF_SUCCESS;
-        else
-            rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* Implement this operation via reopening the image. */
-    vmdkFreeImage(pImage, false);
-    rc = vmdkOpenImage(pImage, uOpenFlags);
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetComment */
-static int vmdkGetComment(void *pBackendData, char *pszComment,
-                          size_t cbComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        const char *pszCommentEncoded = NULL;
-        rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
-                              "ddb.comment", &pszCommentEncoded);
-        if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
-            pszCommentEncoded = NULL;
-        else if (RT_FAILURE(rc))
-            goto out;
-
-        if (pszComment && pszCommentEncoded)
-            rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
-        else
-        {
-            if (pszComment)
-                *pszComment = '\0';
-            rc = VINF_SUCCESS;
-        }
-        if (pszCommentEncoded)
-            RTStrFree((char *)(void *)pszCommentEncoded);
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetComment */
-static int vmdkSetComment(void *pBackendData, const char *pszComment)
-{
-    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-    if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
-    {
-        rc = VERR_NOT_SUPPORTED;
-        goto out;
-    }
-
-    if (pImage)
-        rc = vmdkSetImageComment(pImage, pszComment);
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
-static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ImageUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
-static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    LogFlowFunc(("%RTuuid\n", pUuid));
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-            {
-                pImage->ImageUuid = *pUuid;
-                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                        VMDK_DDB_IMAGE_UUID, pUuid);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
-                rc = VINF_SUCCESS;
-            }
-            else
-                rc = VERR_NOT_SUPPORTED;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
-static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ModificationUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
-static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-            {
-                /* Only touch the modification uuid if it changed. */
-                if (RTUuidCompare(&pImage->ModificationUuid, pUuid))
-                {
-                    pImage->ModificationUuid = *pUuid;
-                    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                            VMDK_DDB_MODIFICATION_UUID, pUuid);
-                    if (RT_FAILURE(rc))
-                        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
-                }
-                rc = VINF_SUCCESS;
-            }
-            else
-                rc = VERR_NOT_SUPPORTED;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
-static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ParentUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
-static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-            {
-                pImage->ParentUuid = *pUuid;
-                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                        VMDK_DDB_PARENT_UUID, pUuid);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
-                rc = VINF_SUCCESS;
-            }
-            else
-                rc = VERR_NOT_SUPPORTED;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
-static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        *pUuid = pImage->ParentModificationUuid;
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
-static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
-{
-    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    int rc;
-
-    AssertPtr(pImage);
-
-    if (pImage)
-    {
-        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-        {
-            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
-            {
-                pImage->ParentModificationUuid = *pUuid;
-                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
-                                        VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
-                if (RT_FAILURE(rc))
-                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
-                rc = VINF_SUCCESS;
-            }
-            else
-                rc = VERR_NOT_SUPPORTED;
-        }
-        else
-            rc = VERR_VD_IMAGE_READ_ONLY;
-    }
-    else
-        rc = VERR_VD_NOT_OPENED;
-
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnDump */
-static void vmdkDump(void *pBackendData)
-{
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-
-    AssertPtr(pImage);
-    if (pImage)
-    {
-        vmdkMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
-                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
-                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
-                    VMDK_BYTE2SECTOR(pImage->cbSize));
-        vmdkMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
-        vmdkMessage(pImage, "Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
-        vmdkMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
-        vmdkMessage(pImage, "Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
-    }
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
-static bool vmdkIsAsyncIOSupported(void *pBackendData)
-{
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-
-    /* We do not support async I/O for stream optimized VMDK images. */
-    return (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) == 0;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
-static int vmdkAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
-                         PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKEXTENT pExtent;
-    uint64_t uSectorExtentRel;
-    uint64_t uSectorExtentAbs;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbRead % 512 == 0);
-
-    if (   uOffset + cbRead > pImage->cbSize
-        || cbRead == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorExtentRel);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Check access permissions as defined in the extent descriptor. */
-    if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
-    {
-        rc = VERR_VD_VMDK_INVALID_STATE;
-        goto out;
-    }
-
-    /* Clip read range to remain in this extent. */
-    cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-
-    /* Handle the read according to the current extent type. */
-    switch (pExtent->enmType)
-    {
-        case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-        case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent,
-                                    uSectorExtentRel, &uSectorExtentAbs);
-            if (RT_FAILURE(rc))
-                goto out;
-            /* Clip read range to at most the rest of the grain. */
-            cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
-            Assert(!(cbRead % 512));
-            if (uSectorExtentAbs == 0)
-                rc = VERR_VD_BLOCK_FREE;
-            else
-            {
-                AssertMsg(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), ("Async I/O is not supported for stream optimized VMDK's\n"));
-                rc = vmdkFileReadUserAsync(pImage, pExtent->pFile,
-                                           VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                           pIoCtx, cbRead);
-            }
-            break;
-        case VMDKETYPE_VMFS:
-        case VMDKETYPE_FLAT:
-            rc = vmdkFileReadUserAsync(pImage, pExtent->pFile,
-                                       VMDK_SECTOR2BYTE(uSectorExtentRel),
-                                       pIoCtx, cbRead);
-            break;
-        case VMDKETYPE_ZERO:
-            size_t cbSet;
-
-            cbSet = vmdkFileIoCtxSet(pImage, pIoCtx, 0, cbRead);
-            Assert(cbSet == cbRead);
-
-            rc = VINF_SUCCESS;
-            break;
-    }
-    if (pcbActuallyRead)
-        *pcbActuallyRead = cbRead;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
-static int vmdkAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
-                          PVDIOCTX pIoCtx,
-                          size_t *pcbWriteProcess, size_t *pcbPreRead,
-                          size_t *pcbPostRead, unsigned fWrite)
-{
-    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
-                 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKEXTENT pExtent;
-    uint64_t uSectorExtentRel;
-    uint64_t uSectorExtentAbs;
-    int rc;
-
-    AssertPtr(pImage);
-    Assert(uOffset % 512 == 0);
-    Assert(cbWrite % 512 == 0);
-
-    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
-    {
-        rc = VERR_VD_IMAGE_READ_ONLY;
-        goto out;
-    }
-
-    if (cbWrite == 0)
-    {
-        rc = VERR_INVALID_PARAMETER;
-        goto out;
-    }
-
-    /* No size check here, will do that later when the extent is located.
-     * There are sparse images out there which according to the spec are
-     * invalid, because the total size is not a multiple of the grain size.
-     * Also for sparse images which are stitched together in odd ways (not at
-     * grain boundaries, and with the nominal size not being a multiple of the
-     * grain size), this would prevent writing to the last grain. */
-
-    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
-                        &pExtent, &uSectorExtentRel);
-    if (RT_FAILURE(rc))
-        goto out;
-
-    /* Check access permissions as defined in the extent descriptor. */
-    if (pExtent->enmAccess != VMDKACCESS_READWRITE)
-    {
-        rc = VERR_VD_VMDK_INVALID_STATE;
-        goto out;
-    }
-
-    /* Handle the write according to the current extent type. */
-    switch (pExtent->enmType)
-    {
-        case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-        case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent, uSectorExtentRel,
-                                    &uSectorExtentAbs);
-            if (RT_FAILURE(rc))
-                goto out;
-            /* Clip write range to at most the rest of the grain. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
-            if (    pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
-                &&  uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
-            {
-                rc = VERR_VD_VMDK_INVALID_WRITE;
-                goto out;
-            }
-            if (uSectorExtentAbs == 0)
-            {
-                if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
-                {
-                    /* Full block write to a previously unallocated block.
-                     * Check if the caller wants to avoid the automatic alloc. */
-                    if (!(fWrite & VD_WRITE_NO_ALLOC))
-                    {
-                        /* Allocate GT and find out where to store the grain. */
-                        rc = vmdkAllocGrainAsync(pImage, pExtent, pIoCtx,
-                                                 uSectorExtentRel, cbWrite);
-                    }
-                    else
-                        rc = VERR_VD_BLOCK_FREE;
-                    *pcbPreRead = 0;
-                    *pcbPostRead = 0;
-                }
-                else
-                {
-                    /* Clip write range to remain in this extent. */
-                    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-                    *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
-                    *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
-                    rc = VERR_VD_BLOCK_FREE;
-                }
-            }
-            else
-            {
-                Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
-                rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
-                                            VMDK_SECTOR2BYTE(uSectorExtentAbs),
-                                            pIoCtx, cbWrite, NULL, NULL);
-            }
-            break;
-        case VMDKETYPE_VMFS:
-        case VMDKETYPE_FLAT:
-            /* Clip write range to remain in this extent. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-            rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
-                                        VMDK_SECTOR2BYTE(uSectorExtentRel),
-                                        pIoCtx, cbWrite, NULL, NULL);
-            break;
-        case VMDKETYPE_ZERO:
-            /* Clip write range to remain in this extent. */
-            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
-            break;
-    }
-
-    if (pcbWriteProcess)
-        *pcbWriteProcess = cbWrite;
-
-out:
-    LogFlowFunc(("returns %Rrc\n", rc));
-    return rc;
-}
-
-/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
-static int vmdkAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
-{
-    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
-    PVMDKEXTENT pExtent;
-    int rc = VINF_SUCCESS;
-
-    for (unsigned i = 0; i < pImage->cExtents; i++)
-    {
-        pExtent = &pImage->pExtents[i];
-        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
-        {
-            switch (pExtent->enmType)
-            {
-                case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-                case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-                    rc = vmdkWriteMetaSparseExtentAsync(pImage, pExtent, 0, pIoCtx);
-                    if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-                        goto out;
-                    if (pExtent->fFooter)
-                    {
-                        uint64_t uFileOffset = pExtent->uAppendPosition;
-                        if (!uFileOffset)
-                        {
-                            rc = VERR_INTERNAL_ERROR;
-                            goto out;
-                        }
-                        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
-                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
-                        if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-                            goto out;
-                    }
-                    break;
-                case VMDKETYPE_VMFS:
-                case VMDKETYPE_FLAT:
-                    /* Nothing to do. */
-                    break;
-                case VMDKETYPE_ZERO:
-                default:
-                    AssertMsgFailed(("extent with type %d marked as dirty\n",
-                                     pExtent->enmType));
-                    break;
-            }
-        }
-        switch (pExtent->enmType)
-        {
-            case VMDKETYPE_HOSTED_SPARSE:
-#ifdef VBOX_WITH_VMDK_ESX
-            case VMDKETYPE_ESX_SPARSE:
-#endif /* VBOX_WITH_VMDK_ESX */
-            case VMDKETYPE_VMFS:
-            case VMDKETYPE_FLAT:
-                /*
-                 * Don't ignore block devices like in the sync case
-                 * (they have an absolute path).
-                 * We might have unwritten data in the writeback cache and
-                 * the async I/O manager will handle these requests properly
-                 * even if the block device doesn't support these requests.
-                 */
-                if (   pExtent->pFile != NULL
-                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
-                    rc = vmdkFileFlushAsync(pImage, pExtent->pFile, pIoCtx);
-                break;
-            case VMDKETYPE_ZERO:
-                /* No need to do anything for this extent. */
-                break;
-            default:
-                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
-                break;
-        }
-    }
-
-out:
-    return rc;
-}
-
-
-VBOXHDDBACKEND g_VmdkBackend =
-{
-    /* pszBackendName */
-    "VMDK",
-    /* cbSize */
-    sizeof(VBOXHDDBACKEND),
-    /* uBackendCaps */
-      VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
-    | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC
-    | VD_CAP_VFS,
-    /* paFileExtensions */
-    s_aVmdkFileExtensions,
-    /* paConfigInfo */
-    NULL,
-    /* hPlugin */
-    NIL_RTLDRMOD,
-    /* pfnCheckIfValid */
-    vmdkCheckIfValid,
-    /* pfnOpen */
-    vmdkOpen,
-    /* pfnCreate */
-    vmdkCreate,
-    /* pfnRename */
-    vmdkRename,
-    /* pfnClose */
-    vmdkClose,
-    /* pfnRead */
-    vmdkRead,
-    /* pfnWrite */
-    vmdkWrite,
-    /* pfnFlush */
-    vmdkFlush,
-    /* pfnGetVersion */
-    vmdkGetVersion,
-    /* pfnGetSize */
-    vmdkGetSize,
-    /* pfnGetFileSize */
-    vmdkGetFileSize,
-    /* pfnGetPCHSGeometry */
-    vmdkGetPCHSGeometry,
-    /* pfnSetPCHSGeometry */
-    vmdkSetPCHSGeometry,
-    /* pfnGetLCHSGeometry */
-    vmdkGetLCHSGeometry,
-    /* pfnSetLCHSGeometry */
-    vmdkSetLCHSGeometry,
-    /* pfnGetImageFlags */
-    vmdkGetImageFlags,
-    /* pfnGetOpenFlags */
-    vmdkGetOpenFlags,
-    /* pfnSetOpenFlags */
-    vmdkSetOpenFlags,
-    /* pfnGetComment */
-    vmdkGetComment,
-    /* pfnSetComment */
-    vmdkSetComment,
-    /* pfnGetUuid */
-    vmdkGetUuid,
-    /* pfnSetUuid */
-    vmdkSetUuid,
-    /* pfnGetModificationUuid */
-    vmdkGetModificationUuid,
-    /* pfnSetModificationUuid */
-    vmdkSetModificationUuid,
-    /* pfnGetParentUuid */
-    vmdkGetParentUuid,
-    /* pfnSetParentUuid */
-    vmdkSetParentUuid,
-    /* pfnGetParentModificationUuid */
-    vmdkGetParentModificationUuid,
-    /* pfnSetParentModificationUuid */
-    vmdkSetParentModificationUuid,
-    /* pfnDump */
-    vmdkDump,
-    /* pfnGetTimeStamp */
-    NULL,
-    /* pfnGetParentTimeStamp */
-    NULL,
-    /* pfnSetParentTimeStamp */
-    NULL,
-    /* pfnGetParentFilename */
-    NULL,
-    /* pfnSetParentFilename */
-    NULL,
-    /* pfnIsAsyncIOSupported */
-    vmdkIsAsyncIOSupported,
-    /* pfnAsyncRead */
-    vmdkAsyncRead,
-    /* pfnAsyncWrite */
-    vmdkAsyncWrite,
-    /* pfnAsyncFlush */
-    vmdkAsyncFlush,
-    /* pfnComposeLocation */
-    genericFileComposeLocation,
-    /* pfnComposeName */
-    genericFileComposeName,
-    /* pfnCompact */
-    NULL,
-    /* pfnResize */
-    NULL
-};
Index: /trunk/src/VBox/Devices/VBoxDDUDeps.cpp
===================================================================
--- /trunk/src/VBox/Devices/VBoxDDUDeps.cpp	(revision 33566)
+++ /trunk/src/VBox/Devices/VBoxDDUDeps.cpp	(revision 33567)
@@ -20,4 +20,5 @@
 *******************************************************************************/
 #include <VBox/types.h>
+#include <VBox/vd.h>
 #ifdef VBOX_WITH_USB
 # include <VBox/usblib.h>
@@ -34,4 +35,5 @@
 PFNRT g_apfnVBoxDDUDeps[] =
 {
+    (PFNRT)VDInit,
 #ifdef VBOX_WITH_USB
     (PFNRT)USBFilterInit,
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp	(revision 33566)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp	(revision 33567)
@@ -33,5 +33,5 @@
 #include <VBox/com/VirtualBox.h>
 
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 #include <VBox/sup.h>
 #include <VBox/err.h>
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 33566)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 33567)
@@ -36,5 +36,5 @@
 #include <iprt/getopt.h>
 #include <VBox/log.h>
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 #include "VBoxManage.h"
Index: /trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp	(revision 33566)
+++ /trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp	(revision 33567)
@@ -19,5 +19,5 @@
 
 #include "VBoxGlobal.h"
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 #include <VBox/version.h>
 
Index: /trunk/src/VBox/Main/ApplianceImplIO.cpp
===================================================================
--- /trunk/src/VBox/Main/ApplianceImplIO.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/ApplianceImplIO.cpp	(revision 33567)
@@ -31,5 +31,5 @@
 #include <iprt/stream.h>
 #include <iprt/circbuf.h>
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 /******************************************************************************
Index: /trunk/src/VBox/Main/ApplianceImplImport.cpp
===================================================================
--- /trunk/src/VBox/Main/ApplianceImplImport.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/ApplianceImplImport.cpp	(revision 33567)
@@ -26,5 +26,5 @@
 #include <iprt/stream.h>
 
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 #include <VBox/com/array.h>
 
Index: /trunk/src/VBox/Main/MediumFormatImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/MediumFormatImpl.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/MediumFormatImpl.cpp	(revision 33567)
@@ -21,5 +21,5 @@
 #include "Logging.h"
 
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 #include <iprt/cpp/utils.h>
Index: /trunk/src/VBox/Main/MediumImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/MediumImpl.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/MediumImpl.cpp	(revision 33567)
@@ -37,5 +37,5 @@
 #include <iprt/cpp/utils.h>
 
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 #include <algorithm>
Index: /trunk/src/VBox/Main/SystemPropertiesImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/SystemPropertiesImpl.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/SystemPropertiesImpl.cpp	(revision 33567)
@@ -34,5 +34,5 @@
 #include <VBox/param.h>
 #include <VBox/settings.h>
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 // defines
Index: /trunk/src/VBox/Main/VirtualBoxImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/VirtualBoxImpl.cpp	(revision 33566)
+++ /trunk/src/VBox/Main/VirtualBoxImpl.cpp	(revision 33567)
@@ -37,5 +37,4 @@
 #include <VBox/err.h>
 #include <VBox/param.h>
-#include <VBox/VBoxHDD.h>
 #include <VBox/settings.h>
 #include <VBox/version.h>
Index: /trunk/src/VBox/Main/include/MediumImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MediumImpl.h	(revision 33566)
+++ /trunk/src/VBox/Main/include/MediumImpl.h	(revision 33567)
@@ -21,5 +21,5 @@
 #define ____H_MEDIUMIMPL
 
-#include <VBox/VBoxHDD.h>
+#include <VBox/vd.h>
 
 #include "VirtualBoxBase.h"
Index: /trunk/src/VBox/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Makefile.kmk	(revision 33566)
+++ /trunk/src/VBox/Makefile.kmk	(revision 33567)
@@ -55,4 +55,5 @@
  include $(PATH_SUB_CURRENT)/Runtime/Makefile.kmk
  include $(PATH_SUB_CURRENT)/Disassembler/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/Storage/Makefile.kmk
  if1of ($(KBUILD_TARGET_ARCH), amd64 x86)
   include $(PATH_SUB_CURRENT)/VMM/Makefile.kmk
Index: /trunk/src/VBox/Storage/DMG.cpp
===================================================================
--- /trunk/src/VBox/Storage/DMG.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/DMG.cpp	(revision 33567)
@@ -0,0 +1,2445 @@
+/* $Id$ */
+/** @file
+ * VBoxDMG - Interpreter for Apple Disk Images (DMG).
+ */
+
+/*
+ * Copyright (C) 2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
+#include <VBox/vd-plugin.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/ctype.h>
+#include <iprt/string.h>
+#include <iprt/base64.h>
+#include <iprt/zip.h>
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+
+/** Sector size, multiply with all sector counts to get number of bytes. */
+#define DMG_SECTOR_SIZE 512
+
+/** Convert block number/size to byte offset/size. */
+#define DMG_BLOCK2BYTE(u)          ((uint64_t)(u) << 9)
+
+/** Convert byte offset/size to block number/size. */
+#define DMG_BYTE2BLOCK(u)          ((u) >> 9)
+
+/**
+ * UDIF checksum structure.
+ */
+typedef struct DMGUDIFCKSUM
+{
+    uint32_t        u32Kind;                    /**< The kind of checksum.  */
+    uint32_t        cBits;                      /**< The size of the checksum. */
+    union
+    {
+        uint8_t     au8[128];                   /**< 8-bit view. */
+        uint32_t    au32[32];                   /**< 32-bit view. */
+    }               uSum;                       /**< The checksum. */
+} DMGUDIFCKSUM;
+AssertCompileSize(DMGUDIFCKSUM, 8 + 128);
+typedef DMGUDIFCKSUM *PDMGUDIFCKSUM;
+typedef const DMGUDIFCKSUM *PCDMGUDIFCKSUM;
+
+/** @name Checksum Kind (DMGUDIFCKSUM::u32Kind)
+ * @{ */
+/** No checksum. */
+#define DMGUDIFCKSUM_NONE          UINT32_C(0)
+/** CRC-32. */
+#define DMGUDIFCKSUM_CRC32         UINT32_C(2)
+/** @} */
+
+/**
+ * UDIF ID.
+ * This is kind of like a UUID only it isn't, but we'll use the UUID
+ * representation of it for simplicity.
+ */
+typedef RTUUID DMGUDIFID;
+AssertCompileSize(DMGUDIFID, 16);
+typedef DMGUDIFID *PDMGUDIFID;
+typedef const DMGUDIFID *PCDMGUDIFID;
+
+/**
+ * UDIF footer used by Apple Disk Images (DMG).
+ *
+ * This is a footer placed 512 bytes from the end of the file. Typically a DMG
+ * file starts with the data, which is followed by the block table and then ends
+ * with this structure.
+ *
+ * All fields are stored in big endian format.
+ */
+#pragma pack(1)
+typedef struct DMGUDIF
+{
+    uint32_t            u32Magic;               /**< 0x000 - Magic, 'koly' (DMGUDIF_MAGIC).                       (fUDIFSignature) */
+    uint32_t            u32Version;             /**< 0x004 - The UDIF version (DMGUDIF_VER_CURRENT).              (fUDIFVersion) */
+    uint32_t            cbFooter;               /**< 0x008 - The size of the this structure (512).                 (fUDIFHeaderSize) */
+    uint32_t            fFlags;                 /**< 0x00c - Flags.                                                (fUDIFFlags) */
+    uint64_t            offRunData;             /**< 0x010 - Where the running data fork starts (usually 0).       (fUDIFRunningDataForkOffset) */
+    uint64_t            offData;                /**< 0x018 - Where the data fork starts (usually 0).               (fUDIFDataForkOffset) */
+    uint64_t            cbData;                 /**< 0x020 - Size of the data fork (in bytes).                     (fUDIFDataForkLength) */
+    uint64_t            offRsrc;                /**< 0x028 - Where the resource fork starts (usually cbData or 0). (fUDIFRsrcForkOffset) */
+    uint64_t            cbRsrc;                 /**< 0x030 - The size of the resource fork.                        (fUDIFRsrcForkLength)*/
+    uint32_t            iSegment;               /**< 0x038 - The segment number of this file.                      (fUDIFSegmentNumber) */
+    uint32_t            cSegments;              /**< 0x03c - The number of segments.                               (fUDIFSegmentCount) */
+    DMGUDIFID           SegmentId;              /**< 0x040 - The segment ID.                                       (fUDIFSegmentID) */
+    DMGUDIFCKSUM        DataCkSum;              /**< 0x050 - The data checksum.                                    (fUDIFDataForkChecksum) */
+    uint64_t            offXml;                 /**< 0x0d8 - The XML offset (.plist kind of data).                 (fUDIFXMLOffset) */
+    uint64_t            cbXml;                  /**< 0x0e0 - The size of the XML.                                  (fUDIFXMLSize) */
+    uint8_t             abUnknown[120];         /**< 0x0e8 - Unknown stuff, hdiutil doesn't dump it... */
+    DMGUDIFCKSUM        MasterCkSum;            /**< 0x160 - The master checksum.                                  (fUDIFMasterChecksum) */
+    uint32_t            u32Type;                /**< 0x1e8 - The image type.                                       (fUDIFImageVariant) */
+    uint64_t            cSectors;               /**< 0x1ec - The sector count. Warning! Unaligned!                 (fUDISectorCount) */
+    uint32_t            au32Unknown[3];         /**< 0x1f4 - Unknown stuff, hdiutil doesn't dump it... */
+} DMGUDIF;
+#pragma pack()
+AssertCompileSize(DMGUDIF, 512);
+AssertCompileMemberOffset(DMGUDIF, cbRsrc,   0x030);
+AssertCompileMemberOffset(DMGUDIF, cbXml,    0x0e0);
+AssertCompileMemberOffset(DMGUDIF, cSectors, 0x1ec);
+
+typedef DMGUDIF *PDMGUDIF;
+typedef const DMGUDIF *PCDMGUDIF;
+
+/** The UDIF magic 'koly' (DMGUDIF::u32Magic). */
+#define DMGUDIF_MAGIC              UINT32_C(0x6b6f6c79)
+
+/** The current UDIF version (DMGUDIF::u32Version).
+ * This is currently the only we recognizes and will create. */
+#define DMGUDIF_VER_CURRENT        4
+
+/** @name UDIF flags (DMGUDIF::fFlags).
+ * @{ */
+/** Flatten image whatever that means.
+ * (hdiutil -debug calls it kUDIFFlagsFlattened.) */
+#define DMGUDIF_FLAGS_FLATTENED    RT_BIT_32(0)
+/** Internet enabled image.
+ * (hdiutil -debug calls it kUDIFFlagsInternetEnabled) */
+#define DMGUDIF_FLAGS_INET_ENABLED RT_BIT_32(2)
+/** Mask of known bits. */
+#define DMGUDIF_FLAGS_KNOWN_MASK   (RT_BIT_32(0) | RT_BIT_32(2))
+/** @} */
+
+/** @name UDIF Image Types (DMGUDIF::u32Type).
+ * @{ */
+/** Device image type. (kUDIFDeviceImageType) */
+#define DMGUDIF_TYPE_DEVICE        1
+/** Device image type. (kUDIFPartitionImageType) */
+#define DMGUDIF_TYPE_PARTITION     2
+/** @}  */
+
+/**
+ * BLKX data.
+ *
+ * This contains the start offset and size of raw data stored in the image.
+ *
+ * All fields are stored in big endian format.
+ */
+#pragma pack(1)
+typedef struct DMGBLKX
+{
+    uint32_t            u32Magic;               /**< 0x000 - Magic, 'mish' (DMGBLKX_MAGIC). */
+    uint32_t            u32Version;             /**< 0x004 - The BLKX version (DMGBLKX_VER_CURRENT). */
+    uint64_t            cSectornumberFirst;     /**< 0x008 - The first sector number the block represents in the virtual device. */
+    uint64_t            cSectors;               /**< 0x010 - Number of sectors this block represents. */
+    uint64_t            offDataStart;           /**< 0x018 - Start offset for raw data. */
+    uint32_t            cSectorsDecompress;     /**< 0x020 - Size of the buffer in sectors needed to decompress. */
+    uint32_t            u32BlocksDescriptor;    /**< 0x024 - Blocks descriptor. */
+    uint8_t             abReserved[24];
+    DMGUDIFCKSUM        BlkxCkSum;              /**< 0x03c - Checksum for the BLKX table. */
+    uint32_t            cBlocksRunCount;        /**< 0x    - Number of entries in the blkx run table afterwards. */
+} DMGBLKX;
+#pragma pack()
+AssertCompileSize(DMGBLKX, 204);
+
+typedef DMGBLKX *PDMGBLKX;
+typedef const DMGBLKX *PCDMGBLKX;
+
+/** The BLKX magic 'mish' (DMGBLKX::u32Magic). */
+#define DMGBLKX_MAGIC              UINT32_C(0x6d697368)
+/** BLKX version (DMGBLKX::u32Version). */
+#define DMGBLKX_VERSION            UINT32_C(0x00000001)
+
+/** Blocks descriptor type: entire device. */
+#define DMGBLKX_DESC_ENTIRE_DEVICE UINT32_C(0xfffffffe)
+
+/**
+ * BLKX table descriptor.
+ *
+ * All fields are stored in big endian format.
+ */
+#pragma pack(1)
+typedef struct DMGBLKXDESC
+{
+    uint32_t            u32Type;                /**< 0x000 - Type of the descriptor. */
+    uint32_t            u32Reserved;            /**< 0x004 - Reserved, but contains +beg or +end in case thisi is a comment descriptor. */
+    uint64_t            u64SectorStart;         /**< 0x008 - First sector number in the block this entry describes. */
+    uint64_t            u64SectorCount;         /**< 0x010 - Number of sectors this entry describes. */
+    uint64_t            offData;                /**< 0x018 - Offset in the image where the data starts. */
+    uint64_t            cbData;                 /**< 0x020 - Number of bytes in the image. */
+} DMGBLKXDESC;
+#pragma pack()
+AssertCompileSize(DMGBLKXDESC, 40);
+
+typedef DMGBLKXDESC *PDMGBLKXDESC;
+typedef const DMGBLKXDESC *PCDMGBLKXDESC;
+
+/** Raw image data type. */
+#define DMGBLKXDESC_TYPE_RAW        1
+/** Ignore type. */
+#define DMGBLKXDESC_TYPE_IGNORE     2
+/** Compressed with zlib type. */
+#define DMGBLKXDESC_TYPE_ZLIB       UINT32_C(0x80000005)
+/** Comment type. */
+#define DMGBLKXDESC_TYPE_COMMENT    UINT32_C(0x7ffffffe)
+/** Terminator type. */
+#define DMGBLKXDESC_TYPE_TERMINATOR UINT32_C(0xffffffff)
+
+/**
+ * UDIF Resource Entry.
+ */
+typedef struct DMGUDIFRSRCENTRY
+{
+    /** The ID. */
+    int32_t             iId;
+    /** Attributes. */
+    uint32_t            fAttributes;
+    /** The name. */
+    char               *pszName;
+    /** The CoreFoundation name. Can be NULL. */
+    char               *pszCFName;
+    /** The size of the data. */
+    size_t              cbData;
+    /** The raw data. */
+    uint8_t            *pbData;
+} DMGUDIFRSRCENTRY;
+/** Pointer to an UDIF resource entry. */
+typedef DMGUDIFRSRCENTRY *PDMGUDIFRSRCENTRY;
+/** Pointer to a const UDIF resource entry. */
+typedef DMGUDIFRSRCENTRY const *PCDMGUDIFRSRCENTRY;
+
+/**
+ * UDIF Resource Array.
+ */
+typedef struct DMGUDIFRSRCARRAY
+{
+    /** The array name. */
+    char                szName[12];
+    /** The number of occupied entries. */
+    uint32_t            cEntries;
+    /** The array entries.
+     * A lazy bird ASSUME there are no more than 4 entries in any DMG. Increase the
+     * size if DMGs with more are found.
+     * r=aeichner: Saw one with 6 here (image of a whole DVD) */
+    DMGUDIFRSRCENTRY    aEntries[10];
+} DMGUDIFRSRCARRAY;
+/** Pointer to a UDIF resource array. */
+typedef DMGUDIFRSRCARRAY *PDMGUDIFRSRCARRAY;
+/** Pointer to a const UDIF resource array. */
+typedef DMGUDIFRSRCARRAY const *PCDMGUDIFRSRCARRAY;
+
+/**
+ * DMG extent types.
+ */
+typedef enum DMGEXTENTTYPE
+{
+    /** Null, never used. */
+    DMGEXTENTTYPE_NULL = 0,
+    /** Raw image data. */
+    DMGEXTENTTYPE_RAW,
+    /** Zero extent, reads return 0 and writes have no effect. */
+    DMGEXTENTTYPE_ZERO,
+    /** Compressed extent - compression method ZLIB. */
+    DMGEXTENTTYPE_COMP_ZLIB,
+    /** 32bit hack. */
+    DMGEXTENTTYPE_32BIT_HACK = 0x7fffffff
+} DMGEXTENTTYPE, *PDMGEXTENTTYPE;
+
+/**
+ * DMG extent mapping a virtual image block to real file offsets.
+ */
+typedef struct DMGEXTENT
+{
+    /** Extent type. */
+    DMGEXTENTTYPE        enmType;
+    /** First sector this extent describes. */
+    uint64_t             uSectorExtent;
+    /** Number of sectors this extent describes. */
+    uint64_t             cSectorsExtent;
+    /** Start offset in the real file. */
+    uint64_t             offFileStart;
+    /** Number of bytes for the extent data in the file. */
+    uint64_t             cbFile;
+} DMGEXTENT;
+/** Pointer to an DMG extent. */
+typedef DMGEXTENT *PDMGEXTENT;
+
+/**
+ * VirtualBox Apple Disk Image (DMG) interpreter instance data.
+ */
+typedef struct DMGIMAGE
+{
+    /** Image name.
+     * Kept around for logging and delete-on-close purposes. */
+    const char         *pszFilename;
+    /** Storage handle. */
+    PVDIOSTORAGE        pStorage;
+    /** I/O interface. */
+    PVDINTERFACE        pInterfaceIO;
+    /** Async I/O interface callbacks. */
+    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE        pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE        pVDIfsImage;
+
+    /** Error callback. */
+    PVDINTERFACE        pInterfaceError;
+    /** Opaque data for error callback. */
+    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
+
+    /** Flags the image was opened with. */
+    uint32_t            uOpenFlags;
+    /** Image flags. */
+    unsigned            uImageFlags;
+    /** Total size of the virtual image. */
+    uint64_t            cbSize;
+    /** Size of the image. */
+    uint64_t            cbFile;
+    /** Physical geometry of this image. */
+    VDGEOMETRY          PCHSGeometry;
+    /** Logical geometry of this image. */
+    VDGEOMETRY          LCHSGeometry;
+
+    /** The resources.
+     * A lazy bird ASSUME there are only two arrays in the resource-fork section in
+     * the XML, namely 'blkx' and 'plst'. These have been assigned fixed indexes. */
+    DMGUDIFRSRCARRAY    aRsrcs[2];
+    /** The UDIF footer. */
+    DMGUDIF             Ftr;
+
+    /** Number of valid extents in the array. */
+    unsigned            cExtents;
+    /** Number of entries the array can hold. */
+    unsigned            cExtentsMax;
+    /** Pointer to the extent array. */
+    PDMGEXTENT          paExtents;
+    /** Index of the last accessed extent. */
+    unsigned            idxExtentLast;
+
+    /** Extent which owns the data in the buffer. */
+    PDMGEXTENT          pExtentDecomp;
+    /** Buffer holding the decompressed data for a extent. */
+    void               *pvDecompExtent;
+    /** Size of the buffer. */
+    size_t              cbDecompExtent;
+} DMGIMAGE;
+/** Pointer to an instance of the DMG Image Interpreter. */
+typedef DMGIMAGE *PDMGIMAGE;
+
+/** @name Resources indexes (into DMG::aRsrcs).
+ * @{ */
+#define DMG_RSRC_IDX_BLKX   0
+#define DMG_RSRC_IDX_PLST   1
+/** @} */
+
+/** State for the input callout of the inflate reader. */
+typedef struct DMGINFLATESTATE
+{
+    /* Image this operation relates to. */
+    PDMGIMAGE pImage;
+    /* Total size of the data to read. */
+    size_t    cbSize;
+    /* Offset in the file to read. */
+    uint64_t  uFileOffset;
+    /* Current read position. */
+    ssize_t   iOffset;
+} DMGINFLATESTATE;
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+/** @def DMG_PRINTF
+ * Wrapper for LogRel.
+ */
+#define DMG_PRINTF(a)  LogRel(a)
+
+/** @def DMG_VALIDATE
+ * For validating a struct thing and log/print what's wrong.
+ */
+# define DMG_VALIDATE(expr, logstuff) \
+    do { \
+        if (!(expr)) \
+        { \
+            LogRel(("DMG: validation failed: %s\nDMG: ", #expr)); \
+            LogRel(logstuff); \
+            fRc = false; \
+        } \
+    } while (0)
+
+/** VBoxDMG: Unable to parse the XML. */
+#define VERR_VD_DMG_XML_PARSE_ERROR         (-3280)
+
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aDmgFileExtensions[] =
+{
+    {"dmg", VDTYPE_DVD},
+    {NULL, VDTYPE_INVALID}
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif);
+static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif);
+
+static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId);
+static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId);
+
+static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum);
+static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum);
+static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix);
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) dmgError(PDMGIMAGE pThis, int rc, RT_SRC_POS_DECL,
+                         const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pThis->pInterfaceError)
+        pThis->pInterfaceErrorCallbacks->pfnError(pThis->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                  pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) dmgMessage(PDMGIMAGE pThis, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pThis->pInterfaceError)
+        rc = pThis->pInterfaceErrorCallbacks->pfnMessage(pThis->pInterfaceError->pvUser,
+                                                         pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) dmgFileOpen(PDMGIMAGE pThis, const char *pszFilename,
+                            uint32_t fOpen)
+{
+    return pThis->pInterfaceIOCallbacks->pfnOpen(pThis->pInterfaceIO->pvUser,
+                                                 pszFilename, fOpen,
+                                                 &pThis->pStorage);
+}
+
+DECLINLINE(int) dmgFileClose(PDMGIMAGE pThis)
+{
+    return pThis->pInterfaceIOCallbacks->pfnClose(pThis->pInterfaceIO->pvUser,
+                                                  pThis->pStorage);
+}
+
+DECLINLINE(int) dmgFileDelete(PDMGIMAGE pThis, const char *pszFilename)
+{
+    return pThis->pInterfaceIOCallbacks->pfnDelete(pThis->pInterfaceIO->pvUser,
+                                                   pszFilename);
+}
+
+DECLINLINE(int) dmgFileMove(PDMGIMAGE pThis, const char *pszSrc,
+                            const char *pszDst, unsigned fMove)
+{
+    return pThis->pInterfaceIOCallbacks->pfnMove(pThis->pInterfaceIO->pvUser,
+                                                 pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) dmgFileGetFreeSpace(PDMGIMAGE pThis, const char *pszFilename,
+                                    int64_t *pcbFree)
+{
+    return pThis->pInterfaceIOCallbacks->pfnGetFreeSpace(pThis->pInterfaceIO->pvUser,
+                                                         pszFilename, pcbFree);
+}
+
+DECLINLINE(int) dmgFileGetSize(PDMGIMAGE pThis, uint64_t *pcbSize)
+{
+    return pThis->pInterfaceIOCallbacks->pfnGetSize(pThis->pInterfaceIO->pvUser,
+                                                    pThis->pStorage, pcbSize);
+}
+
+DECLINLINE(int) dmgFileSetSize(PDMGIMAGE pThis, uint64_t cbSize)
+{
+    return pThis->pInterfaceIOCallbacks->pfnSetSize(pThis->pInterfaceIO->pvUser,
+                                                    pThis->pStorage, cbSize);
+}
+
+DECLINLINE(int) dmgFileWriteSync(PDMGIMAGE pThis, uint64_t uOffset,
+                                 const void *pvBuffer, size_t cbBuffer,
+                                 size_t *pcbWritten)
+{
+    return pThis->pInterfaceIOCallbacks->pfnWriteSync(pThis->pInterfaceIO->pvUser,
+                                                      pThis->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, pcbWritten);
+}
+
+DECLINLINE(int) dmgFileReadSync(PDMGIMAGE pThis, uint64_t uOffset,
+                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    return pThis->pInterfaceIOCallbacks->pfnReadSync(pThis->pInterfaceIO->pvUser,
+                                                     pThis->pStorage, uOffset,
+                                                     pvBuffer, cbBuffer, pcbRead);
+}
+
+DECLINLINE(int) dmgFileFlushSync(PDMGIMAGE pThis)
+{
+    return pThis->pInterfaceIOCallbacks->pfnFlushSync(pThis->pInterfaceIO->pvUser,
+                                                      pThis->pStorage);
+}
+
+static DECLCALLBACK(int) dmgFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
+{
+    DMGINFLATESTATE *pInflateState = (DMGINFLATESTATE *)pvUser;
+
+    Assert(cbBuf);
+    if (pInflateState->iOffset < 0)
+    {
+        *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
+        if (pcbBuf)
+            *pcbBuf = 1;
+        pInflateState->iOffset = 0;
+        return VINF_SUCCESS;
+    }
+    cbBuf = RT_MIN(cbBuf, pInflateState->cbSize);
+    int rc = dmgFileReadSync(pInflateState->pImage, pInflateState->uFileOffset,
+                             pvBuf, cbBuf, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    pInflateState->uFileOffset += cbBuf;
+    pInflateState->iOffset += cbBuf;
+    pInflateState->cbSize -= cbBuf;
+    Assert(pcbBuf);
+    *pcbBuf = cbBuf;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal: read from a file and inflate the compressed data,
+ * distinguishing between async and normal operation
+ */
+DECLINLINE(int) dmgFileInflateSync(PDMGIMAGE pImage, uint64_t uOffset, size_t cbToRead,
+                                   void *pvBuf, size_t cbBuf)
+{
+    int rc;
+    PRTZIPDECOMP pZip = NULL;
+    DMGINFLATESTATE InflateState;
+    size_t cbActuallyRead;
+
+    InflateState.pImage      = pImage;
+    InflateState.cbSize      = cbToRead;
+    InflateState.uFileOffset = uOffset;
+    InflateState.iOffset     = -1;
+
+    rc = RTZipDecompCreate(&pZip, &InflateState, dmgFileInflateHelper);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = RTZipDecompress(pZip, pvBuf, cbBuf, &cbActuallyRead);
+    RTZipDecompDestroy(pZip);
+    if (RT_FAILURE(rc))
+        return rc;
+    if (cbActuallyRead != cbBuf)
+        rc = VERR_VD_VMDK_INVALID_FORMAT;
+    return rc;
+}
+
+/**
+ * Swaps endian.
+ * @param   pUdif       The structure.
+ */
+static void dmgSwapEndianUdif(PDMGUDIF pUdif)
+{
+#ifndef RT_BIG_ENDIAN
+    pUdif->u32Magic   = RT_BSWAP_U32(pUdif->u32Magic);
+    pUdif->u32Version = RT_BSWAP_U32(pUdif->u32Version);
+    pUdif->cbFooter   = RT_BSWAP_U32(pUdif->cbFooter);
+    pUdif->fFlags     = RT_BSWAP_U32(pUdif->fFlags);
+    pUdif->offRunData = RT_BSWAP_U64(pUdif->offRunData);
+    pUdif->offData    = RT_BSWAP_U64(pUdif->offData);
+    pUdif->cbData     = RT_BSWAP_U64(pUdif->cbData);
+    pUdif->offRsrc    = RT_BSWAP_U64(pUdif->offRsrc);
+    pUdif->cbRsrc     = RT_BSWAP_U64(pUdif->cbRsrc);
+    pUdif->iSegment   = RT_BSWAP_U32(pUdif->iSegment);
+    pUdif->cSegments  = RT_BSWAP_U32(pUdif->cSegments);
+    pUdif->offXml     = RT_BSWAP_U64(pUdif->offXml);
+    pUdif->cbXml      = RT_BSWAP_U64(pUdif->cbXml);
+    pUdif->u32Type    = RT_BSWAP_U32(pUdif->u32Type);
+    pUdif->cSectors   = RT_BSWAP_U64(pUdif->cSectors);
+#endif
+}
+
+
+/**
+ * Swaps endian from host cpu to file.
+ * @param   pUdif       The structure.
+ */
+static void dmgUdifFtrHost2FileEndian(PDMGUDIF pUdif)
+{
+    dmgSwapEndianUdif(pUdif);
+    dmgUdifIdHost2FileEndian(&pUdif->SegmentId);
+    dmgUdifCkSumHost2FileEndian(&pUdif->DataCkSum);
+    dmgUdifCkSumHost2FileEndian(&pUdif->MasterCkSum);
+}
+
+
+/**
+ * Swaps endian from file to host cpu.
+ * @param   pUdif       The structure.
+ */
+static void dmgUdifFtrFile2HostEndian(PDMGUDIF pUdif)
+{
+    dmgSwapEndianUdif(pUdif);
+    dmgUdifIdFile2HostEndian(&pUdif->SegmentId);
+    dmgUdifCkSumFile2HostEndian(&pUdif->DataCkSum);
+    dmgUdifCkSumFile2HostEndian(&pUdif->MasterCkSum);
+}
+
+/**
+ * Swaps endian from file to host cpu.
+ * @param   pBlkx       The blkx structure.
+ */
+static void dmgBlkxFile2HostEndian(PDMGBLKX pBlkx)
+{
+    pBlkx->u32Magic            = RT_BE2H_U32(pBlkx->u32Magic);
+    pBlkx->u32Version          = RT_BE2H_U32(pBlkx->u32Version);
+    pBlkx->cSectornumberFirst  = RT_BE2H_U64(pBlkx->cSectornumberFirst);
+    pBlkx->cSectors            = RT_BE2H_U64(pBlkx->cSectors);
+    pBlkx->offDataStart        = RT_BE2H_U64(pBlkx->offDataStart);
+    pBlkx->cSectorsDecompress  = RT_BE2H_U32(pBlkx->cSectorsDecompress);
+    pBlkx->u32BlocksDescriptor = RT_BE2H_U32(pBlkx->u32BlocksDescriptor);
+    pBlkx->cBlocksRunCount     = RT_BE2H_U32(pBlkx->cBlocksRunCount);
+    dmgUdifCkSumFile2HostEndian(&pBlkx->BlkxCkSum);
+}
+
+/**
+ * Swaps endian from file to host cpu.
+ * @param   pBlkxDesc   The blkx descriptor structure.
+ */
+static void dmgBlkxDescFile2HostEndian(PDMGBLKXDESC pBlkxDesc)
+{
+    pBlkxDesc->u32Type        = RT_BE2H_U32(pBlkxDesc->u32Type);
+    pBlkxDesc->u32Reserved    = RT_BE2H_U32(pBlkxDesc->u32Reserved);
+    pBlkxDesc->u64SectorStart = RT_BE2H_U64(pBlkxDesc->u64SectorStart);
+    pBlkxDesc->u64SectorCount = RT_BE2H_U64(pBlkxDesc->u64SectorCount);
+    pBlkxDesc->offData        = RT_BE2H_U64(pBlkxDesc->offData);
+    pBlkxDesc->cbData         = RT_BE2H_U64(pBlkxDesc->cbData);
+}
+
+/**
+ * Validates an UDIF footer structure.
+ *
+ * @returns true if valid, false and LogRel()s on failure.
+ * @param   pFtr        The UDIF footer to validate.
+ * @param   offFtr      The offset of the structure.
+ */
+static bool dmgUdifFtrIsValid(PCDMGUDIF pFtr, uint64_t offFtr)
+{
+    bool fRc = true;
+
+    DMG_VALIDATE(!(pFtr->fFlags & ~DMGUDIF_FLAGS_KNOWN_MASK), ("fFlags=%#RX32 fKnown=%RX32\n", pFtr->fFlags, DMGUDIF_FLAGS_KNOWN_MASK));
+    DMG_VALIDATE(pFtr->offRunData < offFtr, ("offRunData=%#RX64\n", pFtr->offRunData));
+    DMG_VALIDATE(pFtr->cbData    <= offFtr && pFtr->offData + pFtr->cbData <= offFtr, ("cbData=%#RX64 offData=%#RX64 offFtr=%#RX64\n", pFtr->cbData, pFtr->offData, offFtr));
+    DMG_VALIDATE(pFtr->offData    < offFtr, ("offData=%#RX64\n", pFtr->offData));
+    DMG_VALIDATE(pFtr->cbRsrc    <= offFtr && pFtr->offRsrc + pFtr->cbRsrc <= offFtr, ("cbRsrc=%#RX64 offRsrc=%#RX64 offFtr=%#RX64\n", pFtr->cbRsrc, pFtr->offRsrc, offFtr));
+    DMG_VALIDATE(pFtr->offRsrc    < offFtr, ("offRsrc=%#RX64\n", pFtr->offRsrc));
+    DMG_VALIDATE(pFtr->cSegments <= 1,      ("cSegments=%RU32\n", pFtr->cSegments));
+    DMG_VALIDATE(pFtr->iSegment  == 0 || pFtr->iSegment == 1, ("iSegment=%RU32 cSegments=%RU32\n", pFtr->iSegment, pFtr->cSegments));
+    DMG_VALIDATE(pFtr->cbXml    <= offFtr && pFtr->offXml + pFtr->cbXml <= offFtr, ("cbXml=%#RX64 offXml=%#RX64 offFtr=%#RX64\n", pFtr->cbXml, pFtr->offXml, offFtr));
+    DMG_VALIDATE(pFtr->offXml    < offFtr,  ("offXml=%#RX64\n", pFtr->offXml));
+    DMG_VALIDATE(pFtr->cbXml     > 128,     ("cbXml=%#RX64\n", pFtr->cbXml));
+    DMG_VALIDATE(pFtr->cbXml     < 10 * _1M,     ("cbXml=%#RX64\n", pFtr->cbXml));
+    DMG_VALIDATE(pFtr->u32Type == DMGUDIF_TYPE_DEVICE || pFtr->u32Type == DMGUDIF_TYPE_PARTITION,  ("u32Type=%RU32\n", pFtr->u32Type));
+    DMG_VALIDATE(pFtr->cSectors != 0,       ("cSectors=%#RX64\n", pFtr->cSectors));
+    fRc &= dmgUdifCkSumIsValid(&pFtr->DataCkSum, "DataCkSum");
+    fRc &= dmgUdifCkSumIsValid(&pFtr->MasterCkSum, "MasterCkSum");
+
+    return fRc;
+}
+
+
+static bool dmgBlkxIsValid(PCDMGBLKX pBlkx)
+{
+    bool fRc = true;
+
+    fRc &= dmgUdifCkSumIsValid(&pBlkx->BlkxCkSum, "BlkxCkSum");
+    DMG_VALIDATE(pBlkx->u32Magic == DMGBLKX_MAGIC, ("u32Magic=%#RX32 u32MagicExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_MAGIC));
+    DMG_VALIDATE(pBlkx->u32Version == DMGBLKX_VERSION, ("u32Version=%#RX32 u32VersionExpected=%#RX32\n", pBlkx->u32Magic, DMGBLKX_VERSION));
+
+    return fRc;
+}
+
+/**
+ * Swaps endian from host cpu to file.
+ * @param   pId         The structure.
+ */
+static void dmgUdifIdHost2FileEndian(PDMGUDIFID pId)
+{
+    NOREF(pId);
+}
+
+
+/**
+ * Swaps endian from file to host cpu.
+ * @param   pId         The structure.
+ */
+static void dmgUdifIdFile2HostEndian(PDMGUDIFID pId)
+{
+    dmgUdifIdHost2FileEndian(pId);
+}
+
+
+/**
+ * Swaps endian.
+ * @param   pCkSum      The structure.
+ */
+static void dmgSwapEndianUdifCkSum(PDMGUDIFCKSUM pCkSum, uint32_t u32Kind, uint32_t cBits)
+{
+#ifdef RT_BIG_ENDIAN
+    NOREF(pCkSum);
+    NOREF(u32Kind);
+    NOREF(cBits);
+#else
+    switch (u32Kind)
+    {
+        case DMGUDIFCKSUM_NONE:
+            /* nothing to do here */
+            break;
+
+        case DMGUDIFCKSUM_CRC32:
+            Assert(cBits == 32);
+            pCkSum->u32Kind      = RT_BSWAP_U32(pCkSum->u32Kind);
+            pCkSum->cBits        = RT_BSWAP_U32(pCkSum->cBits);
+            pCkSum->uSum.au32[0] = RT_BSWAP_U32(pCkSum->uSum.au32[0]);
+            break;
+
+        default:
+            AssertMsgFailed(("%x\n", u32Kind));
+            break;
+    }
+    NOREF(cBits);
+#endif
+}
+
+
+/**
+ * Swaps endian from host cpu to file.
+ * @param   pCkSum      The structure.
+ */
+static void dmgUdifCkSumHost2FileEndian(PDMGUDIFCKSUM pCkSum)
+{
+    dmgSwapEndianUdifCkSum(pCkSum, pCkSum->u32Kind, pCkSum->cBits);
+}
+
+
+/**
+ * Swaps endian from file to host cpu.
+ * @param   pCkSum      The structure.
+ */
+static void dmgUdifCkSumFile2HostEndian(PDMGUDIFCKSUM pCkSum)
+{
+    dmgSwapEndianUdifCkSum(pCkSum, RT_BE2H_U32(pCkSum->u32Kind), RT_BE2H_U32(pCkSum->cBits));
+}
+
+
+/**
+ * Validates an UDIF checksum structure.
+ *
+ * @returns true if valid, false and LogRel()s on failure.
+ * @param   pCkSum      The checksum structure.
+ * @param   pszPrefix   The message prefix.
+ * @remarks This does not check the checksummed data.
+ */
+static bool dmgUdifCkSumIsValid(PCDMGUDIFCKSUM pCkSum, const char *pszPrefix)
+{
+    bool fRc = true;
+
+    switch (pCkSum->u32Kind)
+    {
+        case DMGUDIFCKSUM_NONE:
+            DMG_VALIDATE(pCkSum->cBits == 0, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
+            break;
+
+        case DMGUDIFCKSUM_CRC32:
+            DMG_VALIDATE(pCkSum->cBits == 32, ("%s/NONE: cBits=%d\n", pszPrefix, pCkSum->cBits));
+            break;
+
+        default:
+            DMG_VALIDATE(0, ("%s: u32Kind=%#RX32\n", pszPrefix, pCkSum->u32Kind));
+            break;
+    }
+    return fRc;
+}
+
+
+/**
+ * Internal. Flush image data to disk.
+ */
+static int dmgFlushImage(PDMGIMAGE pThis)
+{
+    int rc = VINF_SUCCESS;
+
+    if (   pThis->pStorage
+        && !(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+    {
+        /* @todo handle writable files, update checksums etc. */
+    }
+
+    return rc;
+}
+
+
+/**
+ * Internal. Free all allocated space for representing an image except pThis,
+ * and optionally delete the image from disk.
+ */
+static int dmgFreeImage(PDMGIMAGE pThis, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pThis)
+    {
+        if (pThis->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+                dmgFlushImage(pThis);
+
+            dmgFileClose(pThis);
+            pThis->pStorage = NULL;
+        }
+
+        for (unsigned iRsrc = 0; iRsrc < RT_ELEMENTS(pThis->aRsrcs); iRsrc++)
+            for (unsigned i = 0; i < pThis->aRsrcs[iRsrc].cEntries; i++)
+            {
+                if (pThis->aRsrcs[iRsrc].aEntries[i].pbData)
+                {
+                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pbData);
+                    pThis->aRsrcs[iRsrc].aEntries[i].pbData = NULL;
+                }
+                if (pThis->aRsrcs[iRsrc].aEntries[i].pszName)
+                {
+                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszName);
+                    pThis->aRsrcs[iRsrc].aEntries[i].pszName = NULL;
+                }
+                if (pThis->aRsrcs[iRsrc].aEntries[i].pszCFName)
+                {
+                    RTMemFree(pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
+                    pThis->aRsrcs[iRsrc].aEntries[i].pszCFName = NULL;
+                }
+            }
+
+        if (fDelete && pThis->pszFilename)
+            dmgFileDelete(pThis, pThis->pszFilename);
+
+        if (pThis->pvDecompExtent)
+        {
+            RTMemFree(pThis->pvDecompExtent);
+            pThis->pvDecompExtent = NULL;
+            pThis->cbDecompExtent = 0;
+        }
+
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+#define STARTS_WITH(pszString, szStart) \
+        ( strncmp(pszString, szStart, sizeof(szStart) - 1) == 0 )
+
+#define STARTS_WITH_WORD(pszString, szWord) \
+        (   STARTS_WITH(pszString, szWord) \
+         && !RT_C_IS_ALNUM((pszString)[sizeof(szWord) - 1]) )
+
+#define SKIP_AHEAD(psz, szWord) \
+        do { \
+            (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
+        } while (0)
+
+#define REQUIRE_WORD(psz, szWord) \
+        do { \
+            if (!STARTS_WITH_WORD(psz, szWord)) \
+                return psz; \
+            (psz) = RTStrStripL((psz) + sizeof(szWord) - 1); \
+        } while (0)
+
+#define REQUIRE_TAG(psz, szTag) \
+        do { \
+            if (!STARTS_WITH(psz, "<" szTag ">")) \
+                return psz; \
+            (psz) = RTStrStripL((psz) + sizeof("<" szTag ">") - 1); \
+        } while (0)
+
+#define REQUIRE_TAG_NO_STRIP(psz, szTag) \
+        do { \
+            if (!STARTS_WITH(psz, "<" szTag ">")) \
+                return psz; \
+            (psz) += sizeof("<" szTag ">") - 1; \
+        } while (0)
+
+#define REQUIRE_END_TAG(psz, szTag) \
+        do { \
+            if (!STARTS_WITH(psz, "</" szTag ">")) \
+                return psz; \
+            (psz) = RTStrStripL((psz) + sizeof("</" szTag ">") - 1); \
+        } while (0)
+
+
+/**
+ * Finds the next tag end.
+ *
+ * @returns Pointer to a '>' or '\0'.
+ * @param   pszCur      The current position.
+ */
+static const char *dmgXmlFindTagEnd(const char *pszCur)
+{
+    /* Might want to take quoted '>' into account? */
+    char ch;
+    while ((ch = *pszCur) != '\0' && ch != '>')
+        pszCur++;
+    return pszCur;
+}
+
+
+/**
+ * Finds the end tag.
+ *
+ * Does not deal with '<tag attr="1"/>' style tags.
+ *
+ * @returns Pointer to the first char in the end tag. NULL if another tag
+ *          was encountered first or if we hit the end of the file.
+ * @param   ppszCur     The current position (IN/OUT).
+ * @param   pszTag      The tag name.
+ */
+static const char *dmgXmlFindEndTag(const char **ppszCur, const char *pszTag)
+{
+    const char         *psz = *ppszCur;
+    char                ch;
+    while ((ch = *psz))
+    {
+        if (ch == '<')
+        {
+            size_t const cchTag = strlen(pszTag);
+            if (    psz[1] == '/'
+                &&  !memcmp(&psz[2], pszTag, cchTag)
+                &&  psz[2 + cchTag] == '>')
+            {
+                *ppszCur = psz + 2 + cchTag + 1;
+                return psz;
+            }
+            break;
+        }
+        psz++;
+    }
+    return NULL;
+}
+
+
+/**
+ * Reads a signed 32-bit value.
+ *
+ * @returns NULL on success, pointer to the offending text on failure.
+ * @param   ppszCur     The text position (IN/OUT).
+ * @param   pi32        Where to store the value.
+ */
+static const char *dmgXmlParseS32(const char **ppszCur, int32_t *pi32)
+{
+    const char *psz = *ppszCur;
+
+    /*
+     * <string>-1</string>
+     */
+    REQUIRE_TAG_NO_STRIP(psz, "string");
+
+    char *pszNext;
+    int rc = RTStrToInt32Ex(psz, &pszNext, 0, pi32);
+    if (rc != VWRN_TRAILING_CHARS)
+        return *ppszCur;
+    psz = pszNext;
+
+    REQUIRE_END_TAG(psz, "string");
+    *ppszCur = psz;
+    return NULL;
+}
+
+
+/**
+ * Reads an unsigned 32-bit value.
+ *
+ * @returns NULL on success, pointer to the offending text on failure.
+ * @param   ppszCur     The text position (IN/OUT).
+ * @param   pu32        Where to store the value.
+ */
+static const char *dmgXmlParseU32(const char **ppszCur, uint32_t *pu32)
+{
+    const char *psz = *ppszCur;
+
+    /*
+     * <string>0x00ff</string>
+     */
+    REQUIRE_TAG_NO_STRIP(psz, "string");
+
+    char *pszNext;
+    int rc = RTStrToUInt32Ex(psz, &pszNext, 0, pu32);
+    if (rc != VWRN_TRAILING_CHARS)
+        return *ppszCur;
+    psz = pszNext;
+
+    REQUIRE_END_TAG(psz, "string");
+    *ppszCur = psz;
+    return NULL;
+}
+
+
+/**
+ * Reads a string value.
+ *
+ * @returns NULL on success, pointer to the offending text on failure.
+ * @param   ppszCur     The text position (IN/OUT).
+ * @param   ppszString  Where to store the pointer to the string. The caller
+ *                      must free this using RTMemFree.
+ */
+static const char *dmgXmlParseString(const char **ppszCur, char **ppszString)
+{
+    const char *psz = *ppszCur;
+
+    /*
+     * <string>Driver Descriptor Map (DDM : 0)</string>
+     */
+    REQUIRE_TAG_NO_STRIP(psz, "string");
+
+    const char *pszStart = psz;
+    const char *pszEnd = dmgXmlFindEndTag(&psz, "string");
+    if (!pszEnd)
+        return *ppszCur;
+    psz = RTStrStripL(psz);
+
+    *ppszString = (char *)RTMemDupEx(pszStart, pszEnd - pszStart, 1);
+    if (!*ppszString)
+        return *ppszCur;
+
+    *ppszCur = psz;
+    return NULL;
+}
+
+
+/**
+ * Parses the BASE-64 coded data tags.
+ *
+ * @returns NULL on success, pointer to the offending text on failure.
+ * @param   ppszCur     The text position (IN/OUT).
+ * @param   ppbData     Where to store the pointer to the data we've read. The
+ *                      caller must free this using RTMemFree.
+ * @param   pcbData     The number of bytes we're returning.
+ */
+static const char *dmgXmlParseData(const char **ppszCur, uint8_t **ppbData, size_t *pcbData)
+{
+    const char *psz = *ppszCur;
+
+    /*
+     * <data>   AAAAA...    </data>
+     */
+    REQUIRE_TAG(psz, "data");
+
+    const char *pszStart = psz;
+    ssize_t cbData = RTBase64DecodedSize(pszStart, (char **)&psz);
+    if (cbData == -1)
+        return *ppszCur;
+    const char *pszEnd = psz;
+
+    REQUIRE_END_TAG(psz, "data");
+
+    *ppbData = (uint8_t *)RTMemAlloc(cbData);
+    if (!*ppbData)
+        return *ppszCur;
+    char *pszIgnored;
+    int rc = RTBase64Decode(pszStart, *ppbData, cbData, pcbData, &pszIgnored);
+    if (RT_FAILURE(rc))
+    {
+        RTMemFree(*ppbData);
+        *ppbData = NULL;
+        return *ppszCur;
+    }
+
+    *ppszCur = psz;
+    return NULL;
+}
+
+
+/**
+ * Parses the XML resource-fork in a rather presumptive manner.
+ *
+ * This function is supposed to construct the DMG::aRsrcs instance data
+ * parts.
+ *
+ * @returns NULL on success, pointer to the problematic text on failure.
+ * @param   pThis       The DMG instance data.
+ * @param   pszXml      The XML text to parse, UTF-8.
+ * @param   cch         The size of the the XML text.
+ */
+static const char *dmgOpenXmlToRsrc(PDMGIMAGE pThis, char const *pszXml)
+{
+    const char *psz = pszXml;
+
+    /*
+     * Verify the ?xml, !DOCTYPE and plist tags.
+     */
+    SKIP_AHEAD(psz, "");
+
+    /* <?xml version="1.0" encoding="UTF-8"?> */
+    REQUIRE_WORD(psz, "<?xml");
+    while (*psz != '?')
+    {
+        if (!*psz)
+            return psz;
+        if (STARTS_WITH_WORD(psz, "version="))
+        {
+            SKIP_AHEAD(psz, "version=");
+            REQUIRE_WORD(psz, "\"1.0\"");
+        }
+        else if (STARTS_WITH_WORD(psz, "encoding="))
+        {
+            SKIP_AHEAD(psz, "encoding=");
+            REQUIRE_WORD(psz, "\"UTF-8\"");
+        }
+        else
+            return psz;
+    }
+    SKIP_AHEAD(psz, "?>");
+
+    /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
+    REQUIRE_WORD(psz, "<!DOCTYPE");
+    REQUIRE_WORD(psz, "plist");
+    REQUIRE_WORD(psz, "PUBLIC");
+    psz = dmgXmlFindTagEnd(psz);
+    REQUIRE_WORD(psz, ">");
+
+    /* <plist version="1.0"> */
+    REQUIRE_WORD(psz, "<plist");
+    REQUIRE_WORD(psz, "version=");
+    REQUIRE_WORD(psz, "\"1.0\"");
+    REQUIRE_WORD(psz, ">");
+
+    /*
+     * Descend down to the 'resource-fork' dictionary.
+     * ASSUME it's the only top level dictionary.
+     */
+    /* <dict> <key>resource-fork</key> */
+    REQUIRE_TAG(psz, "dict");
+    REQUIRE_WORD(psz, "<key>resource-fork</key>");
+
+    /*
+     * Parse the keys in the resource-fork dictionary.
+     * ASSUME that there are just two, 'blkx' and 'plst'.
+     */
+    REQUIRE_TAG(psz, "dict");
+    while (!STARTS_WITH_WORD(psz, "</dict>"))
+    {
+        /*
+         * Parse the key and Create the resource-fork entry.
+         */
+        unsigned iRsrc;
+        if (STARTS_WITH_WORD(psz, "<key>blkx</key>"))
+        {
+            REQUIRE_WORD(psz, "<key>blkx</key>");
+            iRsrc = DMG_RSRC_IDX_BLKX;
+            strcpy(&pThis->aRsrcs[iRsrc].szName[0], "blkx");
+        }
+        else if (STARTS_WITH_WORD(psz, "<key>plst</key>"))
+        {
+            REQUIRE_WORD(psz, "<key>plst</key>");
+            iRsrc = DMG_RSRC_IDX_PLST;
+            strcpy(&pThis->aRsrcs[iRsrc].szName[0], "plst");
+        }
+        else
+        {
+            SKIP_AHEAD(psz, "</array>");
+            continue;
+        }
+
+
+        /*
+         * Descend into the array and add the elements to the resource entry.
+         */
+        /* <array> */
+        REQUIRE_TAG(psz, "array");
+        while (!STARTS_WITH_WORD(psz, "</array>"))
+        {
+            REQUIRE_TAG(psz, "dict");
+            uint32_t i = pThis->aRsrcs[iRsrc].cEntries;
+            if (i == RT_ELEMENTS(pThis->aRsrcs[iRsrc].aEntries))
+                return psz;
+
+            while (!STARTS_WITH_WORD(psz, "</dict>"))
+            {
+
+                /* switch on the key. */
+                const char *pszErr;
+                if (STARTS_WITH_WORD(psz, "<key>Attributes</key>"))
+                {
+                    REQUIRE_WORD(psz, "<key>Attributes</key>");
+                    pszErr = dmgXmlParseU32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].fAttributes);
+                }
+                else if (STARTS_WITH_WORD(psz, "<key>ID</key>"))
+                {
+                    REQUIRE_WORD(psz, "<key>ID</key>");
+                    pszErr = dmgXmlParseS32(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].iId);
+                }
+                else if (STARTS_WITH_WORD(psz, "<key>Name</key>"))
+                {
+                    REQUIRE_WORD(psz, "<key>Name</key>");
+                    pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszName);
+                }
+                else if (STARTS_WITH_WORD(psz, "<key>CFName</key>"))
+                {
+                    REQUIRE_WORD(psz, "<key>CFName</key>");
+                    pszErr = dmgXmlParseString(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pszCFName);
+                }
+                else if (STARTS_WITH_WORD(psz, "<key>Data</key>"))
+                {
+                    REQUIRE_WORD(psz, "<key>Data</key>");
+                    pszErr = dmgXmlParseData(&psz, &pThis->aRsrcs[iRsrc].aEntries[i].pbData, &pThis->aRsrcs[iRsrc].aEntries[i].cbData);
+                }
+                else
+                    pszErr = psz;
+                if (pszErr)
+                    return pszErr;
+            } /* while not </dict> */
+            REQUIRE_END_TAG(psz, "dict");
+
+            pThis->aRsrcs[iRsrc].cEntries++;
+        } /* while not </array> */
+        REQUIRE_END_TAG(psz, "array");
+
+    } /* while not </dict> */
+    REQUIRE_END_TAG(psz, "dict");
+
+    /*
+     * ASSUMING there is only the 'resource-fork', we'll now see the end of
+     * the outer dict, plist and text.
+     */
+    /* </dict> </plist> */
+    REQUIRE_END_TAG(psz, "dict");
+    REQUIRE_END_TAG(psz, "plist");
+
+    /* the end */
+    if (*psz)
+        return psz;
+
+    return NULL;
+}
+
+#undef REQUIRE_END_TAG
+#undef REQUIRE_TAG_NO_STRIP
+#undef REQUIRE_TAG
+#undef REQUIRE_WORD
+#undef SKIP_AHEAD
+#undef STARTS_WITH_WORD
+#undef STARTS_WITH
+
+/**
+ * Returns the data attached to a resource.
+ *
+ * @returns VBox status code.
+ * @param   pThis        The DMG instance data.
+ * @param   pcszRsrcName Name of the resource to get.
+ */
+static int dmgGetRsrcData(PDMGIMAGE pThis, const char *pcszRsrcName,
+                          PCDMGUDIFRSRCARRAY *ppcRsrc)
+{
+    int rc = VERR_NOT_FOUND;
+
+    for (unsigned i = 0; i < RT_ELEMENTS(pThis->aRsrcs); i++)
+    {
+        if (!strcmp(pThis->aRsrcs[i].szName, pcszRsrcName))
+        {
+            *ppcRsrc = &pThis->aRsrcs[i];
+            rc = VINF_SUCCESS;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Creates a new extent from the given blkx descriptor.
+ *
+ * @returns VBox status code.
+ * @param   pThis          DMG instance data.
+ * @param   uSectorPart    First sector the partition owning the blkx descriptor has.
+ * @param   pBlkxDesc      The blkx descriptor.
+ */
+static int dmgExtentCreateFromBlkxDesc(PDMGIMAGE pThis, uint64_t uSectorPart, PDMGBLKXDESC pBlkxDesc)
+{
+    int rc = VINF_SUCCESS;
+    DMGEXTENTTYPE enmExtentTypeNew;
+    PDMGEXTENT pExtentNew = NULL;
+
+    if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_RAW)
+        enmExtentTypeNew = DMGEXTENTTYPE_RAW;
+    else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_IGNORE)
+        enmExtentTypeNew = DMGEXTENTTYPE_ZERO;
+    else if (pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_ZLIB)
+        enmExtentTypeNew = DMGEXTENTTYPE_COMP_ZLIB;
+    else
+    {
+        AssertMsgFailed(("This method supports only raw or zero extents!\n"));
+        return VERR_NOT_SUPPORTED;
+    }
+
+    /** @todo: Merge raw extents if possible to save memory. */
+#if 0
+    pExtentNew = pThis->pExtentLast;
+    if (   pExtentNew
+        && pExtentNew->enmType == enmExtentTypeNew
+        && enmExtentTypeNew == DMGEXTENTTYPE_RAW
+        && pExtentNew->uSectorExtent + pExtentNew->cSectorsExtent == offDevice + pBlkxDesc->u64SectorStart * DMG_SECTOR_SIZE;
+        && pExtentNew->offFileStart + pExtentNew->cbExtent == pBlkxDesc->offData)
+    {
+        /* Increase the last extent. */
+        pExtentNew->cbExtent += pBlkxDesc->cbData;
+    }
+    else
+#endif
+    {
+        if (pThis->cExtentsMax == pThis->cExtents)
+        {
+            pThis->cExtentsMax += 64;
+
+            /* Increase the array. */
+            PDMGEXTENT paExtentsNew = (PDMGEXTENT)RTMemRealloc(pThis->paExtents, sizeof(DMGEXTENT) * pThis->cExtentsMax);
+            if (!paExtentsNew)
+            {
+                rc = VERR_NO_MEMORY;
+                pThis->cExtentsMax -= 64;
+            }
+            else
+                pThis->paExtents = paExtentsNew;
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            pExtentNew = &pThis->paExtents[pThis->cExtents++];
+
+            pExtentNew->enmType        = enmExtentTypeNew;
+            pExtentNew->uSectorExtent  = uSectorPart + pBlkxDesc->u64SectorStart;
+            pExtentNew->cSectorsExtent = pBlkxDesc->u64SectorCount;
+            pExtentNew->offFileStart   = pBlkxDesc->offData;
+            pExtentNew->cbFile         = pBlkxDesc->cbData;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Find the extent for the given sector number.
+ */
+static PDMGEXTENT dmgExtentGetFromOffset(PDMGIMAGE pThis, uint64_t uSector)
+{
+    /*
+     * We assume that the array is ordered from lower to higher sector
+     * numbers.
+     * This makes it possible to bisect the array to find the extent
+     * faster than using a linked list.
+     */
+    PDMGEXTENT pExtent = NULL;
+    unsigned idxCur = pThis->idxExtentLast;
+    unsigned idxMax = pThis->cExtents;
+    unsigned idxMin = 0;
+
+    while (idxMin < idxMax)
+    {
+        PDMGEXTENT pExtentCur = &pThis->paExtents[idxCur];
+
+        /* Determine the search direction. */
+        if (uSector < pExtentCur->uSectorExtent)
+        {
+            /* Search left from the current extent. */
+            idxMax = idxCur;
+        }
+        else if (uSector >= pExtentCur->uSectorExtent + pExtentCur->cSectorsExtent)
+        {
+            /* Search right from the current extent. */
+            idxMin = idxCur;
+        }
+        else
+        {
+            /* The sector lies in the extent, stop searching. */
+            pExtent = pExtentCur;
+            break;
+        }
+
+        idxCur = idxMin + (idxMax - idxMin) / 2;
+    }
+
+    if (pExtent)
+        pThis->idxExtentLast = idxCur;
+
+    return pExtent;
+}
+
+/**
+ * Goes through the BLKX structure and creates the necessary extents.
+ */
+static int dmgBlkxParse(PDMGIMAGE pThis, PDMGBLKX pBlkx)
+{
+    int rc = VINF_SUCCESS;
+    PDMGBLKXDESC pBlkxDesc = (PDMGBLKXDESC)(pBlkx + 1);
+
+    for (unsigned i = 0; i < pBlkx->cBlocksRunCount; i++)
+    {
+        dmgBlkxDescFile2HostEndian(pBlkxDesc);
+
+        switch (pBlkxDesc->u32Type)
+        {
+            case DMGBLKXDESC_TYPE_RAW:
+            case DMGBLKXDESC_TYPE_IGNORE:
+            case DMGBLKXDESC_TYPE_ZLIB:
+            {
+                rc = dmgExtentCreateFromBlkxDesc(pThis, pBlkx->cSectornumberFirst, pBlkxDesc);
+                break;
+            }
+            case DMGBLKXDESC_TYPE_COMMENT:
+            case DMGBLKXDESC_TYPE_TERMINATOR:
+                break;
+            default:
+                rc = VERR_VD_DMG_INVALID_HEADER;
+                break;
+        }
+
+        if (   pBlkxDesc->u32Type == DMGBLKXDESC_TYPE_TERMINATOR
+            || RT_FAILURE(rc))
+                break;
+
+        pBlkxDesc++;
+    }
+
+    return rc;
+}
+
+/**
+ * Worker for dmgOpen that reads in and validates all the necessary
+ * structures from the image.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The DMG instance data.
+ * @param   uOpenFlags  Flags for defining the open type.
+ */
+static int dmgOpenImage(PDMGIMAGE pThis, unsigned uOpenFlags)
+{
+    pThis->uOpenFlags  = uOpenFlags;
+
+    pThis->pInterfaceError = VDInterfaceGet(pThis->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pThis->pInterfaceError)
+        pThis->pInterfaceErrorCallbacks = VDGetInterfaceError(pThis->pInterfaceError);
+
+    /* Get I/O interface. */
+    pThis->pInterfaceIO = VDInterfaceGet(pThis->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pThis->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pThis->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pThis->pInterfaceIO);
+    AssertPtrReturn(pThis->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    int rc = dmgFileOpen(pThis, pThis->pszFilename,
+                         VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                    false /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        return rc;
+    }
+
+    /*
+     * Read the footer.
+     */
+    rc = dmgFileGetSize(pThis, &pThis->cbFile);
+    if (RT_FAILURE(rc))
+        return rc;
+    if (pThis->cbFile < 1024)
+        return VERR_VD_DMG_INVALID_HEADER;
+    rc = dmgFileReadSync(pThis, pThis->cbFile - sizeof(pThis->Ftr), &pThis->Ftr, sizeof(pThis->Ftr), NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    dmgUdifFtrFile2HostEndian(&pThis->Ftr);
+
+    /*
+     * Do we recognize the footer structure? If so, is it valid?
+     */
+    if (pThis->Ftr.u32Magic != DMGUDIF_MAGIC)
+        return VERR_VD_DMG_INVALID_HEADER;
+    if (pThis->Ftr.u32Version != DMGUDIF_VER_CURRENT)
+        return VERR_VD_DMG_INVALID_HEADER;
+    if (pThis->Ftr.cbFooter != sizeof(pThis->Ftr))
+        return VERR_VD_DMG_INVALID_HEADER;
+
+    if (!dmgUdifFtrIsValid(&pThis->Ftr, pThis->cbFile - sizeof(pThis->Ftr)))
+    {
+        DMG_PRINTF(("Bad DMG: '%s' cbFile=%RTfoff\n", pThis->pszFilename, pThis->cbFile));
+        return VERR_VD_DMG_INVALID_HEADER;
+    }
+
+    pThis->cbSize = pThis->Ftr.cSectors * DMG_SECTOR_SIZE;
+
+    /*
+     * Read and parse the XML portion.
+     */
+    size_t cchXml = (size_t)pThis->Ftr.cbXml;
+    char *pszXml = (char *)RTMemAlloc(cchXml + 1);
+    if (!pszXml)
+        return VERR_NO_MEMORY;
+    rc = dmgFileReadSync(pThis, pThis->Ftr.offXml, pszXml, cchXml, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        pszXml[cchXml] = '\0';
+        const char *pszError = dmgOpenXmlToRsrc(pThis, pszXml);
+        if (!pszError)
+        {
+            PCDMGUDIFRSRCARRAY pRsrcBlkx = NULL;
+
+            rc = dmgGetRsrcData(pThis, "blkx", &pRsrcBlkx);
+            if (RT_SUCCESS(rc))
+            {
+                for (unsigned idxBlkx = 0; idxBlkx < pRsrcBlkx->cEntries; idxBlkx++)
+                {
+                    PDMGBLKX pBlkx = NULL;
+
+                    if (pRsrcBlkx->aEntries[idxBlkx].cbData < sizeof(DMGBLKX))
+                    {
+                        rc = VERR_VD_DMG_INVALID_HEADER;
+                        break;
+                    }
+
+                    pBlkx = (PDMGBLKX)RTMemAllocZ(pRsrcBlkx->aEntries[idxBlkx].cbData);
+                    if (!pBlkx)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
+
+                    memcpy(pBlkx, pRsrcBlkx->aEntries[idxBlkx].pbData, pRsrcBlkx->aEntries[idxBlkx].cbData);
+
+                    dmgBlkxFile2HostEndian(pBlkx);
+
+                    if (   dmgBlkxIsValid(pBlkx)
+                        && pRsrcBlkx->aEntries[idxBlkx].cbData == pBlkx->cBlocksRunCount * sizeof(DMGBLKXDESC) + sizeof(DMGBLKX))
+                        rc = dmgBlkxParse(pThis, pBlkx);
+                    else
+                        rc = VERR_VD_DMG_INVALID_HEADER;
+
+                    RTMemFree(pBlkx);
+
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            }
+            else
+                rc = VERR_VD_DMG_INVALID_HEADER;
+        }
+        else
+        {
+            DMG_PRINTF(("**** XML DUMP BEGIN ***\n%s\n**** XML DUMP END ****\n", pszXml));
+            DMG_PRINTF(("**** Bad XML at %#lx (%lu) ***\n%.256s\n**** Bad XML END ****\n",
+                            (unsigned long)(pszError - pszXml), (unsigned long)(pszError - pszXml), pszError));
+            rc = VERR_VD_DMG_XML_PARSE_ERROR;
+        }
+    }
+    RTMemFree(pszXml);
+
+    if (RT_FAILURE(rc))
+        dmgFreeImage(pThis, false);
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int dmgCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
+                 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
+    int rc;
+    PVDIOSTORAGE pStorage;
+    uint64_t cbFile, offFtr = 0;
+    DMGUDIF Ftr;
+
+    /* Get I/O interface. */
+    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
+    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the file and read the footer.
+     */
+    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
+                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
+                                                                   false /* fCreate */),
+                                        &pStorage);
+    if (RT_SUCCESS(rc))
+        rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
+                                               &cbFile);
+    if (RT_SUCCESS(rc))
+    {
+        offFtr = cbFile - sizeof(Ftr);
+        rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
+                                                offFtr, &Ftr, sizeof(Ftr), NULL);
+    }
+    else
+    {
+        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+        rc = VERR_VD_DMG_INVALID_HEADER;
+    }
+    if (RT_SUCCESS(rc))
+    {
+        dmgUdifFtrFile2HostEndian(&Ftr);
+
+        /*
+         * Do we recognize this stuff? Does it look valid?
+         */
+        if (    Ftr.u32Magic    == DMGUDIF_MAGIC
+            &&  Ftr.u32Version  == DMGUDIF_VER_CURRENT
+            &&  Ftr.cbFooter    == sizeof(Ftr))
+        {
+            if (dmgUdifFtrIsValid(&Ftr, offFtr))
+            {
+                rc = VINF_SUCCESS;
+                *penmType = VDTYPE_DVD;
+            }
+            else
+            {
+                DMG_PRINTF(("Bad DMG: '%s' offFtr=%RTfoff\n", pszFilename, offFtr));
+                rc = VERR_VD_DMG_INVALID_HEADER;
+            }
+        }
+        else
+            rc = VERR_VD_DMG_INVALID_HEADER;
+    }
+
+    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int dmgOpen(const char *pszFilename, unsigned uOpenFlags,
+                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                   VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc = VINF_SUCCESS;
+    PDMGIMAGE pThis;
+
+    /* Check open flags. All valid flags are (in principle) supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /*
+     * Reject combinations we don't currently support.
+     *
+     * There is no point in being paranoid about the input here as we're just a
+     * simple backend and can expect the caller to be the only user and already
+     * have validate what it passes thru to us.
+     */
+    if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+    {
+        rc = VERR_NOT_SUPPORTED;
+        goto out;
+    }
+
+    /*
+     * Create the basic instance data structure and open the file,
+     * then hand it over to a worker function that does all the rest.
+     */
+    pThis = (PDMGIMAGE)RTMemAllocZ(sizeof(*pThis));
+    if (!pThis)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    pThis->pszFilename = pszFilename;
+    pThis->pStorage    = NULL;
+    pThis->pVDIfsDisk  = pVDIfsDisk;
+    pThis->pVDIfsImage = pVDIfsImage;
+
+    rc = dmgOpenImage(pThis, uOpenFlags);
+  if (RT_SUCCESS(rc))
+        *ppBackendData = pThis;
+    else
+        RTMemFree(pThis);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int dmgCreate(const char *pszFilename, uint64_t cbSize,
+                     unsigned uImageFlags, const char *pszComment,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     PCRTUUID pUuid, unsigned uOpenFlags,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc = VERR_NOT_SUPPORTED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int dmgRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+    int rc = VERR_NOT_SUPPORTED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int dmgClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    rc = dmgFreeImage(pThis, fDelete);
+    RTMemFree(pThis);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int dmgRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                   size_t cbToRead, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    PDMGEXTENT pExtent = NULL;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pThis);
+    Assert(uOffset % DMG_SECTOR_SIZE == 0);
+    Assert(cbToRead % DMG_SECTOR_SIZE == 0);
+
+    if (   uOffset + cbToRead > pThis->cbSize
+        || cbToRead == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pExtent = dmgExtentGetFromOffset(pThis, DMG_BYTE2BLOCK(uOffset));
+
+    if (pExtent)
+    {
+        uint64_t uExtentRel = DMG_BYTE2BLOCK(uOffset) - pExtent->uSectorExtent;
+
+        /* Remain in this extent. */
+        cbToRead = RT_MIN(cbToRead, DMG_BLOCK2BYTE(pExtent->cSectorsExtent - uExtentRel));
+
+        switch (pExtent->enmType)
+        {
+            case DMGEXTENTTYPE_RAW:
+            {
+                rc = dmgFileReadSync(pThis, pExtent->offFileStart + DMG_BLOCK2BYTE(uExtentRel), pvBuf, cbToRead, NULL);
+                break;
+            }
+            case DMGEXTENTTYPE_ZERO:
+            {
+                memset(pvBuf, 0, cbToRead);
+                break;
+            }
+            case DMGEXTENTTYPE_COMP_ZLIB:
+            {
+                if (pThis->pExtentDecomp != pExtent)
+                {
+                    if (DMG_BLOCK2BYTE(pExtent->cSectorsExtent) > pThis->cbDecompExtent)
+                    {
+                        if (RT_LIKELY(pThis->pvDecompExtent))
+                            RTMemFree(pThis->pvDecompExtent);
+
+                        pThis->pvDecompExtent = RTMemAllocZ(DMG_BLOCK2BYTE(pExtent->cSectorsExtent));
+                        if (!pThis->pvDecompExtent)
+                            rc = VERR_NO_MEMORY;
+                        else
+                            pThis->cbDecompExtent = DMG_BLOCK2BYTE(pExtent->cSectorsExtent);
+                    }
+
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = dmgFileInflateSync(pThis, pExtent->offFileStart, pExtent->cbFile,
+                                                pThis->pvDecompExtent,
+                                                RT_MIN(pThis->cbDecompExtent, DMG_BLOCK2BYTE(pExtent->cSectorsExtent)));
+                        if (RT_SUCCESS(rc))
+                            pThis->pExtentDecomp = pExtent;
+                    }
+                }
+
+                if (RT_SUCCESS(rc))
+                    memcpy(pvBuf, (uint8_t *)pThis->pvDecompExtent + DMG_BLOCK2BYTE(uExtentRel), cbToRead);
+                break;
+            }
+            default:
+                AssertMsgFailed(("Invalid extent type\n"));
+        }
+
+        if (RT_SUCCESS(rc))
+            *pcbActuallyRead = cbToRead;
+    }
+    else
+        rc = VERR_INVALID_PARAMETER;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int dmgWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                    size_t cbToWrite, size_t *pcbWriteProcess,
+                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+                 pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc = VERR_NOT_IMPLEMENTED;
+
+    AssertPtr(pThis);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    AssertMsgFailed(("Not implemented\n"));
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int dmgFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    rc = dmgFlushImage(pThis);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned dmgGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        return 1;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t dmgGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pThis);
+
+    if (pThis && pThis->pStorage)
+        cb = pThis->cbSize;
+
+    LogFlowFunc(("returns %llu\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t dmgGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        uint64_t cbFile;
+        if (pThis->pStorage)
+        {
+            int rc = dmgFileGetSize(pThis, &cbFile);
+            if (RT_SUCCESS(rc))
+                cb = cbFile;
+        }
+    }
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int dmgGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (pThis->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pThis->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int dmgSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
+                 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pThis->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int dmgGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (pThis->LCHSGeometry.cCylinders)
+        {
+            *pLCHSGeometry = pThis->LCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int dmgSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
+                 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pThis->LCHSGeometry = *pLCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned dmgGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        uImageFlags = pThis->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned dmgGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        uOpenFlags = pThis->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int dmgSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pThis || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO || VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    rc = dmgFreeImage(pThis, false);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = dmgOpenImage(pThis, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int dmgGetComment(void *pBackendData, char *pszComment,
+                         size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int dmgSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PDMGIMAGE pImage = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int dmgGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int dmgSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int dmgGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int dmgSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int dmgGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int dmgSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int dmgGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int dmgSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pThis);
+
+    if (pThis)
+    {
+        if (!(pThis->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void dmgDump(void *pBackendData)
+{
+    PDMGIMAGE pThis = (PDMGIMAGE)pBackendData;
+
+    AssertPtr(pThis);
+    if (pThis)
+    {
+        dmgMessage(pThis, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
+                    pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors,
+                    pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors,
+                    pThis->cbSize / 512);
+    }
+}
+
+
+VBOXHDDBACKEND g_DmgBackend =
+{
+    /* pszBackendName */
+    "DMG",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+    VD_CAP_FILE | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aDmgFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    dmgCheckIfValid,
+    /* pfnOpen */
+    dmgOpen,
+    /* pfnCreate */
+    dmgCreate,
+    /* pfnRename */
+    dmgRename,
+    /* pfnClose */
+    dmgClose,
+    /* pfnRead */
+    dmgRead,
+    /* pfnWrite */
+    dmgWrite,
+    /* pfnFlush */
+    dmgFlush,
+    /* pfnGetVersion */
+    dmgGetVersion,
+    /* pfnGetSize */
+    dmgGetSize,
+    /* pfnGetFileSize */
+    dmgGetFileSize,
+    /* pfnGetPCHSGeometry */
+    dmgGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    dmgSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    dmgGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    dmgSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    dmgGetImageFlags,
+    /* pfnGetOpenFlags */
+    dmgGetOpenFlags,
+    /* pfnSetOpenFlags */
+    dmgSetOpenFlags,
+    /* pfnGetComment */
+    dmgGetComment,
+    /* pfnSetComment */
+    dmgSetComment,
+    /* pfnGetUuid */
+    dmgGetUuid,
+    /* pfnSetUuid */
+    dmgSetUuid,
+    /* pfnGetModificationUuid */
+    dmgGetModificationUuid,
+    /* pfnSetModificationUuid */
+    dmgSetModificationUuid,
+    /* pfnGetParentUuid */
+    dmgGetParentUuid,
+    /* pfnSetParentUuid */
+    dmgSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    dmgGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    dmgSetParentModificationUuid,
+    /* pfnDump */
+    dmgDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    NULL,
+    /* pfnAsyncRead */
+    NULL,
+    /* pfnAsyncWrite */
+    NULL,
+    /* pfnAsyncFlush */
+    NULL,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    NULL
+};
Index: /trunk/src/VBox/Storage/ISCSI.cpp
===================================================================
--- /trunk/src/VBox/Storage/ISCSI.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/ISCSI.cpp	(revision 33567)
@@ -0,0 +1,5613 @@
+/* $Id$ */
+/** @file
+ * iSCSI initiator driver, VD backend.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_ISCSI
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+#include <iprt/md5.h>
+#include <iprt/tcp.h>
+#include <iprt/time.h>
+#include <VBox/scsi.h>
+
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+
+/** Default port number to use for iSCSI. */
+#define ISCSI_DEFAULT_PORT 3260
+
+
+/** Converts a number in the range of 0 - 15 into the corresponding hex char. */
+#define NUM_2_HEX(b) ('0' + (b) + (((b) > 9) ? 39 : 0))
+/** Converts a hex char into the corresponding number in the range 0-15. */
+#define HEX_2_NUM(c) (((c) <= '9') ? ((c) - '0') : (((c - 'A' + 10) & 0xf)))
+/* Converts a base64 char into the corresponding number in the range 0-63. */
+#define B64_2_NUM(c) ((c >= 'A' && c <= 'Z') ? (c - 'A') : (c >= 'a' && c <= 'z') ? (c - 'a' + 26) : (c >= '0' && c <= '9') ? (c - '0' + 52) : (c == '+') ? 62 : (c == '/') ? 63 : -1)
+
+
+/** Minimum CHAP_MD5 challenge length in bytes. */
+#define CHAP_MD5_CHALLENGE_MIN 16
+/** Maximum CHAP_MD5 challenge length in bytes. */
+#define CHAP_MD5_CHALLENGE_MAX 24
+
+
+/**
+ * SCSI peripheral device type. */
+typedef enum SCSIDEVTYPE
+{
+    /** direct-access device. */
+    SCSI_DEVTYPE_DISK = 0,
+    /** sequential-access device. */
+    SCSI_DEVTYPE_TAPE,
+    /** printer device. */
+    SCSI_DEVTYPE_PRINTER,
+    /** processor device. */
+    SCSI_DEVTYPE_PROCESSOR,
+    /** write-once device. */
+    SCSI_DEVTYPE_WORM,
+    /** CD/DVD device. */
+    SCSI_DEVTYPE_CDROM,
+    /** scanner device. */
+    SCSI_DEVTYPE_SCANNER,
+    /** optical memory device. */
+    SCSI_DEVTYPE_OPTICAL,
+    /** medium changer. */
+    SCSI_DEVTYPE_CHANGER,
+    /** communications device. */
+    SCSI_DEVTYPE_COMMUNICATION,
+    /** storage array controller device. */
+    SCSI_DEVTYPE_RAIDCTL = 0x0c,
+    /** enclosure services device. */
+    SCSI_DEVTYPE_ENCLOSURE,
+    /** simplified direct-access device. */
+    SCSI_DEVTYPE_SIMPLEDISK,
+    /** optical card reader/writer device. */
+    SCSI_DEVTYPE_OCRW,
+    /** bridge controller device. */
+    SCSI_DEVTYPE_BRIDGE,
+    /** object-based storage device. */
+    SCSI_DEVTYPE_OSD
+} SCSIDEVTYPE;
+
+/** Mask for extracting the SCSI device type out of the first byte of the INQUIRY response. */
+#define SCSI_DEVTYPE_MASK 0x1f
+
+/** Mask to extract the CmdQue bit out of the seventh byte of the INQUIRY response. */
+#define SCSI_INQUIRY_CMDQUE_MASK 0x02
+
+/** Maximum PDU payload size we can handle in one piece. Greater or equal than
+ * s_iscsiConfigDefaultWriteSplit. */
+#define ISCSI_DATA_LENGTH_MAX _256K
+
+/** Maximum PDU size we can handle in one piece. */
+#define ISCSI_RECV_PDU_BUFFER_SIZE (ISCSI_DATA_LENGTH_MAX + ISCSI_BHS_SIZE)
+
+
+/** Version of the iSCSI standard which this initiator driver can handle. */
+#define ISCSI_MY_VERSION 0
+
+
+/** Length of ISCSI basic header segment. */
+#define ISCSI_BHS_SIZE 48
+
+
+/** Reserved task tag value. */
+#define ISCSI_TASK_TAG_RSVD 0xffffffff
+
+
+/**
+ * iSCSI opcodes. */
+typedef enum ISCSIOPCODE
+{
+    /** NOP-Out. */
+    ISCSIOP_NOP_OUT = 0x00000000,
+    /** SCSI command. */
+    ISCSIOP_SCSI_CMD = 0x01000000,
+    /** SCSI task management request. */
+    ISCSIOP_SCSI_TASKMGMT_REQ = 0x02000000,
+    /** Login request. */
+    ISCSIOP_LOGIN_REQ = 0x03000000,
+    /** Text request. */
+    ISCSIOP_TEXT_REQ = 0x04000000,
+    /** SCSI Data-Out. */
+    ISCSIOP_SCSI_DATA_OUT = 0x05000000,
+    /** Logout request. */
+    ISCSIOP_LOGOUT_REQ = 0x06000000,
+    /** SNACK request. */
+    ISCSIOP_SNACK_REQ = 0x10000000,
+
+    /** NOP-In. */
+    ISCSIOP_NOP_IN = 0x20000000,
+    /** SCSI response. */
+    ISCSIOP_SCSI_RES = 0x21000000,
+    /** SCSI Task Management response. */
+    ISCSIOP_SCSI_TASKMGMT_RES = 0x22000000,
+    /** Login response. */
+    ISCSIOP_LOGIN_RES = 0x23000000,
+    /** Text response. */
+    ISCSIOP_TEXT_RES = 0x24000000,
+    /** SCSI Data-In. */
+    ISCSIOP_SCSI_DATA_IN = 0x25000000,
+    /** Logout response. */
+    ISCSIOP_LOGOUT_RES = 0x26000000,
+    /** Ready To Transfer (R2T). */
+    ISCSIOP_R2T = 0x31000000,
+    /** Asynchronous message. */
+    ISCSIOP_ASYN_MSG = 0x32000000,
+    /** Reject. */
+    ISCSIOP_REJECT = 0x3f000000
+} ISCSIOPCODE;
+
+/** Mask for extracting the iSCSI opcode out of the first header word. */
+#define ISCSIOP_MASK 0x3f000000
+
+
+/** ISCSI BHS word 0: Request should be processed immediately. */
+#define ISCSI_IMMEDIATE_DELIVERY_BIT 0x40000000
+
+/** ISCSI BHS word 0: This is the final PDU for this request/response. */
+#define ISCSI_FINAL_BIT 0x00800000
+/** ISCSI BHS word 0: Mask for extracting the CSG. */
+#define ISCSI_CSG_MASK 0x000c0000
+/** ISCSI BHS word 0: Shift offset for extracting the CSG. */
+#define ISCSI_CSG_SHIFT 18
+/** ISCSI BHS word 0: Mask for extracting the NSG. */
+#define ISCSI_NSG_MASK 0x00030000
+/** ISCSI BHS word 0: Shift offset for extracting the NSG. */
+#define ISCSI_NSG_SHIFT 16
+
+/** ISCSI BHS word 0: task attribute untagged */
+#define ISCSI_TASK_ATTR_UNTAGGED 0x00000000
+/** ISCSI BHS word 0: task attribute simple */
+#define ISCSI_TASK_ATTR_SIMPLE 0x00010000
+/** ISCSI BHS word 0: task attribute ordered */
+#define ISCSI_TASK_ATTR_ORDERED 0x00020000
+/** ISCSI BHS word 0: task attribute head of queue */
+#define ISCSI_TASK_ATTR_HOQ 0x00030000
+/** ISCSI BHS word 0: task attribute ACA */
+#define ISCSI_TASK_ATTR_ACA 0x00040000
+
+/** ISCSI BHS word 0: transit to next login phase. */
+#define ISCSI_TRANSIT_BIT 0x00800000
+/** ISCSI BHS word 0: continue with login negotiation. */
+#define ISCSI_CONTINUE_BIT 0x00400000
+
+/** ISCSI BHS word 0: residual underflow. */
+#define ISCSI_RESIDUAL_UNFL_BIT 0x00020000
+/** ISCSI BHS word 0: residual overflow. */
+#define ISCSI_RESIDUAL_OVFL_BIT 0x00040000
+/** ISCSI BHS word 0: Bidirectional read residual underflow. */
+#define ISCSI_BI_READ_RESIDUAL_UNFL_BIT 0x00080000
+/** ISCSI BHS word 0: Bidirectional read residual overflow. */
+#define ISCSI_BI_READ_RESIDUAL_OVFL_BIT 0x00100000
+
+/** ISCSI BHS word 0: SCSI response mask. */
+#define ISCSI_SCSI_RESPONSE_MASK 0x0000ff00
+/** ISCSI BHS word 0: SCSI status mask. */
+#define ISCSI_SCSI_STATUS_MASK 0x000000ff
+
+/** ISCSI BHS word 0: response includes status. */
+#define ISCSI_STATUS_BIT 0x00010000
+
+/** Maximum number of scatter/gather segments needed to send a PDU. */
+#define ISCSI_SG_SEGMENTS_MAX 4
+
+/** Number of entries in the command table. */
+#define ISCSI_CMD_WAITING_ENTRIES 32
+
+/**
+ * iSCSI login status class. */
+typedef enum ISCSILOGINSTATUSCLASS
+{
+    /** Success. */
+    ISCSI_LOGIN_STATUS_CLASS_SUCCESS = 0,
+    /** Redirection. */
+    ISCSI_LOGIN_STATUS_CLASS_REDIRECTION,
+    /** Initiator error. */
+    ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR,
+    /** Target error. */
+    ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR
+} ISCSILOGINSTATUSCLASS;
+
+
+/**
+ * iSCSI connection state. */
+typedef enum ISCSISTATE
+{
+    /** Not having a connection/session at all. */
+    ISCSISTATE_FREE,
+    /** Currently trying to login. */
+    ISCSISTATE_IN_LOGIN,
+    /** Normal operation, corresponds roughly to the Full Feature Phase. */
+    ISCSISTATE_NORMAL,
+    /** Currently trying to logout. */
+    ISCSISTATE_IN_LOGOUT
+} ISCSISTATE;
+
+/**
+ * iSCSI PDU send flags (and maybe more in the future). */
+typedef enum ISCSIPDUFLAGS
+{
+    /** No special flags */
+    ISCSIPDU_DEFAULT = 0,
+    /** Do not attempt to re-attach to the target if the connection is lost */
+    ISCSIPDU_NO_REATTACH = RT_BIT(1)
+} ISCSIPDUFLAGS;
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+
+/**
+ * iSCSI login negotiation parameter
+ */
+typedef struct ISCSIPARAMETER
+{
+    /** Name of the parameter. */
+    const char *pszParamName;
+    /** Value of the parameter. */
+    const char *pszParamValue;
+    /** Length of the binary parameter. 0=zero-terminated string. */
+    size_t cbParamValue;
+} ISCSIPARAMETER;
+
+
+/**
+ * iSCSI Response PDU buffer (scatter).
+ */
+typedef struct ISCSIRES
+{
+    /** Length of PDU segment. */
+    size_t cbSeg;
+    /** Pointer to PDU segment. */
+    void *pvSeg;
+} ISCSIRES;
+/** Pointer to an iSCSI Response PDU buffer. */
+typedef ISCSIRES *PISCSIRES;
+/** Pointer to a const iSCSI Response PDU buffer. */
+typedef ISCSIRES const *PCISCSIRES;
+
+
+/**
+ * iSCSI Request PDU buffer (gather).
+ */
+typedef struct ISCSIREQ
+{
+    /** Length of PDU segment in bytes. */
+    size_t cbSeg;
+    /** Pointer to PDU segment. */
+    const void *pcvSeg;
+} ISCSIREQ;
+/** Pointer to an iSCSI Request PDU buffer. */
+typedef ISCSIREQ *PISCSIREQ;
+/** Pointer to a const iSCSI Request PDU buffer. */
+typedef ISCSIREQ const *PCISCSIREQ;
+
+
+/**
+ * SCSI transfer directions.
+ */
+typedef enum SCSIXFER
+{
+    SCSIXFER_NONE = 0,
+    SCSIXFER_TO_TARGET,
+    SCSIXFER_FROM_TARGET,
+    SCSIXFER_TO_FROM_TARGET
+} SCSIXFER, *PSCSIXFER;
+
+/** Forward declaration. */
+typedef struct ISCSIIMAGE *PISCSIIMAGE;
+
+/**
+ * SCSI request structure.
+ */
+typedef struct SCSIREQ
+{
+    /** Transfer direction. */
+    SCSIXFER        enmXfer;
+    /** Length of command block. */
+    size_t          cbCDB;
+    /** Length of Initiator2Target data buffer. */
+    size_t          cbI2TData;
+    /** Length of Target2Initiator data buffer. */
+    size_t          cbT2IData;
+    /** Length of sense buffer
+     * This contains the number of sense bytes received upon completion. */
+    size_t          cbSense;
+    /** Completion status of the command. */
+    uint8_t         status;
+    /** Pointer to command block. */
+    void           *pvCDB;
+    /** Pointer to sense buffer. */
+    void           *pvSense;
+    /** Pointer to the Initiator2Target S/G list. */
+    PRTSGSEG        paI2TSegs;
+    /** Number of entries in the I2T S/G list. */
+    unsigned        cI2TSegs;
+    /** Pointer to the Target2Initiator S/G list. */
+    PRTSGSEG        paT2ISegs;
+    /** Number of entries in the T2I S/G list. */
+    unsigned        cT2ISegs;
+    /** S/G buffer for the target to initiator bits. */
+    RTSGBUF         SgBufT2I;
+} SCSIREQ, *PSCSIREQ;
+
+/**
+ * Async request structure holding all necessary data for
+ * request processing.
+ */
+typedef struct SCSIREQASYNC
+{
+    /** I/O context associated with this request. */
+    PVDIOCTX        pIoCtx;
+    /** Pointer to the SCSI request structure. */
+    PSCSIREQ        pScsiReq;
+    /** The CDB. */
+    uint8_t         abCDB[16];
+    /** The sense buffer. */
+    uint8_t         abSense[96];
+    /** Status code to return if we got sense data. */
+    int             rcSense;
+    /** Number of retries if the command completes with sense
+     * data before we return with an error.
+     */
+    unsigned        cSenseRetries;
+    /** The number of entries in the I2T S/G list. */
+    unsigned        cI2TSegs;
+    /** The number of entries in the T2I S/G list. */
+    unsigned        cT2ISegs;
+    /** The S/G list - variable in size.
+     * This array holds both the I2T and T2I segments.
+     * The I2T segments are first and the T2I are second.
+     */
+    RTSGSEG         aSegs[1];
+} SCSIREQASYNC, *PSCSIREQASYNC;
+
+typedef enum ISCSICMDTYPE
+{
+    /** Process a SCSI request. */
+    ISCSICMDTYPE_REQ = 0,
+    /** Call a function in the I/O thread. */
+    ISCSICMDTYPE_EXEC,
+    /** Usual 32bit hack. */
+    ISCSICMDTYPE_32BIT_HACK = 0x7fffffff
+} ISCSICMDTYPE;
+
+
+/** The command completion function. */
+typedef DECLCALLBACK(void) FNISCSICMDCOMPLETED(PISCSIIMAGE pImage, int rcReq, void *pvUser);
+/** Pointer to a command completion function. */
+typedef FNISCSICMDCOMPLETED *PFNISCSICMDCOMPLETED;
+
+/** The command execution function. */
+typedef DECLCALLBACK(int) FNISCSIEXEC(void *pvUser);
+/** Pointer to a command execution function. */
+typedef FNISCSIEXEC *PFNISCSIEXEC;
+
+/**
+ * Structure used to complete a synchronous request.
+ */
+typedef struct ISCSICMDSYNC
+{
+    /** Event semaphore to wakeup the waiting thread. */
+    RTSEMEVENT EventSem;
+    /** Status code of the command. */
+    int        rcCmd;
+} ISCSICMDSYNC, *PISCSICMDSYNC;
+
+/**
+ * iSCSI command.
+ * Used to forward requests to the I/O thread
+ * if existing.
+ */
+typedef struct ISCSICMD
+{
+    /** Next one in the list. */
+    struct ISCSICMD      *pNext;
+    /** Assigned ITT. */
+    uint32_t              Itt;
+    /** Completion callback. */
+    PFNISCSICMDCOMPLETED  pfnComplete;
+    /** Opaque user data. */
+    void                 *pvUser;
+    /** Command to execute. */
+    ISCSICMDTYPE          enmCmdType;
+    /** Command type dependent data. */
+    union
+    {
+        /** Process a SCSI request. */
+        struct
+        {
+            /** The SCSI request to process. */
+            PSCSIREQ              pScsiReq;
+        } ScsiReq;
+        /** Call a function in the I/O thread. */
+        struct
+        {
+            /** The method to execute. */
+            PFNISCSIEXEC          pfnExec;
+            /** User data. */
+            void                 *pvUser;
+        } Exec;
+    } CmdType;
+} ISCSICMD, *PISCSICMD;
+
+/**
+ *  Send iSCSI PDU.
+ *  Contains all necessary data to send a PDU.
+ */
+typedef struct ISCSIPDUTX
+{
+    /** Pointer to the next PDu to send. */
+    struct ISCSIPDUTX *pNext;
+    /** The BHS. */
+    uint32_t    aBHS[12];
+    /** Assigned CmdSN for this PDU. */
+    uint32_t    CmdSN;
+    /** The S/G buffer used for sending. */
+    RTSGBUF     SgBuf;
+    /** Number of bytes to send until the PDU completed. */
+    size_t      cbSgLeft;
+    /** The iSCSI command this PDU belongs to. */
+    PISCSICMD   pIScsiCmd;
+    /** Number of segments in the request segments array. */
+    unsigned    cISCSIReq;
+    /** The request segments - variable in size. */
+    RTSGSEG     aISCSIReq[1];
+} ISCSIPDUTX, *PISCSIPDUTX;
+
+/**
+ * Block driver instance data.
+ */
+typedef struct ISCSIIMAGE
+{
+    /** Pointer to the filename (location). Not really used. */
+    const char          *pszFilename;
+    /** Pointer to the initiator name. */
+    char                *pszInitiatorName;
+    /** Pointer to the target name. */
+    char                *pszTargetName;
+    /** Pointer to the target address. */
+    char                *pszTargetAddress;
+    /** Pointer to the user name for authenticating the Initiator. */
+    char                *pszInitiatorUsername;
+    /** Pointer to the secret for authenticating the Initiator. */
+    uint8_t             *pbInitiatorSecret;
+    /** Length of the secret for authenticating the Initiator. */
+    size_t              cbInitiatorSecret;
+    /** Pointer to the user name for authenticating the Target. */
+    char                *pszTargetUsername;
+    /** Pointer to the secret for authenticating the Initiator. */
+    uint8_t             *pbTargetSecret;
+    /** Length of the secret for authenticating the Initiator. */
+    size_t              cbTargetSecret;
+    /** Limit for iSCSI writes, essentially limiting the amount of data
+     * written in a single write. This is negotiated with the target, so
+     * the actual size might be smaller. */
+    uint32_t            cbWriteSplit;
+    /** Initiator session identifier. */
+    uint64_t            ISID;
+    /** SCSI Logical Unit Number. */
+    uint64_t            LUN;
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE        pVDIfsDisk;
+    /** Error interface. */
+    PVDINTERFACE        pInterfaceError;
+    /** Error interface callback table. */
+    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE        pVDIfsImage;
+    /** Config interface. */
+    PVDINTERFACE        pInterfaceConfig;
+    /** Config interface callback table. */
+    PVDINTERFACECONFIG  pInterfaceConfigCallbacks;
+    /** I/O interface. */
+    PVDINTERFACE        pInterfaceIo;
+    /** I/O interface callback table. */
+    PVDINTERFACEIOINT   pInterfaceIoCallbacks;
+    /** TCP network stack interface. */
+    PVDINTERFACE        pInterfaceNet;
+    /** TCP network stack interface callback table. */
+    PVDINTERFACETCPNET  pInterfaceNetCallbacks;
+    /** Image open flags. */
+    unsigned            uOpenFlags;
+    /** Number of re-login retries when a connection fails. */
+    uint32_t            cISCSIRetries;
+    /** Sector size on volume. */
+    uint32_t            cbSector;
+    /** Size of volume in sectors. */
+    uint64_t            cVolume;
+    /** Total volume size in bytes. Easier than multiplying the above values all the time. */
+    uint64_t            cbSize;
+
+    /** Negotiated maximum data length when sending to target. */
+    uint32_t            cbSendDataLength;
+    /** Negotiated maximum data length when receiving from target. */
+    uint32_t            cbRecvDataLength;
+
+    /** Current state of the connection/session. */
+    ISCSISTATE          state;
+    /** Flag whether the first Login Response PDU has been seen. */
+    bool                FirstRecvPDU;
+    /** Initiator Task Tag of the last iSCSI request PDU. */
+    uint32_t            ITT;
+    /** Sequence number of the last command. */
+    uint32_t            CmdSN;
+    /** Sequence number of the next command expected by the target. */
+    uint32_t            ExpCmdSN;
+    /** Maximum sequence number accepted by the target (determines size of window). */
+    uint32_t            MaxCmdSN;
+    /** Expected sequence number of next status. */
+    uint32_t            ExpStatSN;
+    /** Currently active request. */
+    PISCSIREQ           paCurrReq;
+    /** Segment number of currently active request. */
+    uint32_t            cnCurrReq;
+    /** Pointer to receive PDU buffer. (Freed by RT) */
+    void                *pvRecvPDUBuf;
+    /** Length of receive PDU buffer. */
+    size_t              cbRecvPDUBuf;
+    /** Mutex protecting against concurrent use from several threads. */
+    RTSEMMUTEX          Mutex;
+
+    /** Pointer to the target hostname. */
+    char                *pszHostname;
+    /** Pointer to the target hostname. */
+    uint32_t            uPort;
+    /** Socket handle of the TCP connection. */
+    VDSOCKET            Socket;
+    /** Timeout for read operations on the TCP connection (in milliseconds). */
+    uint32_t            uReadTimeout;
+    /** Flag whether to automatically generate the initiator name. */
+    bool                fAutomaticInitiatorName;
+    /** Flag whether to use the host IP stack or DevINIP. */
+    bool                fHostIP;
+
+    /** Head of request queue */
+    PISCSICMD           pScsiReqQueue;
+    /** Mutex protecting the request queue from concurrent access. */
+    RTSEMMUTEX          MutexReqQueue;
+    /** I/O thread. */
+    RTTHREAD            hThreadIo;
+    /** Flag whether the thread should be still running. */
+    volatile bool       fRunning;
+    /* Flag whether the target supports command queuing. */
+    bool                fCmdQueuingSupported;
+    /** Flag whether extended select is supported. */
+    bool                fExtendedSelectSupported;
+    /** Padding used for aligning the PDUs. */
+    uint8_t             aPadding[4];
+    /** Socket events to poll for. */
+    uint32_t            fPollEvents;
+    /** Number of bytes to read to complete the current PDU. */
+    size_t              cbRecvPDUResidual;
+    /** Current position in the PDU buffer. */
+    uint8_t             *pbRecvPDUBufCur;
+    /** Flag whether we are currently reading the BHS. */
+    bool                fRecvPDUBHS;
+    /** List of PDUs waiting to get transmitted. */
+    PISCSIPDUTX         pIScsiPDUTxHead;
+    /** Tail of PDUs waiting to get transmitted. */
+    PISCSIPDUTX         pIScsiPDUTxTail;
+    /** PDU we are currently transmitting. */
+    PISCSIPDUTX         pIScsiPDUTxCur;
+    /** Number of commands waiting for an answer from the target.
+     * Used for timeout handling for poll.
+     */
+    unsigned            cCmdsWaiting;
+    /** Table of commands waiting for a response from the target. */
+    PISCSICMD           aCmdsWaiting[ISCSI_CMD_WAITING_ENTRIES];
+} ISCSIIMAGE;
+
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** Default initiator basename. */
+static const char *s_iscsiDefaultInitiatorBasename = "iqn.2009-08.com.sun.virtualbox.initiator";
+
+/** Default LUN. */
+static const char *s_iscsiConfigDefaultLUN = "0";
+
+/** Default timeout, 10 seconds. */
+static const char *s_iscsiConfigDefaultTimeout = "10000";
+
+/** Default write split value, less or equal to ISCSI_DATA_LENGTH_MAX. */
+static const char *s_iscsiConfigDefaultWriteSplit = "262144";
+
+/** Default host IP stack. */
+static const char *s_iscsiConfigDefaultHostIPStack = "1";
+
+/** Description of all accepted config parameters. */
+static const VDCONFIGINFO s_iscsiConfigInfo[] =
+{
+    { "TargetName",         NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
+    /* LUN is defined of string type to handle the "enc" prefix. */
+    { "LUN",                s_iscsiConfigDefaultLUN,            VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
+    { "TargetAddress",      NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_MANDATORY },
+    { "InitiatorName",      NULL,                               VDCFGVALUETYPE_STRING,  0 },
+    { "InitiatorUsername",  NULL,                               VDCFGVALUETYPE_STRING,  0 },
+    { "InitiatorSecret",    NULL,                               VDCFGVALUETYPE_BYTES,   0 },
+    { "TargetUsername",     NULL,                               VDCFGVALUETYPE_STRING,  VD_CFGKEY_EXPERT },
+    { "TargetSecret",       NULL,                               VDCFGVALUETYPE_BYTES,   VD_CFGKEY_EXPERT },
+    { "WriteSplit",         s_iscsiConfigDefaultWriteSplit,     VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
+    { "Timeout",            s_iscsiConfigDefaultTimeout,        VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
+    { "HostIPStack",        s_iscsiConfigDefaultHostIPStack,    VDCFGVALUETYPE_INTEGER, VD_CFGKEY_EXPERT },
+    { NULL,                 NULL,                               VDCFGVALUETYPE_INTEGER, 0 }
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+/* iSCSI low-level functions (only to be used from the iSCSI high-level functions). */
+static uint32_t iscsiNewITT(PISCSIIMAGE pImage);
+static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq, uint32_t uFlags);
+static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes);
+static int iscsiRecvPDUAsync(PISCSIIMAGE pImage);
+static int iscsiSendPDUAsync(PISCSIIMAGE pImage);
+static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes);
+static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
+static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd);
+static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes);
+static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd);
+static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey, const char *pcszValue, size_t cbValue);
+static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue);
+static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue);
+static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf);
+
+/* Serial number arithmetic comparison. */
+static bool serial_number_less(uint32_t sn1, uint32_t sn2);
+static bool serial_number_greater(uint32_t sn1, uint32_t sn2);
+
+/* CHAP-MD5 functions. */
+#ifdef IMPLEMENT_TARGET_AUTH
+static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge);
+#endif
+static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
+                                      const uint8_t *pbSecret, size_t cbSecret);
+
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) iscsiError(PISCSIIMAGE pImage, int rc, RT_SRC_POS_DECL,
+                           const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+
+#ifdef LOG_ENABLED
+    va_start(va, pszFormat);
+    Log(("iscsiError(%d/%s): %N\n", iLine, pszFunction, pszFormat, &va));
+    va_end(va);
+#endif
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) iscsiMessage(PISCSIIMAGE pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+DECLINLINE(bool) iscsiIsClientConnected(PISCSIIMAGE pImage)
+{
+    return    pImage->Socket != NIL_VDSOCKET
+           && pImage->pInterfaceNetCallbacks->pfnIsClientConnected(pImage->Socket);
+}
+
+/**
+ * Calculates the hash for the given ITT used
+ * to look up the command in the table.
+ */
+DECLINLINE(uint32_t) iscsiIttHash(uint32_t Itt)
+{
+    return Itt % ISCSI_CMD_WAITING_ENTRIES;
+}
+
+static PISCSICMD iscsiCmdGetFromItt(PISCSIIMAGE pImage, uint32_t Itt)
+{
+    PISCSICMD pIScsiCmd = NULL;
+
+    pIScsiCmd = pImage->aCmdsWaiting[iscsiIttHash(Itt)];
+
+    while (   pIScsiCmd
+           && pIScsiCmd->Itt != Itt)
+        pIScsiCmd = pIScsiCmd->pNext;
+
+    return pIScsiCmd;
+}
+
+static void iscsiCmdInsert(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
+{
+    PISCSICMD pIScsiCmdOld;
+    uint32_t idx = iscsiIttHash(pIScsiCmd->Itt);
+
+    Assert(!pIScsiCmd->pNext);
+
+    pIScsiCmdOld = pImage->aCmdsWaiting[idx];
+    pIScsiCmd->pNext = pIScsiCmdOld;
+    pImage->aCmdsWaiting[idx] = pIScsiCmd;
+    pImage->cCmdsWaiting++;
+}
+
+static PISCSICMD iscsiCmdRemove(PISCSIIMAGE pImage, uint32_t Itt)
+{
+    PISCSICMD pIScsiCmd = NULL;
+    PISCSICMD pIScsiCmdPrev = NULL;
+    uint32_t idx = iscsiIttHash(Itt);
+
+    pIScsiCmd = pImage->aCmdsWaiting[idx];
+
+    while (   pIScsiCmd
+           && pIScsiCmd->Itt != Itt)
+    {
+        pIScsiCmdPrev = pIScsiCmd;
+        pIScsiCmd = pIScsiCmd->pNext;
+    }
+
+    if (pIScsiCmd)
+    {
+        if (pIScsiCmdPrev)
+        {
+            Assert(!pIScsiCmd->pNext || VALID_PTR(pIScsiCmd->pNext));
+            pIScsiCmdPrev->pNext = pIScsiCmd->pNext;
+        }
+        else
+        {
+            pImage->aCmdsWaiting[idx] = pIScsiCmd->pNext;
+            Assert(!pImage->aCmdsWaiting[idx] || VALID_PTR(pImage->aCmdsWaiting[idx]));
+        }
+        pImage->cCmdsWaiting--;
+    }
+
+    return pIScsiCmd;
+}
+
+/**
+ * Removes all commands from the table and returns the
+ * list head
+ *
+ * @returns Pointer to the head of teh command list.
+ * @param   pImage    iSCSI connection to use.
+ */
+static PISCSICMD iscsiCmdRemoveAll(PISCSIIMAGE pImage)
+{
+    PISCSICMD pIScsiCmdHead = NULL;
+
+    for (unsigned idx = 0; idx < RT_ELEMENTS(pImage->aCmdsWaiting); idx++)
+    {
+        PISCSICMD pHead;
+        PISCSICMD pTail;
+
+        pHead = pImage->aCmdsWaiting[idx];
+        pImage->aCmdsWaiting[idx] = NULL;
+
+        if (pHead)
+        {
+            /* Get the tail. */
+            pTail = pHead;
+            while (pTail->pNext)
+                pTail = pTail->pNext;
+
+            /* Concatenate. */
+            pTail->pNext = pIScsiCmdHead;
+            pIScsiCmdHead = pHead;
+        }
+    }
+    pImage->cCmdsWaiting = 0;
+
+    return pIScsiCmdHead;
+}
+
+static int iscsiTransportConnect(PISCSIIMAGE pImage)
+{
+    int rc;
+    if (!pImage->pszHostname)
+        return VERR_NET_DEST_ADDRESS_REQUIRED;
+
+    rc = pImage->pInterfaceNetCallbacks->pfnClientConnect(pImage->Socket, pImage->pszHostname, pImage->uPort);
+    if (RT_FAILURE(rc))
+    {
+        if (   rc == VERR_NET_CONNECTION_REFUSED
+            || rc == VERR_NET_CONNECTION_RESET
+            || rc == VERR_NET_UNREACHABLE
+            || rc == VERR_NET_HOST_UNREACHABLE
+            || rc == VERR_NET_CONNECTION_TIMED_OUT)
+        {
+            /* Standardize return value for no connection. */
+            rc = VERR_NET_CONNECTION_REFUSED;
+        }
+        return rc;
+    }
+
+    /* Disable Nagle algorithm, we want things to be sent immediately. */
+    pImage->pInterfaceNetCallbacks->pfnSetSendCoalescing(pImage->Socket, false);
+
+    /* Make initiator name and ISID unique on this host. */
+    RTNETADDR LocalAddr;
+    rc = pImage->pInterfaceNetCallbacks->pfnGetLocalAddress(pImage->Socket,
+                                                            &LocalAddr);
+    if (RT_FAILURE(rc))
+        return rc;
+    if (   LocalAddr.uPort == RTNETADDR_PORT_NA
+        || LocalAddr.uPort > 65535)
+        return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
+    pImage->ISID &= ~65535ULL;
+    pImage->ISID |= LocalAddr.uPort;
+    /* Eliminate the port so that it isn't included below. */
+    LocalAddr.uPort = RTNETADDR_PORT_NA;
+    if (pImage->fAutomaticInitiatorName)
+    {
+        if (pImage->pszInitiatorName)
+            RTStrFree(pImage->pszInitiatorName);
+        RTStrAPrintf(&pImage->pszInitiatorName, "%s:01:%RTnaddr",
+                     s_iscsiDefaultInitiatorBasename, &LocalAddr);
+        if (!pImage->pszInitiatorName)
+            return VERR_NO_MEMORY;
+    }
+    return VINF_SUCCESS;
+}
+
+
+static int iscsiTransportRead(PISCSIIMAGE pImage, PISCSIRES paResponse, unsigned int cnResponse)
+{
+    int rc = VINF_SUCCESS;
+    unsigned int i = 0;
+    size_t cbToRead, cbActuallyRead, residual, cbSegActual = 0, cbAHSLength, cbDataLength;
+    char *pDst;
+
+    LogFlowFunc(("cnResponse=%d (%s:%d)\n", cnResponse, pImage->pszHostname, pImage->uPort));
+    if (!iscsiIsClientConnected(pImage))
+    {
+        /* Reconnecting makes no sense in this case, as there will be nothing
+         * to receive. We would just run into a timeout. */
+        rc = VERR_BROKEN_PIPE;
+    }
+
+    if (RT_SUCCESS(rc) && paResponse[0].cbSeg >= ISCSI_BHS_SIZE)
+    {
+        cbToRead = 0;
+        residual = ISCSI_BHS_SIZE;  /* Do not read more than the BHS length before the true PDU length is known. */
+        cbSegActual = residual;
+        pDst = (char *)paResponse[i].pvSeg;
+        uint64_t u64Timeout = RTTimeMilliTS() + pImage->uReadTimeout;
+        do
+        {
+            int64_t cMilliesRemaining = u64Timeout - RTTimeMilliTS();
+            if (cMilliesRemaining <= 0)
+            {
+                rc = VERR_TIMEOUT;
+                break;
+            }
+            Assert(cMilliesRemaining < 1000000);
+            rc = pImage->pInterfaceNetCallbacks->pfnSelectOne(pImage->Socket,
+                                                              cMilliesRemaining);
+            if (RT_FAILURE(rc))
+                break;
+            rc = pImage->pInterfaceNetCallbacks->pfnRead(pImage->Socket,
+                                                         pDst, residual,
+                                                         &cbActuallyRead);
+            if (RT_FAILURE(rc))
+                break;
+            if (cbActuallyRead == 0)
+            {
+                /* The other end has closed the connection. */
+                pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
+                pImage->state = ISCSISTATE_FREE;
+                rc = VERR_NET_CONNECTION_RESET;
+                break;
+            }
+            if (cbToRead == 0)
+            {
+                /* Currently reading the BHS. */
+                residual -= cbActuallyRead;
+                pDst += cbActuallyRead;
+                if (residual <= 40)
+                {
+                    /* Enough data read to figure out the actual PDU size. */
+                    uint32_t word1 = RT_N2H_U32(((uint32_t *)(paResponse[0].pvSeg))[1]);
+                    cbAHSLength = (word1 & 0xff000000) >> 24;
+                    cbAHSLength = ((cbAHSLength - 1) | 3) + 1;      /* Add padding. */
+                    cbDataLength = word1 & 0x00ffffff;
+                    cbDataLength = ((cbDataLength - 1) | 3) + 1;    /* Add padding. */
+                    cbToRead = residual + cbAHSLength + cbDataLength;
+                    residual += paResponse[0].cbSeg - ISCSI_BHS_SIZE;
+                    if (residual > cbToRead)
+                        residual = cbToRead;
+                    cbSegActual = ISCSI_BHS_SIZE + cbAHSLength + cbDataLength;
+                    /* Check whether we are already done with this PDU (no payload). */
+                    if (cbToRead == 0)
+                        break;
+                }
+            }
+            else
+            {
+                cbToRead -= cbActuallyRead;
+                if (cbToRead == 0)
+                    break;
+                pDst += cbActuallyRead;
+                residual -= cbActuallyRead;
+            }
+            if (residual == 0)
+            {
+                i++;
+                if (i >= cnResponse)
+                {
+                    /* No space left in receive buffers. */
+                    rc = VERR_BUFFER_OVERFLOW;
+                    break;
+                }
+                pDst = (char *)paResponse[i].pvSeg;
+                residual = paResponse[i].cbSeg;
+                if (residual > cbToRead)
+                    residual = cbToRead;
+                cbSegActual = residual;
+            }
+            LogFlowFunc(("cbToRead=%u residual=%u cbSegActual=%u cbActuallRead=%u\n",
+                         cbToRead, residual, cbSegActual, cbActuallyRead));
+        } while (true);
+    }
+    else
+    {
+        if (RT_SUCCESS(rc))
+            rc = VERR_BUFFER_OVERFLOW;
+    }
+    if (RT_SUCCESS(rc))
+    {
+        paResponse[i].cbSeg = cbSegActual;
+        for (i++; i < cnResponse; i++)
+            paResponse[i].cbSeg = 0;
+    }
+
+    if (RT_UNLIKELY(    RT_FAILURE(rc)
+                    && (   rc == VERR_NET_CONNECTION_RESET
+                        || rc == VERR_NET_CONNECTION_ABORTED
+                        || rc == VERR_NET_CONNECTION_RESET_BY_PEER
+                        || rc == VERR_NET_CONNECTION_REFUSED
+                        || rc == VERR_BROKEN_PIPE)))
+    {
+        /* Standardize return value for broken connection. */
+        rc = VERR_BROKEN_PIPE;
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+static int iscsiTransportWrite(PISCSIIMAGE pImage, PISCSIREQ paRequest, unsigned int cnRequest)
+{
+    int rc = VINF_SUCCESS;
+    uint32_t pad = 0;
+    unsigned int i;
+
+    LogFlowFunc(("cnRequest=%d (%s:%d)\n", cnRequest, pImage->pszHostname, pImage->uPort));
+    if (!iscsiIsClientConnected(pImage))
+    {
+        /* Attempt to reconnect if the connection was previously broken. */
+        rc = iscsiTransportConnect(pImage);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /* Construct scatter/gather buffer for entire request, worst case
+         * needs twice as many entries to allow for padding. */
+        unsigned cBuf = 0;
+        for (i = 0; i < cnRequest; i++)
+        {
+            cBuf++;
+            if (paRequest[i].cbSeg & 3)
+                cBuf++;
+        }
+        Assert(cBuf < ISCSI_SG_SEGMENTS_MAX);
+        RTSGBUF buf;
+        RTSGSEG aSeg[ISCSI_SG_SEGMENTS_MAX];
+        static char aPad[4] = { 0, 0, 0, 0 };
+        RTSgBufInit(&buf, &aSeg[0], cBuf);
+        unsigned iBuf = 0;
+        for (i = 0; i < cnRequest; i++)
+        {
+            /* Actual data chunk. */
+            aSeg[iBuf].pvSeg = (void *)paRequest[i].pcvSeg;
+            aSeg[iBuf].cbSeg = paRequest[i].cbSeg;
+            iBuf++;
+            /* Insert proper padding before the next chunk. */
+            if (paRequest[i].cbSeg & 3)
+            {
+                aSeg[iBuf].pvSeg = &aPad[0];
+                aSeg[iBuf].cbSeg = 4 - (paRequest[i].cbSeg & 3);
+                iBuf++;
+            }
+        }
+        /* Send out the request, the socket is set to send data immediately,
+         * avoiding unnecessary delays. */
+        rc = pImage->pInterfaceNetCallbacks->pfnSgWrite(pImage->Socket, &buf);
+
+    }
+
+    if (RT_UNLIKELY(    RT_FAILURE(rc)
+                    && (   rc == VERR_NET_CONNECTION_RESET
+                        || rc == VERR_NET_CONNECTION_ABORTED
+                        || rc == VERR_NET_CONNECTION_RESET_BY_PEER
+                        || rc == VERR_NET_CONNECTION_REFUSED
+                        || rc == VERR_BROKEN_PIPE)))
+    {
+        /* Standardize return value for broken connection. */
+        rc = VERR_BROKEN_PIPE;
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+static int iscsiTransportOpen(PISCSIIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+    size_t cbHostname = 0; /* shut up gcc */
+    const char *pcszPort = NULL; /* shut up gcc */
+    char *pszPortEnd;
+    uint16_t uPort;
+
+    /* Clean up previous connection data. */
+    if (iscsiIsClientConnected(pImage))
+    {
+        pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
+    }
+    if (pImage->pszHostname)
+    {
+        RTMemFree(pImage->pszHostname);
+        pImage->pszHostname = NULL;
+        pImage->uPort = 0;
+    }
+
+    /* Locate the port number via the colon separating the hostname from the port. */
+    if (*pImage->pszTargetAddress)
+    {
+        if (*pImage->pszTargetAddress != '[')
+        {
+            /* Normal hostname or IPv4 dotted decimal. */
+            pcszPort = strchr(pImage->pszTargetAddress, ':');
+            if (pcszPort != NULL)
+            {
+                cbHostname = pcszPort - pImage->pszTargetAddress;
+                pcszPort++;
+            }
+            else
+                cbHostname = strlen(pImage->pszTargetAddress);
+        }
+        else
+        {
+            /* IPv6 literal address. Contains colons, so skip to closing square bracket. */
+            pcszPort = strchr(pImage->pszTargetAddress, ']');
+            if (pcszPort != NULL)
+            {
+                pcszPort++;
+                cbHostname = pcszPort - pImage->pszTargetAddress;
+                if (*pcszPort == '\0')
+                    pcszPort = NULL;
+                else if (*pcszPort != ':')
+                    rc = VERR_PARSE_ERROR;
+                else
+                    pcszPort++;
+            }
+            else
+                rc = VERR_PARSE_ERROR;
+        }
+    }
+    else
+        rc = VERR_PARSE_ERROR;
+
+    /* Now split address into hostname and port. */
+    if (RT_SUCCESS(rc))
+    {
+        pImage->pszHostname = (char *)RTMemAlloc(cbHostname + 1);
+        if (!pImage->pszHostname)
+            rc = VERR_NO_MEMORY;
+        else
+        {
+            memcpy(pImage->pszHostname, pImage->pszTargetAddress, cbHostname);
+            pImage->pszHostname[cbHostname] = '\0';
+            if (pcszPort != NULL)
+            {
+                rc = RTStrToUInt16Ex(pcszPort, &pszPortEnd, 0, &uPort);
+                /* Note that RT_SUCCESS() macro to check the rc value is not strict enough in this case. */
+                if (rc == VINF_SUCCESS && *pszPortEnd == '\0' && uPort != 0)
+                {
+                    pImage->uPort = uPort;
+                }
+                else
+                {
+                    rc = VERR_PARSE_ERROR;
+                }
+            }
+            else
+                pImage->uPort = ISCSI_DEFAULT_PORT;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (!iscsiIsClientConnected(pImage))
+            rc = iscsiTransportConnect(pImage);
+    }
+    else
+    {
+        if (pImage->pszHostname)
+        {
+            RTMemFree(pImage->pszHostname);
+            pImage->pszHostname = NULL;
+        }
+        pImage->uPort = 0;
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+static int iscsiTransportClose(PISCSIIMAGE pImage)
+{
+    int rc;
+
+    LogFlowFunc(("(%s:%d)\n", pImage->pszHostname, pImage->uPort));
+    if (iscsiIsClientConnected(pImage))
+    {
+        rc = pImage->pInterfaceNetCallbacks->pfnClientClose(pImage->Socket);
+    }
+    else
+        rc = VINF_SUCCESS;
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Attach to an iSCSI target. Performs all operations necessary to enter
+ * Full Feature Phase.
+ *
+ * @returns VBox status.
+ * @param   pImage      The iSCSI connection state to be used.
+ */
+static int iscsiAttach(void *pvUser)
+{
+    int rc;
+    uint32_t itt;
+    uint32_t csg, nsg, substate;
+    uint64_t isid_tsih;
+    uint8_t bBuf[4096];         /* Should be large enough even for large authentication values. */
+    size_t cbBuf;
+    bool transit;
+    uint8_t pbChallenge[1024];  /* RFC3720 specifies this as maximum. */
+    size_t cbChallenge = 0;     /* shut up gcc */
+    uint8_t bChapIdx;
+    uint8_t aResponse[RTMD5HASHSIZE];
+    uint32_t cnISCSIReq;
+    ISCSIREQ aISCSIReq[4];
+    uint32_t aReqBHS[12];
+    uint32_t cnISCSIRes;
+    ISCSIRES aISCSIRes[2];
+    uint32_t aResBHS[12];
+    char *pszNext;
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
+
+    bool fParameterNeg = true;;
+    pImage->cbRecvDataLength = ISCSI_DATA_LENGTH_MAX;
+    pImage->cbSendDataLength = RT_MIN(ISCSI_DATA_LENGTH_MAX, pImage->cbWriteSplit);
+    char szMaxDataLength[16];
+    RTStrPrintf(szMaxDataLength, sizeof(szMaxDataLength), "%u", ISCSI_DATA_LENGTH_MAX);
+    ISCSIPARAMETER aParameterNeg[] =
+    {
+        { "HeaderDigest", "None", 0 },
+        { "DataDigest", "None", 0 },
+        { "MaxConnections", "1", 0 },
+        { "InitialR2T", "No", 0 },
+        { "ImmediateData", "Yes", 0 },
+        { "MaxRecvDataSegmentLength", szMaxDataLength, 0 },
+        { "MaxBurstLength", szMaxDataLength, 0 },
+        { "FirstBurstLength", szMaxDataLength, 0 },
+        { "DefaultTime2Wait", "0", 0 },
+        { "DefaultTime2Retain", "60", 0 },
+        { "DataPDUInOrder", "Yes", 0 },
+        { "DataSequenceInOrder", "Yes", 0 },
+        { "ErrorRecoveryLevel", "0", 0 },
+        { "MaxOutstandingR2T", "1", 0 }
+    };
+
+    LogFlowFunc(("entering\n"));
+
+    Assert(pImage->state == ISCSISTATE_FREE);
+
+    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
+
+    /* Make 100% sure the connection isn't reused for a new login. */
+    iscsiTransportClose(pImage);
+
+restart:
+    if (!iscsiIsClientConnected(pImage))
+    {
+        rc = iscsiTransportOpen(pImage);
+        if (RT_FAILURE(rc))
+            goto out;
+    }
+
+    pImage->state = ISCSISTATE_IN_LOGIN;
+    pImage->ITT = 1;
+    pImage->FirstRecvPDU = true;
+    pImage->CmdSN = 1;
+    pImage->ExpCmdSN = 0;
+    pImage->MaxCmdSN = 1;
+    pImage->ExpStatSN = 0;
+
+    /*
+     * Send login request to target.
+     */
+    itt = iscsiNewITT(pImage);
+    csg = 0;
+    nsg = 0;
+    substate = 0;
+    isid_tsih = pImage->ISID << 16;  /* TSIH field currently always 0 */
+
+    do {
+        transit = false;
+        cbBuf = 0;
+        /* Handle all cases with a single switch statement. */
+        switch (csg << 8 | substate)
+        {
+            case 0x0000:    /* security negotiation, step 0: propose authentication. */
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "SessionType", "Normal", 0);
+                if (RT_FAILURE(rc))
+                    goto out;
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "InitiatorName", pImage->pszInitiatorName, 0);
+                if (RT_FAILURE(rc))
+                    goto out;
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "TargetName", pImage->pszTargetName, 0);
+                if (RT_FAILURE(rc))
+                    goto out;
+                if (pImage->pszInitiatorUsername == NULL)
+                {
+                    /* No authentication. Immediately switch to next phase. */
+                    rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "None", 0);
+                    if (RT_FAILURE(rc))
+                        goto out;
+                    nsg = 1;
+                    transit = true;
+                }
+                else
+                {
+                    rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "AuthMethod", "CHAP,None", 0);
+                    if (RT_FAILURE(rc))
+                        goto out;
+                }
+                break;
+            case 0x0001:    /* security negotiation, step 1: propose CHAP_MD5 variant. */
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_A", "5", 0);
+                if (RT_FAILURE(rc))
+                    goto out;
+                break;
+            case 0x0002:    /* security negotiation, step 2: send authentication info. */
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_N", pImage->pszInitiatorUsername, 0);
+                if (RT_FAILURE(rc))
+                    goto out;
+                chap_md5_compute_response(aResponse, bChapIdx, pbChallenge, cbChallenge,
+                                          pImage->pbInitiatorSecret, pImage->cbInitiatorSecret);
+                rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf, "CHAP_R", (const char *)aResponse, RTMD5HASHSIZE);
+                if (RT_FAILURE(rc))
+                    goto out;
+                nsg = 1;
+                transit = true;
+                break;
+            case 0x0100:    /* login operational negotiation, step 0: set parameters. */
+                if (fParameterNeg)
+                {
+                    for (unsigned i = 0; i < RT_ELEMENTS(aParameterNeg); i++)
+                    {
+                        rc = iscsiTextAddKeyValue(bBuf, sizeof(bBuf), &cbBuf,
+                                                  aParameterNeg[i].pszParamName,
+                                                  aParameterNeg[i].pszParamValue,
+                                                  aParameterNeg[i].cbParamValue);
+                        if (RT_FAILURE(rc))
+                            goto out;
+                    }
+                    fParameterNeg = false;
+                }
+
+                nsg = 3;
+                transit = true;
+                break;
+            case 0x0300:    /* full feature phase. */
+            default:
+                /* Should never come here. */
+                AssertMsgFailed(("send: Undefined login state %d substate %d\n", csg, substate));
+                break;
+        }
+
+        aReqBHS[0] = RT_H2N_U32(    ISCSI_IMMEDIATE_DELIVERY_BIT
+                                |   (csg << ISCSI_CSG_SHIFT)
+                                |   (transit ? (nsg << ISCSI_NSG_SHIFT | ISCSI_TRANSIT_BIT) : 0)
+                                |   ISCSI_MY_VERSION            /* Minimum version. */
+                                |   (ISCSI_MY_VERSION << 8)     /* Maximum version. */
+                                |   ISCSIOP_LOGIN_REQ);     /* C=0 */
+        aReqBHS[1] = RT_H2N_U32((uint32_t)cbBuf);     /* TotalAHSLength=0 */
+        aReqBHS[2] = RT_H2N_U32(isid_tsih >> 32);
+        aReqBHS[3] = RT_H2N_U32(isid_tsih & 0xffffffff);
+        aReqBHS[4] = itt;
+        aReqBHS[5] = RT_H2N_U32(1 << 16);   /* CID=1,reserved */
+        aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+        aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+        aReqBHS[8] = 0;             /* reserved */
+        aReqBHS[9] = 0;             /* reserved */
+        aReqBHS[10] = 0;            /* reserved */
+        aReqBHS[11] = 0;            /* reserved */
+
+        cnISCSIReq = 0;
+        aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
+        aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
+        cnISCSIReq++;
+
+        aISCSIReq[cnISCSIReq].pcvSeg = bBuf;
+        aISCSIReq[cnISCSIReq].cbSeg = cbBuf;
+        cnISCSIReq++;
+
+        rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
+        if (RT_SUCCESS(rc))
+        {
+            ISCSIOPCODE cmd;
+            ISCSILOGINSTATUSCLASS loginStatusClass;
+
+            cnISCSIRes = 0;
+            aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
+            aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
+            cnISCSIRes++;
+            aISCSIRes[cnISCSIRes].pvSeg = bBuf;
+            aISCSIRes[cnISCSIRes].cbSeg = sizeof(bBuf);
+            cnISCSIRes++;
+
+            rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
+            if (RT_FAILURE(rc))
+                break;
+            /** @todo collect partial login responses with Continue bit set. */
+            Assert(aISCSIRes[0].pvSeg == aResBHS);
+            Assert(aISCSIRes[0].cbSeg >= ISCSI_BHS_SIZE);
+            Assert((RT_N2H_U32(aResBHS[0]) & ISCSI_CONTINUE_BIT) == 0);
+
+            cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
+            if (cmd == ISCSIOP_LOGIN_RES)
+            {
+                if ((RT_N2H_U32(aResBHS[0]) & 0xff) != ISCSI_MY_VERSION)
+                {
+                    iscsiTransportClose(pImage);
+                    rc = VERR_PARSE_ERROR;
+                    break;  /* Give up immediately, as a RFC violation in version fields is very serious. */
+                }
+
+                loginStatusClass = (ISCSILOGINSTATUSCLASS)(RT_N2H_U32(aResBHS[9]) >> 24);
+                switch (loginStatusClass)
+                {
+                    case ISCSI_LOGIN_STATUS_CLASS_SUCCESS:
+                        uint32_t targetCSG;
+                        uint32_t targetNSG;
+                        bool targetTransit;
+
+                        if (pImage->FirstRecvPDU)
+                        {
+                            pImage->FirstRecvPDU = false;
+                            pImage->ExpStatSN = RT_N2H_U32(aResBHS[6]) + 1;
+                        }
+
+                        targetCSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_CSG_MASK) >> ISCSI_CSG_SHIFT;
+                        targetNSG = (RT_N2H_U32(aResBHS[0]) & ISCSI_NSG_MASK) >> ISCSI_NSG_SHIFT;
+                        targetTransit = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_TRANSIT_BIT);
+
+                        /* Handle all cases with a single switch statement. */
+                        switch (csg << 8 | substate)
+                        {
+                            case 0x0000:    /* security negotiation, step 0: receive final authentication. */
+                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
+                                if (RT_FAILURE(rc))
+                                    break;
+
+                                const char *pcszAuthMethod;
+
+                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "AuthMethod", &pcszAuthMethod);
+                                if (RT_FAILURE(rc))
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                if (strcmp(pcszAuthMethod, "None") == 0)
+                                {
+                                    /* Authentication offered, but none required.  Skip to operational parameters. */
+                                    csg = 1;
+                                    nsg = 1;
+                                    transit = true;
+                                    substate = 0;
+                                    break;
+                                }
+                                else if (strcmp(pcszAuthMethod, "CHAP") == 0 && targetNSG == 0 && !targetTransit)
+                                {
+                                    /* CHAP authentication required, continue with next substate. */
+                                    substate++;
+                                    break;
+                                }
+
+                                /* Unknown auth method or login response PDU headers incorrect. */
+                                rc = VERR_PARSE_ERROR;
+                                break;
+                            case 0x0001:    /* security negotiation, step 1: receive final CHAP variant and challenge. */
+                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
+                                if (RT_FAILURE(rc))
+                                    break;
+
+                                const char *pcszChapAuthMethod;
+                                const char *pcszChapIdxTarget;
+                                const char *pcszChapChallengeStr;
+
+                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_A", &pcszChapAuthMethod);
+                                if (RT_FAILURE(rc))
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                if (strcmp(pcszChapAuthMethod, "5") != 0)
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_I", &pcszChapIdxTarget);
+                                if (RT_FAILURE(rc))
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                rc = RTStrToUInt8Ex(pcszChapIdxTarget, &pszNext, 0, &bChapIdx);
+                                if ((rc > VINF_SUCCESS) || *pszNext != '\0')
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "CHAP_C", &pcszChapChallengeStr);
+                                if (RT_FAILURE(rc))
+                                {
+                                    rc = VERR_PARSE_ERROR;
+                                    break;
+                                }
+                                cbChallenge = sizeof(pbChallenge);
+                                rc = iscsiStrToBinary(pcszChapChallengeStr, pbChallenge, &cbChallenge);
+                                if (RT_FAILURE(rc))
+                                    break;
+                                substate++;
+                                transit = true;
+                                break;
+                            case 0x0002:    /* security negotiation, step 2: check authentication success. */
+                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
+                                if (RT_FAILURE(rc))
+                                    break;
+
+                                if (targetCSG == 0 && targetNSG == 1 && targetTransit)
+                                {
+                                    /* Target wants to continue in login operational state, authentication success. */
+                                    csg = 1;
+                                    nsg = 3;
+                                    substate = 0;
+                                    break;
+                                }
+                                rc = VERR_PARSE_ERROR;
+                                break;
+                            case 0x0100:    /* login operational negotiation, step 0: check results. */
+                                rc = iscsiUpdateParameters(pImage, bBuf, aISCSIRes[1].cbSeg);
+                                if (RT_FAILURE(rc))
+                                    break;
+
+                                if (targetCSG == 1 && targetNSG == 3 && targetTransit)
+                                {
+                                    /* Target wants to continue in full feature phase, login finished. */
+                                    csg = 3;
+                                    nsg = 3;
+                                    substate = 0;
+                                    break;
+                                }
+                                else if (targetCSG == 1 && targetNSG == 1 && !targetTransit)
+                                {
+                                    /* Target wants to negotiate certain parameters and
+                                     * stay in login operational negotiation. */
+                                    csg = 1;
+                                    nsg = 3;
+                                    substate = 0;
+                                }
+                                rc = VERR_PARSE_ERROR;
+                                break;
+                            case 0x0300:    /* full feature phase. */
+                            default:
+                                AssertMsgFailed(("recv: Undefined login state %d substate %d\n", csg, substate));
+                                rc = VERR_PARSE_ERROR;
+                                break;
+                        }
+                        break;
+                    case ISCSI_LOGIN_STATUS_CLASS_REDIRECTION:
+                        const char *pcszTargetRedir;
+
+                        /* Target has moved to some other location, as indicated in the TargetAddress key. */
+                        rc = iscsiTextGetKeyValue(bBuf, aISCSIRes[1].cbSeg, "TargetAddress", &pcszTargetRedir);
+                        if (RT_FAILURE(rc))
+                        {
+                            rc = VERR_PARSE_ERROR;
+                            break;
+                        }
+                        if (pImage->pszTargetAddress)
+                            RTMemFree(pImage->pszTargetAddress);
+                        {
+                            size_t cb = strlen(pcszTargetRedir) + 1;
+                            pImage->pszTargetAddress = (char *)RTMemAlloc(cb);
+                            if (!pImage->pszTargetAddress)
+                            {
+                                rc = VERR_NO_MEMORY;
+                                break;
+                            }
+                            memcpy(pImage->pszTargetAddress, pcszTargetRedir, cb);
+                        }
+                        rc = iscsiTransportOpen(pImage);
+                        goto restart;
+                    case ISCSI_LOGIN_STATUS_CLASS_INITIATOR_ERROR:
+                        iscsiTransportClose(pImage);
+                        rc = VERR_IO_GEN_FAILURE;
+                        goto out;
+                    case ISCSI_LOGIN_STATUS_CLASS_TARGET_ERROR:
+                        iscsiTransportClose(pImage);
+                        rc = VINF_EOF;
+                        break;
+                    default:
+                        rc = VERR_PARSE_ERROR;
+                }
+
+                if (csg == 3)
+                {
+                    /*
+                     * Finished login, continuing with Full Feature Phase.
+                     */
+                    rc = VINF_SUCCESS;
+                    break;
+                }
+            }
+            else
+            {
+                AssertMsgFailed(("%s: ignoring unexpected PDU with first word = %#08x\n", __FUNCTION__, RT_N2H_U32(aResBHS[0])));
+            }
+        }
+        else
+            break;
+    } while (true);
+
+out:
+    if (RT_FAILURE(rc))
+    {
+        /*
+         * Close connection to target.
+         */
+        iscsiTransportClose(pImage);
+        pImage->state = ISCSISTATE_FREE;
+    }
+    else
+        pImage->state = ISCSISTATE_NORMAL;
+
+    RTSemMutexRelease(pImage->Mutex);
+
+    LogFlowFunc(("returning %Rrc\n", rc));
+    LogRel(("iSCSI: login to target %s %s\n", pImage->pszTargetName, RT_SUCCESS(rc) ? "successful" : "failed"));
+    return rc;
+}
+
+
+/**
+ * Detach from an iSCSI target.
+ *
+ * @returns VBox status.
+ * @param   pImage      The iSCSI connection state to be used.
+ */
+static int iscsiDetach(void *pvUser)
+{
+    int rc;
+    uint32_t itt;
+    uint32_t cnISCSIReq = 0;
+    ISCSIREQ aISCSIReq[4];
+    uint32_t aReqBHS[12];
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
+
+    LogFlowFunc(("entering\n"));
+
+    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
+
+    if (pImage->state != ISCSISTATE_FREE && pImage->state != ISCSISTATE_IN_LOGOUT)
+    {
+        pImage->state = ISCSISTATE_IN_LOGOUT;
+
+        /*
+         * Send logout request to target.
+         */
+        itt = iscsiNewITT(pImage);
+        aReqBHS[0] = RT_H2N_U32(ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_REQ);  /* I=0,F=1,Reason=close session */
+        aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
+        aReqBHS[2] = 0;             /* reserved */
+        aReqBHS[3] = 0;             /* reserved */
+        aReqBHS[4] = itt;
+        aReqBHS[5] = 0;             /* reserved */
+        aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+        aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+        aReqBHS[8] = 0;             /* reserved */
+        aReqBHS[9] = 0;             /* reserved */
+        aReqBHS[10] = 0;            /* reserved */
+        aReqBHS[11] = 0;            /* reserved */
+        pImage->CmdSN++;
+
+        aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
+        aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
+        cnISCSIReq++;
+
+        rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Read logout response from target.
+             */
+            ISCSIRES aISCSIRes;
+            uint32_t aResBHS[12];
+
+            aISCSIRes.pvSeg = aResBHS;
+            aISCSIRes.cbSeg = sizeof(aResBHS);
+            rc = iscsiRecvPDU(pImage, itt, &aISCSIRes, 1);
+            if (RT_SUCCESS(rc))
+            {
+                if (RT_N2H_U32(aResBHS[0]) != (ISCSI_FINAL_BIT | ISCSIOP_LOGOUT_RES))
+                    AssertMsgFailed(("iSCSI Logout response invalid\n"));
+            }
+            else
+                AssertMsgFailed(("iSCSI Logout response error, rc=%Rrc\n", rc));
+        }
+        else
+            AssertMsgFailed(("Could not send iSCSI Logout request, rc=%Rrc\n", rc));
+    }
+
+    if (pImage->state != ISCSISTATE_FREE)
+    {
+        /*
+         * Close connection to target.
+         */
+        rc = iscsiTransportClose(pImage);
+        if (RT_FAILURE(rc))
+            AssertMsgFailed(("Could not close connection to target, rc=%Rrc\n", rc));
+    }
+
+    pImage->state = ISCSISTATE_FREE;
+
+    RTSemMutexRelease(pImage->Mutex);
+
+    LogFlowFunc(("leaving\n"));
+    LogRel(("iSCSI: logout to target %s\n", pImage->pszTargetName));
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Perform a command on an iSCSI target. Target must be already in
+ * Full Feature Phase.
+ *
+ * @returns VBOX status.
+ * @param   pImage      The iSCSI connection state to be used.
+ * @param   pRequest    Command descriptor. Contains all information about
+ *                      the command, its transfer directions and pointers
+ *                      to the buffer(s) used for transferring data and
+ *                      status information.
+ */
+static int iscsiCommand(PISCSIIMAGE pImage, PSCSIREQ pRequest)
+{
+    int rc;
+    uint32_t itt;
+    uint32_t cbData;
+    uint32_t cnISCSIReq = 0;
+    ISCSIREQ aISCSIReq[4];
+    uint32_t aReqBHS[12];
+
+    uint32_t *pDst = NULL;
+    size_t cbBufLength;
+    uint32_t aStatus[256]; /**< Plenty of buffer for status information. */
+    uint32_t ExpDataSN = 0;
+    bool final = false;
+
+
+    LogFlowFunc(("entering, CmdSN=%d\n", pImage->CmdSN));
+
+    Assert(pRequest->enmXfer != SCSIXFER_TO_FROM_TARGET);   /**< @todo not yet supported, would require AHS. */
+    Assert(pRequest->cbI2TData <= 0xffffff);    /* larger transfers would require R2T support. */
+    Assert(pRequest->cbCDB <= 16);      /* would cause buffer overrun below. */
+
+    /* If not in normal state, then the transport connection was dropped. Try
+     * to reestablish by logging in, the target might be responsive again. */
+    if (pImage->state == ISCSISTATE_FREE)
+        rc = iscsiAttach(pImage);
+
+    /* If still not in normal state, then the underlying transport connection
+     * cannot be established. Get out before bad things happen (and make
+     * sure the caller suspends the VM again). */
+    if (pImage->state != ISCSISTATE_NORMAL)
+    {
+        rc = VERR_NET_CONNECTION_REFUSED;
+        goto out;
+    }
+
+    /*
+     * Send SCSI command to target with all I2T data included.
+     */
+    cbData = 0;
+    if (pRequest->enmXfer == SCSIXFER_FROM_TARGET)
+        cbData = (uint32_t)pRequest->cbT2IData;
+    else
+        cbData = (uint32_t)pRequest->cbI2TData;
+
+    RTSemMutexRequest(pImage->Mutex, RT_INDEFINITE_WAIT);
+
+    itt = iscsiNewITT(pImage);
+    memset(aReqBHS, 0, sizeof(aReqBHS));
+    aReqBHS[0] = RT_H2N_U32(    ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
+                            |   (pRequest->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
+    aReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pRequest->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
+    aReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
+    aReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
+    aReqBHS[4] = itt;
+    aReqBHS[5] = RT_H2N_U32(cbData);
+    aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+    aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+    memcpy(aReqBHS + 8, pRequest->pvCDB, pRequest->cbCDB);
+    pImage->CmdSN++;
+
+    aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
+    aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
+    cnISCSIReq++;
+
+    if (    pRequest->enmXfer == SCSIXFER_TO_TARGET
+        ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
+    {
+        Assert(pRequest->cI2TSegs == 1);
+        aISCSIReq[cnISCSIReq].pcvSeg = pRequest->paI2TSegs[0].pvSeg;
+        aISCSIReq[cnISCSIReq].cbSeg = pRequest->paI2TSegs[0].cbSeg;  /* Padding done by transport. */
+        cnISCSIReq++;
+    }
+
+    rc = iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_DEFAULT);
+    if (RT_FAILURE(rc))
+        goto out_release;
+
+    /* Place SCSI request in queue. */
+    pImage->paCurrReq = aISCSIReq;
+    pImage->cnCurrReq = cnISCSIReq;
+
+    /*
+     * Read SCSI response/data in PDUs from target.
+     */
+    if (    pRequest->enmXfer == SCSIXFER_FROM_TARGET
+        ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET)
+    {
+        Assert(pRequest->cT2ISegs == 1);
+        pDst = (uint32_t *)pRequest->paT2ISegs[0].pvSeg;
+        cbBufLength = pRequest->paT2ISegs[0].cbSeg;
+    }
+    else
+        cbBufLength = 0;
+
+    do {
+        uint32_t cnISCSIRes = 0;
+        ISCSIRES aISCSIRes[4];
+        uint32_t aResBHS[12];
+
+        aISCSIRes[cnISCSIRes].pvSeg = aResBHS;
+        aISCSIRes[cnISCSIRes].cbSeg = sizeof(aResBHS);
+        cnISCSIRes++;
+        if (cbBufLength != 0 &&
+            (   pRequest->enmXfer == SCSIXFER_FROM_TARGET
+             ||  pRequest->enmXfer == SCSIXFER_TO_FROM_TARGET))
+        {
+            aISCSIRes[cnISCSIRes].pvSeg = pDst;
+            aISCSIRes[cnISCSIRes].cbSeg = cbBufLength;
+            cnISCSIRes++;
+        }
+        /* Always reserve space for the status - it's impossible to tell
+         * beforehand whether this will be the final PDU or not. */
+        aISCSIRes[cnISCSIRes].pvSeg = aStatus;
+        aISCSIRes[cnISCSIRes].cbSeg = sizeof(aStatus);
+        cnISCSIRes++;
+
+        rc = iscsiRecvPDU(pImage, itt, aISCSIRes, cnISCSIRes);
+        if (RT_FAILURE(rc))
+            break;
+
+        final = !!(RT_N2H_U32(aResBHS[0]) & ISCSI_FINAL_BIT);
+        ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(aResBHS[0]) & ISCSIOP_MASK);
+        if (cmd == ISCSIOP_SCSI_RES)
+        {
+            /* This is the final PDU which delivers the status (and may be omitted if
+             * the last Data-In PDU included successful completion status). Note
+             * that ExpStatSN has been bumped already in iscsiRecvPDU. */
+            if (!final || ((RT_N2H_U32(aResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(aResBHS[6]) != pImage->ExpStatSN - 1))
+            {
+                /* SCSI Response in the wrong place or with a (target) failure. */
+                rc = VERR_PARSE_ERROR;
+                break;
+            }
+            /* The following is a bit tricky, as in error situations we may
+             * get the status only instead of the result data plus optional
+             * status. Thus the status may have ended up partially in the
+             * data area. */
+            pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
+            cbData = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
+            if (cbData >= 2)
+            {
+                uint32_t cbStat = RT_N2H_U32(((uint32_t *)aISCSIRes[1].pvSeg)[0]) >> 16;
+                if (cbStat + 2 > cbData)
+                {
+                    rc = VERR_BUFFER_OVERFLOW;
+                    break;
+                }
+                /* Truncate sense data if it doesn't fit into the buffer. */
+                pRequest->cbSense = RT_MIN(cbStat, pRequest->cbSense);
+                memcpy(pRequest->pvSense,
+                       ((const char *)aISCSIRes[1].pvSeg) + 2,
+                       RT_MIN(aISCSIRes[1].cbSeg - 2, pRequest->cbSense));
+                if (   cnISCSIRes > 2 && aISCSIRes[2].cbSeg
+                    && (ssize_t)pRequest->cbSense - aISCSIRes[1].cbSeg + 2 > 0)
+                {
+                    memcpy((char *)pRequest->pvSense + aISCSIRes[1].cbSeg - 2,
+                           aISCSIRes[2].pvSeg,
+                           pRequest->cbSense - aISCSIRes[1].cbSeg + 2);
+                }
+            }
+            else if (cbData == 1)
+            {
+                rc = VERR_PARSE_ERROR;
+                break;
+            }
+            else
+                pRequest->cbSense = 0;
+            break;
+        }
+        else if (cmd == ISCSIOP_SCSI_DATA_IN)
+        {
+            /* A Data-In PDU carries some data that needs to be added to the received
+             * data in response to the command. There may be both partial and complete
+             * Data-In PDUs, so collect data until the status is included or the status
+             * is sent in a separate SCSI Result frame (see above). */
+            if (final && aISCSIRes[2].cbSeg != 0)
+            {
+                /* The received PDU is partially stored in the buffer for status.
+                 * Must not happen under normal circumstances and is a target error. */
+                rc = VERR_BUFFER_OVERFLOW;
+                break;
+            }
+            uint32_t len = RT_N2H_U32(aResBHS[1]) & 0x00ffffff;
+            pDst = (uint32_t *)((char *)pDst + len);
+            cbBufLength -= len;
+            ExpDataSN++;
+            if (final && (RT_N2H_U32(aResBHS[0]) & ISCSI_STATUS_BIT) != 0)
+            {
+                pRequest->status = RT_N2H_U32(aResBHS[0]) & 0x000000ff;
+                pRequest->cbSense = 0;
+                break;
+            }
+        }
+        else
+        {
+            rc = VERR_PARSE_ERROR;
+            break;
+        }
+    } while (true);
+
+    /* Remove SCSI request from queue. */
+    pImage->paCurrReq = NULL;
+    pImage->cnCurrReq = 0;
+
+out_release:
+    if (rc == VERR_TIMEOUT)
+    {
+        /* Drop connection in case the target plays dead. Much better than
+         * delaying the next requests until the timed out command actually
+         * finishes. Also keep in mind that command shouldn't take longer than
+         * about 30-40 seconds, or the guest will lose its patience. */
+        iscsiTransportClose(pImage);
+        pImage->state = ISCSISTATE_FREE;
+    }
+    RTSemMutexRelease(pImage->Mutex);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Generate a new Initiator Task Tag.
+ *
+ * @returns Initiator Task Tag.
+ * @param   pImage      The iSCSI connection state to be used.
+ */
+static uint32_t iscsiNewITT(PISCSIIMAGE pImage)
+{
+    uint32_t next_itt;
+
+    next_itt = pImage->ITT++;
+    if (pImage->ITT == ISCSI_TASK_TAG_RSVD)
+        pImage->ITT = 0;
+    return RT_H2N_U32(next_itt);
+}
+
+
+/**
+ * Send an iSCSI request. The request can consist of several segments, which
+ * are padded to 4 byte boundaries and concatenated.
+ *
+ * @returns VBOX status
+ * @param   pImage      The iSCSI connection state to be used.
+ * @param   paReq       Pointer to array of iSCSI request sections.
+ * @param   cnReq       Number of valid iSCSI request sections in the array.
+ * @param   uFlags      Flags controlling the exact send semantics.
+ */
+static int iscsiSendPDU(PISCSIIMAGE pImage, PISCSIREQ paReq, uint32_t cnReq,
+                        uint32_t uFlags)
+{
+    int rc = VINF_SUCCESS;
+    /** @todo return VERR_VD_ISCSI_INVALID_STATE in the appropriate situations,
+     * needs cleaning up of timeout/disconnect handling a bit, as otherwise
+     * too many incorrect errors are signalled. */
+    Assert(cnReq >= 1);
+    Assert(paReq[0].cbSeg >= ISCSI_BHS_SIZE);
+
+    for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
+    {
+        rc = iscsiTransportWrite(pImage, paReq, cnReq);
+        if (RT_SUCCESS(rc))
+            break;
+        if (   (uFlags & ISCSIPDU_NO_REATTACH)
+            || (rc != VERR_BROKEN_PIPE && rc != VERR_NET_CONNECTION_REFUSED))
+            break;
+        /* No point in reestablishing the connection for a logout */
+        if (pImage->state == ISCSISTATE_IN_LOGOUT)
+            break;
+        RTThreadSleep(500);
+        if (pImage->state != ISCSISTATE_IN_LOGIN)
+        {
+            /* Attempt to re-login when a connection fails, but only when not
+             * currently logging in. */
+            rc = iscsiAttach(pImage);
+            if (RT_FAILURE(rc))
+                break;
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * Wait for an iSCSI response with a matching Initiator Target Tag. The response is
+ * split into several segments, as requested by the caller-provided buffer specification.
+ * Remember that the response can be split into several PDUs by the sender, so make
+ * sure that all parts are collected and processed appropriately by the caller.
+ *
+ * @returns VBOX status
+ * @param   pImage      The iSCSI connection state to be used.
+ * @param   paRes       Pointer to array of iSCSI response sections.
+ * @param   cnRes       Number of valid iSCSI response sections in the array.
+ */
+static int iscsiRecvPDU(PISCSIIMAGE pImage, uint32_t itt, PISCSIRES paRes, uint32_t cnRes)
+{
+    int rc = VINF_SUCCESS;
+    ISCSIRES aResBuf;
+
+    for (uint32_t i = 0; i < pImage->cISCSIRetries; i++)
+    {
+        aResBuf.pvSeg = pImage->pvRecvPDUBuf;
+        aResBuf.cbSeg = pImage->cbRecvPDUBuf;
+        rc = iscsiTransportRead(pImage, &aResBuf, 1);
+        if (RT_FAILURE(rc))
+        {
+            if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
+            {
+                /* No point in reestablishing the connection for a logout */
+                if (pImage->state == ISCSISTATE_IN_LOGOUT)
+                    break;
+                /* Connection broken while waiting for a response - wait a while and
+                 * try to restart by re-sending the original request (if any).
+                 * This also handles the connection reestablishment (login etc.). */
+                RTThreadSleep(500);
+                if (pImage->state != ISCSISTATE_IN_LOGIN)
+                {
+                    /* Attempt to re-login when a connection fails, but only when not
+                     * currently logging in. */
+                    rc = iscsiAttach(pImage);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+                if (pImage->paCurrReq != NULL)
+                {
+                    rc = iscsiSendPDU(pImage, pImage->paCurrReq, pImage->cnCurrReq, ISCSIPDU_DEFAULT);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            }
+            else
+            {
+                /* Signal other errors (VERR_BUFFER_OVERFLOW etc.) to the caller. */
+                break;
+            }
+        }
+        else
+        {
+            ISCSIOPCODE cmd;
+            const uint32_t *pcvResSeg = (const uint32_t *)aResBuf.pvSeg;
+
+            /* Check whether the received PDU is valid, and update the internal state of
+             * the iSCSI connection/session. */
+            rc = iscsiValidatePDU(&aResBuf, 1);
+            if (RT_FAILURE(rc))
+                continue;
+            cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
+            switch (cmd)
+            {
+                case ISCSIOP_SCSI_RES:
+                case ISCSIOP_SCSI_TASKMGMT_RES:
+                case ISCSIOP_SCSI_DATA_IN:
+                case ISCSIOP_R2T:
+                case ISCSIOP_ASYN_MSG:
+                case ISCSIOP_TEXT_RES:
+                case ISCSIOP_LOGIN_RES:
+                case ISCSIOP_LOGOUT_RES:
+                case ISCSIOP_REJECT:
+                case ISCSIOP_NOP_IN:
+                    if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
+                        pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
+                    if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
+                        pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
+                    break;
+                default:
+                    rc = VERR_PARSE_ERROR;
+            }
+            if (RT_FAILURE(rc))
+                continue;
+            if (    !pImage->FirstRecvPDU
+                &&  (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
+            {
+                if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
+                {
+                    /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
+                    if (    (cmd != ISCSIOP_R2T)
+                        &&  ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
+                        pImage->ExpStatSN++;
+                }
+                else
+                {
+                    rc = VERR_PARSE_ERROR;
+                    continue;
+                }
+            }
+            /* Finally check whether the received PDU matches what the caller wants. */
+            if (   itt == pcvResSeg[4]
+                && itt != ISCSI_TASK_TAG_RSVD)
+            {
+                /* Copy received PDU (one segment) to caller-provided buffers. */
+                uint32_t j;
+                size_t cbSeg;
+                const uint8_t *pSrc;
+
+                pSrc = (const uint8_t *)aResBuf.pvSeg;
+                cbSeg = aResBuf.cbSeg;
+                for (j = 0; j < cnRes; j++)
+                {
+                    if (cbSeg > paRes[j].cbSeg)
+                    {
+                        memcpy(paRes[j].pvSeg, pSrc, paRes[j].cbSeg);
+                        pSrc += paRes[j].cbSeg;
+                        cbSeg -= paRes[j].cbSeg;
+                    }
+                    else
+                    {
+                        memcpy(paRes[j].pvSeg, pSrc, cbSeg);
+                        paRes[j].cbSeg = cbSeg;
+                        cbSeg = 0;
+                        break;
+                    }
+                }
+                if (cbSeg != 0)
+                {
+                    rc = VERR_BUFFER_OVERFLOW;
+                    break;
+                }
+                for (j++; j < cnRes; j++)
+                    paRes[j].cbSeg = 0;
+                break;
+            }
+            else if (   cmd == ISCSIOP_NOP_IN
+                     && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
+            {
+                uint32_t cnISCSIReq;
+                ISCSIREQ aISCSIReq[4];
+                uint32_t aReqBHS[12];
+
+                aReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
+                aReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
+                aReqBHS[2] = pcvResSeg[2];      /* copy LUN from NOP-In */
+                aReqBHS[3] = pcvResSeg[3];      /* copy LUN from NOP-In */
+                aReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
+                aReqBHS[5] = pcvResSeg[5];      /* copy TTT from NOP-In */
+                aReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+                aReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+                aReqBHS[8] = 0;             /* reserved */
+                aReqBHS[9] = 0;             /* reserved */
+                aReqBHS[10] = 0;            /* reserved */
+                aReqBHS[11] = 0;            /* reserved */
+
+                cnISCSIReq = 0;
+                aISCSIReq[cnISCSIReq].pcvSeg = aReqBHS;
+                aISCSIReq[cnISCSIReq].cbSeg = sizeof(aReqBHS);
+                cnISCSIReq++;
+
+                iscsiSendPDU(pImage, aISCSIReq, cnISCSIReq, ISCSIPDU_NO_REATTACH);
+                /* Break if the caller wanted to process the NOP-in only. */
+                if (itt == ISCSI_TASK_TAG_RSVD)
+                    break;
+            }
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * Reset the PDU buffer
+ *
+ * @param   pImage      The iSCSI connection state to be used.
+ */
+static void iscsiRecvPDUReset(PISCSIIMAGE pImage)
+{
+    pImage->cbRecvPDUResidual = ISCSI_BHS_SIZE;
+    pImage->fRecvPDUBHS       = true;
+    pImage->pbRecvPDUBufCur   = (uint8_t *)pImage->pvRecvPDUBuf;
+}
+
+static void iscsiPDUTxAdd(PISCSIIMAGE pImage, PISCSIPDUTX pIScsiPDUTx, bool fFront)
+{
+    if (!fFront)
+    {
+        /* Link the PDU at the tail of the list. */
+        if (!pImage->pIScsiPDUTxHead)
+            pImage->pIScsiPDUTxHead = pIScsiPDUTx;
+        else
+            pImage->pIScsiPDUTxTail->pNext = pIScsiPDUTx;
+        pImage->pIScsiPDUTxTail = pIScsiPDUTx;
+    }
+    else
+    {
+        /* Link PDU to at the front of the list. */
+        pIScsiPDUTx->pNext = pImage->pIScsiPDUTxHead;
+        pImage->pIScsiPDUTxHead = pIScsiPDUTx;
+        if (!pImage->pIScsiPDUTxTail)
+            pImage->pIScsiPDUTxTail = pIScsiPDUTx;
+    }
+}
+
+/**
+ * Receives a PDU in a non blocking way.
+ *
+ * @returns VBOX status code.
+ * @param   pImage      The iSCSI connection state to be used.
+ */
+static int iscsiRecvPDUAsync(PISCSIIMAGE pImage)
+{
+    size_t cbActuallyRead = 0;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pImage=%#p\n", pImage));
+
+    /* Check if we are in the middle of a PDU receive. */
+    if (pImage->cbRecvPDUResidual == 0)
+    {
+        /*
+         * We are receiving a new PDU, don't read more than the BHS initially
+         * until we know the real size of the PDU.
+         */
+        iscsiRecvPDUReset(pImage);
+        LogFlow(("Receiving new PDU\n"));
+    }
+
+    rc = pImage->pInterfaceNetCallbacks->pfnReadNB(pImage->Socket, pImage->pbRecvPDUBufCur,
+                                                   pImage->cbRecvPDUResidual, &cbActuallyRead);
+    if (RT_SUCCESS(rc) && cbActuallyRead == 0)
+        rc = VERR_BROKEN_PIPE;
+
+    if (RT_SUCCESS(rc))
+    {
+        LogFlow(("Received %zu bytes\n", cbActuallyRead));
+        pImage->cbRecvPDUResidual -= cbActuallyRead;
+        pImage->pbRecvPDUBufCur   += cbActuallyRead;
+
+        /* Check if we received everything we wanted. */
+        if (   !pImage->cbRecvPDUResidual
+            && pImage->fRecvPDUBHS)
+        {
+            size_t cbAHSLength, cbDataLength;
+
+            /* If we were reading the BHS first get the actual PDU size now. */
+            uint32_t word1 = RT_N2H_U32(((uint32_t *)(pImage->pvRecvPDUBuf))[1]);
+            cbAHSLength = (word1 & 0xff000000) >> 24;
+            cbAHSLength = ((cbAHSLength - 1) | 3) + 1;      /* Add padding. */
+            cbDataLength = word1 & 0x00ffffff;
+            cbDataLength = ((cbDataLength - 1) | 3) + 1;    /* Add padding. */
+            pImage->cbRecvPDUResidual = cbAHSLength + cbDataLength;
+            pImage->fRecvPDUBHS = false; /* Start receiving the rest of the PDU. */
+        }
+
+        if (!pImage->cbRecvPDUResidual)
+        {
+            /* We received the complete PDU with or without any payload now. */
+            LogFlow(("Received complete PDU\n"));
+            ISCSIRES aResBuf;
+            aResBuf.pvSeg = pImage->pvRecvPDUBuf;
+            aResBuf.cbSeg = pImage->cbRecvPDUBuf;
+            rc = iscsiRecvPDUProcess(pImage, &aResBuf, 1);
+        }
+    }
+    else
+        LogFlowFunc(("Reading from the socket returned with rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+static int iscsiSendPDUAsync(PISCSIIMAGE pImage)
+{
+    size_t cbSent = 0;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pImage=%#p\n", pImage));
+
+    do
+    {
+        /*
+         * If there is no PDU active, get the first one from the list.
+         * Check that we are allowed to transfer the PDU by comparing the
+         * command sequence number and the maximum sequence number allowed by the target.
+         */
+        if (!pImage->pIScsiPDUTxCur)
+        {
+            if (   !pImage->pIScsiPDUTxHead
+                || serial_number_greater(pImage->pIScsiPDUTxHead->CmdSN, pImage->MaxCmdSN))
+                break;
+
+            pImage->pIScsiPDUTxCur = pImage->pIScsiPDUTxHead;
+            pImage->pIScsiPDUTxHead = pImage->pIScsiPDUTxCur->pNext;
+            if (!pImage->pIScsiPDUTxHead)
+                pImage->pIScsiPDUTxTail = NULL;
+        }
+
+        /* Send as much as we can. */
+        rc = pImage->pInterfaceNetCallbacks->pfnSgWriteNB(pImage->Socket, &pImage->pIScsiPDUTxCur->SgBuf, &cbSent);
+        LogFlow(("SgWriteNB returned rc=%Rrc cbSent=%zu\n", rc, cbSent));
+        if (RT_SUCCESS(rc))
+        {
+            LogFlow(("Sent %zu bytes for PDU %#p\n", cbSent, pImage->pIScsiPDUTxCur));
+            pImage->pIScsiPDUTxCur->cbSgLeft -= cbSent;
+            RTSgBufAdvance(&pImage->pIScsiPDUTxCur->SgBuf, cbSent);
+            if (!pImage->pIScsiPDUTxCur->cbSgLeft)
+            {
+                /* PDU completed, free it and place the command on the waiting for response list. */
+                if (pImage->pIScsiPDUTxCur->pIScsiCmd)
+                {
+                    LogFlow(("Sent complete PDU, placing on waiting list\n"));
+                    iscsiCmdInsert(pImage, pImage->pIScsiPDUTxCur->pIScsiCmd);
+                }
+                RTMemFree(pImage->pIScsiPDUTxCur);
+                pImage->pIScsiPDUTxCur = NULL;
+            }
+        }
+    } while (   RT_SUCCESS(rc)
+             && !pImage->pIScsiPDUTxCur);
+
+    if (rc == VERR_TRY_AGAIN)
+        rc = VINF_SUCCESS;
+
+    /* Add the write poll flag if we still have something to send, clear it otherwise. */
+    if (pImage->pIScsiPDUTxCur)
+        pImage->fPollEvents |= VD_INTERFACETCPNET_EVT_WRITE;
+    else
+        pImage->fPollEvents &= ~VD_INTERFACETCPNET_EVT_WRITE;
+
+    LogFlowFunc(("rc=%Rrc pIScsiPDUTxCur=%#p\n", rc, pImage->pIScsiPDUTxCur));
+    return rc;
+}
+
+/**
+ * Process a received PDU.
+ *
+ * @return VBOX status code.
+ * @param  pImage      The iSCSI connection state to be used.
+ * @param  paRes       Pointer to the array of iSCSI response sections.
+ * @param  cnRes       Number of valid iSCSI response sections in the array.
+ */
+static int iscsiRecvPDUProcess(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
+
+    /* Validate the PDU first. */
+    rc = iscsiValidatePDU(paRes, cnRes);
+    if (RT_SUCCESS(rc))
+    {
+        ISCSIOPCODE cmd;
+        const uint32_t *pcvResSeg = (const uint32_t *)paRes[0].pvSeg;
+
+        Assert(paRes[0].cbSeg > 9 * sizeof(uint32_t));
+
+        do
+        {
+            cmd = (ISCSIOPCODE)(RT_N2H_U32(pcvResSeg[0]) & ISCSIOP_MASK);
+            switch (cmd)
+            {
+                case ISCSIOP_SCSI_RES:
+                case ISCSIOP_SCSI_TASKMGMT_RES:
+                case ISCSIOP_SCSI_DATA_IN:
+                case ISCSIOP_R2T:
+                case ISCSIOP_ASYN_MSG:
+                case ISCSIOP_TEXT_RES:
+                case ISCSIOP_LOGIN_RES:
+                case ISCSIOP_LOGOUT_RES:
+                case ISCSIOP_REJECT:
+                case ISCSIOP_NOP_IN:
+                    if (serial_number_less(pImage->MaxCmdSN, RT_N2H_U32(pcvResSeg[8])))
+                        pImage->MaxCmdSN = RT_N2H_U32(pcvResSeg[8]);
+                    if (serial_number_less(pImage->ExpCmdSN, RT_N2H_U32(pcvResSeg[7])))
+                        pImage->ExpCmdSN = RT_N2H_U32(pcvResSeg[7]);
+                    break;
+                default:
+                    rc = VERR_PARSE_ERROR;
+            }
+
+            if (RT_FAILURE(rc))
+                break;
+
+            if (    !pImage->FirstRecvPDU
+                &&  (cmd != ISCSIOP_SCSI_DATA_IN || (RT_N2H_U32(pcvResSeg[0]) & ISCSI_STATUS_BIT)))
+            {
+                if (pImage->ExpStatSN == RT_N2H_U32(pcvResSeg[6]))
+                {
+                    /* StatSN counter is not advanced on R2T and on a target SN update NOP-In. */
+                    if (    (cmd != ISCSIOP_R2T)
+                        &&  ((cmd != ISCSIOP_NOP_IN) || (RT_N2H_U32(pcvResSeg[4]) != ISCSI_TASK_TAG_RSVD)))
+                        pImage->ExpStatSN++;
+                }
+                else
+                {
+                   rc = VERR_PARSE_ERROR;
+                   break;
+                }
+            }
+
+            if (pcvResSeg[4] != ISCSI_TASK_TAG_RSVD)
+            {
+                /*
+                 * This is a response from the target for a request from the initiator.
+                 * Get the request and update its state.
+                 */
+                rc = iscsiRecvPDUUpdateRequest(pImage, paRes, cnRes);
+                /* Try to send more PDUs now that we updated the MaxCmdSN field */
+                if (   RT_SUCCESS(rc)
+                    && !pImage->pIScsiPDUTxCur)
+                    rc = iscsiSendPDUAsync(pImage);
+            }
+            else
+            {
+                /* This is a target initiated request (we handle only NOP-In request at the moment). */
+                if (   cmd == ISCSIOP_NOP_IN
+                    && RT_N2H_U32(pcvResSeg[5]) != ISCSI_TASK_TAG_RSVD)
+                {
+                    PISCSIPDUTX pIScsiPDUTx;
+                    uint32_t cnISCSIReq;
+                    uint32_t *paReqBHS;
+
+                    LogFlowFunc(("Sending NOP-Out\n"));
+
+                    /* Allocate a new PDU initialize it and put onto the waiting list. */
+                    pIScsiPDUTx = (PISCSIPDUTX)RTMemAllocZ(sizeof(ISCSIPDUTX));
+                    if (!pIScsiPDUTx)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
+                    paReqBHS = &pIScsiPDUTx->aBHS[0];
+                    paReqBHS[0] = RT_H2N_U32(ISCSI_IMMEDIATE_DELIVERY_BIT | ISCSI_FINAL_BIT | ISCSIOP_NOP_OUT);
+                    paReqBHS[1] = RT_H2N_U32(0); /* TotalAHSLength=0,DataSementLength=0 */
+                    paReqBHS[2] = pcvResSeg[2];      /* copy LUN from NOP-In */
+                    paReqBHS[3] = pcvResSeg[3];      /* copy LUN from NOP-In */
+                    paReqBHS[4] = RT_H2N_U32(ISCSI_TASK_TAG_RSVD); /* ITT, reply */
+                    paReqBHS[5] = pcvResSeg[5];      /* copy TTT from NOP-In */
+                    paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+                    paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+                    paReqBHS[8] = 0;             /* reserved */
+                    paReqBHS[9] = 0;             /* reserved */
+                    paReqBHS[10] = 0;            /* reserved */
+                    paReqBHS[11] = 0;            /* reserved */
+
+                    cnISCSIReq = 0;
+                    pIScsiPDUTx->aISCSIReq[cnISCSIReq].pvSeg = paReqBHS;
+                    pIScsiPDUTx->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDUTx->aBHS);
+                    cnISCSIReq++;
+                    pIScsiPDUTx->cbSgLeft = sizeof(pIScsiPDUTx->aBHS);
+                    RTSgBufInit(&pIScsiPDUTx->SgBuf, pIScsiPDUTx->aISCSIReq, cnISCSIReq);
+
+                    /* Link the PDU to the list. */
+                    iscsiPDUTxAdd(pImage, pIScsiPDUTx, false /* fFront */);
+
+                    /* Start transfer of a PDU if there is no one active at the moment. */
+                    if (!pImage->pIScsiPDUTxCur)
+                        rc = iscsiSendPDUAsync(pImage);
+                }
+            }
+        } while (0);
+    }
+
+    return rc;
+}
+
+/**
+ * Check the static (not dependent on the connection/session state) validity of an iSCSI response PDU.
+ *
+ * @returns VBOX status
+ * @param   paRes       Pointer to array of iSCSI response sections.
+ * @param   cnRes       Number of valid iSCSI response sections in the array.
+ */
+static int iscsiValidatePDU(PISCSIRES paRes, uint32_t cnRes)
+{
+    const uint32_t *pcrgResBHS;
+    uint32_t hw0;
+    Assert(cnRes >= 1);
+    Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
+
+    LogFlowFunc(("paRes=%#p cnRes=%u\n", paRes, cnRes));
+
+    pcrgResBHS = (const uint32_t *)(paRes[0].pvSeg);
+    hw0 = RT_N2H_U32(pcrgResBHS[0]);
+    switch (hw0 & ISCSIOP_MASK)
+    {
+        case ISCSIOP_NOP_IN:
+            /* NOP-In responses must not be split into several PDUs nor it may contain
+             * ping data for target-initiated pings nor may both task tags be valid task tags. */
+            if (    (hw0 & ISCSI_FINAL_BIT) == 0
+                ||  (    RT_N2H_U32(pcrgResBHS[4]) == ISCSI_TASK_TAG_RSVD
+                     &&  RT_N2H_U32(pcrgResBHS[1]) != 0)
+                ||  (    RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD
+                     &&  RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_SCSI_RES:
+            /* SCSI responses must not be split into several PDUs nor must the residual
+             * bits be contradicting each other nor may the residual bits be set for PDUs
+             * containing anything else but a completed command response. Underflow
+             * is no reason for declaring a PDU as invalid, as the target may choose
+             * to return less data than we assume to get. */
+            if (    (hw0 & ISCSI_FINAL_BIT) == 0
+                ||  ((hw0 & ISCSI_BI_READ_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_BI_READ_RESIDUAL_UNFL_BIT))
+                ||  ((hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
+                ||  (    ((hw0 & ISCSI_SCSI_RESPONSE_MASK) == 0)
+                     &&  ((hw0 & ISCSI_SCSI_STATUS_MASK) == SCSI_STATUS_OK)
+                     &&  (hw0 & (  ISCSI_BI_READ_RESIDUAL_OVFL_BIT | ISCSI_BI_READ_RESIDUAL_UNFL_BIT
+                                 | ISCSI_RESIDUAL_OVFL_BIT))))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_LOGIN_RES:
+            /* Login responses must not contain contradicting transit and continue bits. */
+            if ((hw0 & ISCSI_CONTINUE_BIT) && ((hw0 & ISCSI_TRANSIT_BIT) != 0))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_TEXT_RES:
+            /* Text responses must not contain contradicting final and continue bits nor
+             * may the final bit be set for PDUs containing a target transfer tag other than
+             * the reserved transfer tag (and vice versa). */
+            if (    (((hw0 & ISCSI_CONTINUE_BIT) && (hw0 & ISCSI_FINAL_BIT) != 0))
+                ||  (((hw0 & ISCSI_FINAL_BIT) && (RT_N2H_U32(pcrgResBHS[5]) != ISCSI_TASK_TAG_RSVD)))
+                ||  (((hw0 & ISCSI_FINAL_BIT) == 0) && (RT_N2H_U32(pcrgResBHS[5]) == ISCSI_TASK_TAG_RSVD)))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_SCSI_DATA_IN:
+            /* SCSI Data-in responses must not contain contradicting residual bits when
+             * status bit is set. */
+            if ((hw0 & ISCSI_STATUS_BIT) && (hw0 & ISCSI_RESIDUAL_OVFL_BIT) && (hw0 & ISCSI_RESIDUAL_UNFL_BIT))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_LOGOUT_RES:
+            /* Logout responses must not have the final bit unset and may not contain any
+             * data or additional header segments. */
+            if (    ((hw0 & ISCSI_FINAL_BIT) == 0)
+                ||  (RT_N2H_U32(pcrgResBHS[1]) != 0))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_ASYN_MSG:
+            /* Asynchronous Messages must not have the final bit unset and may not contain
+             * an initiator task tag. */
+            if (    ((hw0 & ISCSI_FINAL_BIT) == 0)
+                ||  (RT_N2H_U32(pcrgResBHS[4]) != ISCSI_TASK_TAG_RSVD))
+                return VERR_PARSE_ERROR;
+            break;
+        case ISCSIOP_SCSI_TASKMGMT_RES:
+        case ISCSIOP_R2T:
+        case ISCSIOP_REJECT:
+        default:
+            /* Do some logging, ignore PDU. */
+            LogFlowFunc(("ignore unhandled PDU, first word %#08x\n", RT_N2H_U32(pcrgResBHS[0])));
+            return VERR_PARSE_ERROR;
+    }
+    /* A target must not send PDUs with MaxCmdSN less than ExpCmdSN-1. */
+
+    if (serial_number_less(RT_N2H_U32(pcrgResBHS[8]), RT_N2H_U32(pcrgResBHS[7])-1))
+        return VERR_PARSE_ERROR;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepares a PDU to transfer for the given command and adds it to the list.
+ */
+static int iscsiPDUTxPrepare(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
+{
+    int rc = VINF_SUCCESS;
+    uint32_t *paReqBHS;
+    size_t cbData = 0;
+    size_t cbSegs = 0;
+    PSCSIREQ pScsiReq;
+    PISCSIPDUTX pIScsiPDU = NULL;
+
+    LogFlowFunc(("pImage=%#p pIScsiCmd=%#p\n", pImage, pIScsiCmd));
+
+    Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
+
+    pIScsiCmd->Itt = iscsiNewITT(pImage);
+    pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
+
+    if (pScsiReq->cT2ISegs)
+        RTSgBufInit(&pScsiReq->SgBufT2I, pScsiReq->paT2ISegs, pScsiReq->cT2ISegs);
+
+    /*
+     * Allocate twice as much entries as required for padding (worst case).
+     * The additional segment is for the BHS.
+     */
+    size_t cI2TSegs = 2*(pScsiReq->cI2TSegs + 1);
+    pIScsiPDU = (PISCSIPDUTX)RTMemAllocZ(RT_OFFSETOF(ISCSIPDUTX, aISCSIReq[cI2TSegs]));
+    if (!pIScsiPDU)
+        return VERR_NO_MEMORY;
+
+    pIScsiPDU->pIScsiCmd = pIScsiCmd;
+
+    if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
+        cbData = (uint32_t)pScsiReq->cbT2IData;
+    else
+        cbData = (uint32_t)pScsiReq->cbI2TData;
+
+    paReqBHS = pIScsiPDU->aBHS;
+
+    /* Setup the BHS. */
+    paReqBHS[0] = RT_H2N_U32(  ISCSI_FINAL_BIT | ISCSI_TASK_ATTR_ORDERED | ISCSIOP_SCSI_CMD
+                             | (pScsiReq->enmXfer << 21)); /* I=0,F=1,Attr=Ordered */
+    paReqBHS[1] = RT_H2N_U32(0x00000000 | ((uint32_t)pScsiReq->cbI2TData & 0xffffff)); /* TotalAHSLength=0 */
+    paReqBHS[2] = RT_H2N_U32(pImage->LUN >> 32);
+    paReqBHS[3] = RT_H2N_U32(pImage->LUN & 0xffffffff);
+    paReqBHS[4] = pIScsiCmd->Itt;
+    paReqBHS[5] = RT_H2N_U32(cbData);
+    paReqBHS[6] = RT_H2N_U32(pImage->CmdSN);
+    paReqBHS[7] = RT_H2N_U32(pImage->ExpStatSN);
+    memcpy(paReqBHS + 8, pScsiReq->pvCDB, pScsiReq->cbCDB);
+
+    pIScsiPDU->CmdSN = pImage->CmdSN;
+    pImage->CmdSN++;
+
+    /* Setup the S/G buffers. */
+    uint32_t cnISCSIReq = 0;
+    pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = sizeof(pIScsiPDU->aBHS);
+    pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pIScsiPDU->aBHS;
+    cnISCSIReq++;
+    cbSegs = sizeof(pIScsiPDU->aBHS);
+    /* Padding is not necessary for the BHS. */
+
+    if (pScsiReq->cbI2TData)
+    {
+        for (unsigned cSeg = 0; cSeg < pScsiReq->cI2TSegs; cSeg++)
+        {
+            Assert(cnISCSIReq < cI2TSegs);
+            pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = pScsiReq->paI2TSegs[cSeg].cbSeg;
+            pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = pScsiReq->paI2TSegs[cSeg].pvSeg;
+            cbSegs += pScsiReq->paI2TSegs[cSeg].cbSeg;
+            cnISCSIReq++;
+
+            /* Add padding if necessary. */
+            if (pScsiReq->paI2TSegs[cSeg].cbSeg & 3)
+            {
+                Assert(cnISCSIReq < cI2TSegs);
+                pIScsiPDU->aISCSIReq[cnISCSIReq].pvSeg = &pImage->aPadding[0];
+                pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg = 4 - (pScsiReq->paI2TSegs[cSeg].cbSeg & 3);
+                cbSegs += pIScsiPDU->aISCSIReq[cnISCSIReq].cbSeg;
+                cnISCSIReq++;
+            }
+        }
+    }
+
+    pIScsiPDU->cISCSIReq = cnISCSIReq;
+    pIScsiPDU->cbSgLeft  = cbSegs;
+    RTSgBufInit(&pIScsiPDU->SgBuf, pIScsiPDU->aISCSIReq, cnISCSIReq);
+
+    /* Link the PDU to the list. */
+    iscsiPDUTxAdd(pImage, pIScsiPDU, false /* fFront */);
+
+    /* Start transfer of a PDU if there is no one active at the moment. */
+    if (!pImage->pIScsiPDUTxCur)
+        rc = iscsiSendPDUAsync(pImage);
+
+    return rc;
+}
+
+
+/**
+ * Updates the state of a request from the PDU we received.
+ *
+ * @return VBox status code.
+ * @param   pImage      iSCSI connection state to use.
+ * @param   paRes       Pointer to array of iSCSI response sections.
+ * @param   cnRes       Number of valid iSCSI response sections in the array.
+ */
+static int iscsiRecvPDUUpdateRequest(PISCSIIMAGE pImage, PISCSIRES paRes, uint32_t cnRes)
+{
+    int rc = VINF_SUCCESS;
+    PISCSICMD pIScsiCmd;
+    uint32_t *paResBHS;
+
+    LogFlowFunc(("pImage=%#p paRes=%#p cnRes=%u\n", pImage, paRes, cnRes));
+
+    Assert(cnRes == 1);
+    Assert(paRes[0].cbSeg >= ISCSI_BHS_SIZE);
+
+    paResBHS = (uint32_t *)paRes[0].pvSeg;
+
+    pIScsiCmd = iscsiCmdGetFromItt(pImage, paResBHS[4]);
+
+    if (pIScsiCmd)
+    {
+        bool final = false;
+        PSCSIREQ pScsiReq;
+
+        LogFlow(("Found SCSI command %#p for Itt=%#u\n", pIScsiCmd, paResBHS[4]));
+
+        Assert(pIScsiCmd->enmCmdType == ISCSICMDTYPE_REQ);
+        pScsiReq = pIScsiCmd->CmdType.ScsiReq.pScsiReq;
+
+        final = !!(RT_N2H_U32(paResBHS[0]) & ISCSI_FINAL_BIT);
+        ISCSIOPCODE cmd = (ISCSIOPCODE)(RT_N2H_U32(paResBHS[0]) & ISCSIOP_MASK);
+        if (cmd == ISCSIOP_SCSI_RES)
+        {
+            /* This is the final PDU which delivers the status (and may be omitted if
+             * the last Data-In PDU included successful completion status). Note
+             * that ExpStatSN has been bumped already in iscsiRecvPDU. */
+            if (!final || ((RT_N2H_U32(paResBHS[0]) & 0x0000ff00) != 0) || (RT_N2H_U32(paResBHS[6]) != pImage->ExpStatSN - 1))
+            {
+                /* SCSI Response in the wrong place or with a (target) failure. */
+                LogFlow(("Wrong ExpStatSN value in PDU\n"));
+                rc = VERR_PARSE_ERROR;
+            }
+            else
+            {
+                pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
+                size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
+                void *pvSense = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
+
+                if (cbData >= 2)
+                {
+                    uint32_t cbStat = RT_N2H_U32(((uint32_t *)pvSense)[0]) >> 16;
+                    if (cbStat + 2 > cbData)
+                    {
+                        rc = VERR_BUFFER_OVERFLOW;
+                    }
+                    else
+                    {
+                        /* Truncate sense data if it doesn't fit into the buffer. */
+                        pScsiReq->cbSense = RT_MIN(cbStat, pScsiReq->cbSense);
+                        memcpy(pScsiReq->pvSense, (uint8_t *)pvSense + 2,
+                               RT_MIN(paRes[0].cbSeg - ISCSI_BHS_SIZE - 2, pScsiReq->cbSense));
+                    }
+                }
+                else if (cbData == 1)
+                    rc = VERR_PARSE_ERROR;
+                else
+                    pScsiReq->cbSense = 0;
+            }
+            iscsiCmdComplete(pImage, pIScsiCmd, rc);
+        }
+        else if (cmd == ISCSIOP_SCSI_DATA_IN)
+        {
+            /* A Data-In PDU carries some data that needs to be added to the received
+             * data in response to the command. There may be both partial and complete
+             * Data-In PDUs, so collect data until the status is included or the status
+             * is sent in a separate SCSI Result frame (see above). */
+            size_t cbData = RT_N2H_U32(paResBHS[1]) & 0x00ffffff;
+            void   *pvData = (uint8_t *)paRes[0].pvSeg + ISCSI_BHS_SIZE;
+
+            if (final && cbData > pScsiReq->cbT2IData)
+            {
+                /* The received PDU is bigger than what we requested.
+                 * Must not happen under normal circumstances and is a target error. */
+                rc = VERR_BUFFER_OVERFLOW;
+            }
+            else
+            {
+                /* Copy data from the received PDU into the T2I segments. */
+                size_t cbCopied = RTSgBufCopyFromBuf(&pScsiReq->SgBufT2I, pvData, cbData);
+                Assert(cbCopied == cbData);
+
+                if (final && (RT_N2H_U32(paResBHS[0]) & ISCSI_STATUS_BIT) != 0)
+                {
+                    pScsiReq->status = RT_N2H_U32(paResBHS[0]) & 0x000000ff;
+                    pScsiReq->cbSense = 0;
+                    iscsiCmdComplete(pImage, pIScsiCmd, VINF_SUCCESS);
+                }
+            }
+        }
+        else
+            rc = VERR_PARSE_ERROR;
+    }
+
+    /* Log any errors here but ignore the PDU. */
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("iSCSI: Received malformed PDU from target %s (rc=%Rrc), ignoring\n", pImage->pszTargetName, rc));
+        rc = VINF_SUCCESS;
+    }
+
+    return rc;
+}
+
+/**
+ * Appends a key-value pair to the buffer. Normal ASCII strings (cbValue == 0) and large binary values
+ * of a given length (cbValue > 0) are directly supported. Other value types must be converted to ASCII
+ * by the caller. Strings must be in UTF-8 encoding.
+ *
+ * @returns VBOX status
+ * @param   pbBuf       Pointer to the key-value buffer.
+ * @param   cbBuf       Length of the key-value buffer.
+ * @param   pcbBufCurr  Currently used portion of the key-value buffer.
+ * @param   pszKey      Pointer to a string containing the key.
+ * @param   pszValue    Pointer to either a string containing the value or to a large binary value.
+ * @param   cbValue     Length of the binary value if applicable.
+ */
+static int iscsiTextAddKeyValue(uint8_t *pbBuf, size_t cbBuf, size_t *pcbBufCurr, const char *pcszKey,
+                                   const char *pcszValue, size_t cbValue)
+{
+    size_t cbBufTmp = *pcbBufCurr;
+    size_t cbKey = strlen(pcszKey);
+    size_t cbValueEnc;
+    uint8_t *pbCurr;
+
+    if (cbValue == 0)
+        cbValueEnc = strlen(pcszValue);
+    else
+        cbValueEnc = cbValue * 2 + 2;   /* 2 hex bytes per byte, 2 bytes prefix */
+
+    if (cbBuf < cbBufTmp + cbKey + 1 + cbValueEnc + 1)
+    {
+        /* Buffer would overflow, signal error. */
+        return VERR_BUFFER_OVERFLOW;
+    }
+
+    /*
+     * Append a key=value pair (zero terminated string) to the end of the buffer.
+     */
+    pbCurr = pbBuf + cbBufTmp;
+    memcpy(pbCurr, pcszKey, cbKey);
+    pbCurr += cbKey;
+    *pbCurr++ = '=';
+    if (cbValue == 0)
+    {
+        memcpy(pbCurr, pcszValue, cbValueEnc);
+        pbCurr += cbValueEnc;
+    }
+    else
+    {
+        *pbCurr++ = '0';
+        *pbCurr++ = 'x';
+        for (uint32_t i = 0; i < cbValue; i++)
+        {
+            uint8_t b;
+            b = pcszValue[i];
+            *pbCurr++ = NUM_2_HEX(b >> 4);
+            *pbCurr++ = NUM_2_HEX(b & 0xf);
+        }
+    }
+    *pbCurr = '\0';
+    *pcbBufCurr = cbBufTmp + cbKey + 1 + cbValueEnc + 1;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Retrieve the value for a given key from the key=value buffer.
+ *
+ * @returns VBOX status.
+ * @param   pbBuf       Buffer containing key=value pairs.
+ * @param   cbBuf       Length of buffer with key=value pairs.
+ * @param   pszKey      Pointer to key for which to retrieve the value.
+ * @param   ppszValue   Pointer to value string pointer.
+ */
+static int iscsiTextGetKeyValue(const uint8_t *pbBuf, size_t cbBuf, const char *pcszKey, const char **ppcszValue)
+{
+    size_t cbKey = strlen(pcszKey);
+
+    while (cbBuf != 0)
+    {
+        size_t cbKeyValNull = strlen((const char *)pbBuf) + 1;
+
+        if (strncmp(pcszKey, (const char *)pbBuf, cbKey) == 0 && pbBuf[cbKey] == '=')
+        {
+            *ppcszValue = (const char *)(pbBuf + cbKey + 1);
+            return VINF_SUCCESS;
+        }
+        pbBuf += cbKeyValNull;
+        cbBuf -= cbKeyValNull;
+    }
+    return VERR_INVALID_NAME;
+}
+
+
+/**
+ * Convert a long-binary value from a value string to the binary representation.
+ *
+ * @returns VBOX status
+ * @param   pszValue    Pointer to a string containing the textual value representation.
+ * @param   pbValue     Pointer to the value buffer for the binary value.
+ * @param   pcbValue    In: length of value buffer, out: actual length of binary value.
+ */
+static int iscsiStrToBinary(const char *pcszValue, uint8_t *pbValue, size_t *pcbValue)
+{
+    size_t cbValue = *pcbValue;
+    char c1, c2, c3, c4;
+    Assert(cbValue >= 1);
+
+    if (strlen(pcszValue) < 3)
+        return VERR_PARSE_ERROR;
+    if (*pcszValue++ != '0')
+        return VERR_PARSE_ERROR;
+    switch (*pcszValue++)
+    {
+        case 'x':
+        case 'X':
+            if (strlen(pcszValue) & 1)
+            {
+                c1 = *pcszValue++;
+                *pbValue++ = HEX_2_NUM(c1);
+                cbValue--;
+            }
+            while (*pcszValue != '\0')
+            {
+                if (cbValue == 0)
+                    return VERR_BUFFER_OVERFLOW;
+                c1 = *pcszValue++;
+                if ((c1 < '0' || c1 > '9') && (c1 < 'a' || c1 > 'f') && (c1 < 'A' || c1 > 'F'))
+                    return VERR_PARSE_ERROR;
+                c2 = *pcszValue++;
+                if ((c2 < '0' || c2 > '9') && (c2 < 'a' || c2 > 'f') && (c2 < 'A' || c2 > 'F'))
+                    return VERR_PARSE_ERROR;
+                *pbValue++ = (HEX_2_NUM(c1) << 4) | HEX_2_NUM(c2);
+                cbValue--;
+            }
+            *pcbValue -= cbValue;
+            break;
+        case 'b':
+        case 'B':
+            if ((strlen(pcszValue) & 3) != 0)
+                return VERR_PARSE_ERROR;
+            while (*pcszValue != '\0')
+            {
+                uint32_t temp;
+                if (cbValue == 0)
+                    return VERR_BUFFER_OVERFLOW;
+                c1 = *pcszValue++;
+                if ((c1 < 'A' || c1 > 'Z') && (c1 < 'a' || c1 >'z') && (c1 < '0' || c1 > '9') && (c1 != '+') && (c1 != '/'))
+                    return VERR_PARSE_ERROR;
+                c2 = *pcszValue++;
+                if ((c2 < 'A' || c2 > 'Z') && (c2 < 'a' || c2 >'z') && (c2 < '0' || c2 > '9') && (c2 != '+') && (c2 != '/'))
+                    return VERR_PARSE_ERROR;
+                c3 = *pcszValue++;
+                if ((c3 < 'A' || c3 > 'Z') && (c3 < 'a' || c3 >'z') && (c3 < '0' || c3 > '9') && (c3 != '+') && (c3 != '/') && (c3 != '='))
+                    return VERR_PARSE_ERROR;
+                c4 = *pcszValue++;
+                if (    (c3 == '=' && c4 != '=')
+                    ||  ((c4 < 'A' || c4 > 'Z') && (c4 < 'a' || c4 >'z') && (c4 < '0' || c4 > '9') && (c4 != '+') && (c4 != '/') && (c4 != '=')))
+                    return VERR_PARSE_ERROR;
+                temp = (B64_2_NUM(c1) << 18) | (B64_2_NUM(c2) << 12);
+                if (c3 == '=') {
+                    if (*pcszValue != '\0')
+                        return VERR_PARSE_ERROR;
+                    *pbValue++ = temp >> 16;
+                    cbValue--;
+                } else {
+                    temp |= B64_2_NUM(c3) << 6;
+                    if (c4 == '=') {
+                        if (*pcszValue != '\0')
+                            return VERR_PARSE_ERROR;
+                        if (cbValue < 2)
+                            return VERR_BUFFER_OVERFLOW;
+                        *pbValue++ = temp >> 16;
+                        *pbValue++ = (temp >> 8) & 0xff;
+                        cbValue -= 2;
+                    }
+                    else
+                    {
+                        temp |= B64_2_NUM(c4);
+                        if (cbValue < 3)
+                            return VERR_BUFFER_OVERFLOW;
+                        *pbValue++ = temp >> 16;
+                        *pbValue++ = (temp >> 8) & 0xff;
+                        *pbValue++ = temp & 0xff;
+                        cbValue -= 3;
+                    }
+                }
+            }
+            *pcbValue -= cbValue;
+            break;
+        default:
+            return VERR_PARSE_ERROR;
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Retrieve the relevant parameter values and update the initiator state.
+ *
+ * @returns VBOX status.
+ * @param   pImage      Current iSCSI initiator state.
+ * @param   pbBuf       Buffer containing key=value pairs.
+ * @param   cbBuf       Length of buffer with key=value pairs.
+ */
+static int iscsiUpdateParameters(PISCSIIMAGE pImage, const uint8_t *pbBuf, size_t cbBuf)
+{
+    int rc;
+    const char *pcszMaxRecvDataSegmentLength = NULL;
+    const char *pcszMaxBurstLength = NULL;
+    const char *pcszFirstBurstLength = NULL;
+    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxRecvDataSegmentLength", &pcszMaxRecvDataSegmentLength);
+    if (rc == VERR_INVALID_NAME)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+        return VERR_PARSE_ERROR;
+    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "MaxBurstLength", &pcszMaxBurstLength);
+    if (rc == VERR_INVALID_NAME)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+        return VERR_PARSE_ERROR;
+    rc = iscsiTextGetKeyValue(pbBuf, cbBuf, "FirstBurstLength", &pcszFirstBurstLength);
+    if (rc == VERR_INVALID_NAME)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+        return VERR_PARSE_ERROR;
+    if (pcszMaxRecvDataSegmentLength)
+    {
+        uint32_t cb = pImage->cbSendDataLength;
+        rc = RTStrToUInt32Full(pcszMaxRecvDataSegmentLength, 0, &cb);
+        AssertRC(rc);
+        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
+    }
+    if (pcszMaxBurstLength)
+    {
+        uint32_t cb = pImage->cbSendDataLength;
+        rc = RTStrToUInt32Full(pcszMaxBurstLength, 0, &cb);
+        AssertRC(rc);
+        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
+    }
+    if (pcszFirstBurstLength)
+    {
+        uint32_t cb = pImage->cbSendDataLength;
+        rc = RTStrToUInt32Full(pcszFirstBurstLength, 0, &cb);
+        AssertRC(rc);
+        pImage->cbSendDataLength = RT_MIN(pImage->cbSendDataLength, cb);
+    }
+    return VINF_SUCCESS;
+}
+
+
+static bool serial_number_less(uint32_t s1, uint32_t s2)
+{
+    return (s1 < s2 && s2 - s1 < 0x80000000) || (s1 > s2 && s1 - s2 > 0x80000000);
+}
+
+static bool serial_number_greater(uint32_t s1, uint32_t s2)
+{
+    return (s1 < s2 && s2 - s1 > 0x80000000) || (s1 > s2 && s1 - s2 < 0x80000000);
+}
+
+
+#ifdef IMPLEMENT_TARGET_AUTH
+static void chap_md5_generate_challenge(uint8_t *pbChallenge, size_t *pcbChallenge)
+{
+    uint8_t cbChallenge;
+
+    cbChallenge = RTrand_U8(CHAP_MD5_CHALLENGE_MIN, CHAP_MD5_CHALLENGE_MAX);
+    RTrand_bytes(pbChallenge, cbChallenge);
+    *pcbChallenge = cbChallenge;
+}
+#endif
+
+
+static void chap_md5_compute_response(uint8_t *pbResponse, uint8_t id, const uint8_t *pbChallenge, size_t cbChallenge,
+                                      const uint8_t *pbSecret, size_t cbSecret)
+{
+    RTMD5CONTEXT ctx;
+    uint8_t bId;
+
+    bId = id;
+    RTMd5Init(&ctx);
+    RTMd5Update(&ctx, &bId, 1);
+    RTMd5Update(&ctx, pbSecret, cbSecret);
+    RTMd5Update(&ctx, pbChallenge, cbChallenge);
+    RTMd5Final(pbResponse, &ctx);
+}
+
+/**
+ * Internal. - Wrapper around the extended select callback of the net interface.
+ */
+DECLINLINE(int) iscsiIoThreadWait(PISCSIIMAGE pImage, RTMSINTERVAL cMillies, uint32_t fEvents, uint32_t *pfEvents)
+{
+    return pImage->pInterfaceNetCallbacks->pfnSelectOneEx(pImage->Socket, fEvents, pfEvents, cMillies);
+}
+
+/**
+ * Internal. - Pokes a thread waiting for I/O.
+ */
+DECLINLINE(int) iscsiIoThreadPoke(PISCSIIMAGE pImage)
+{
+    return pImage->pInterfaceNetCallbacks->pfnPoke(pImage->Socket);
+}
+
+/**
+ * Internal. - Get the next request from the queue.
+ */
+DECLINLINE(PISCSICMD) iscsiCmdGet(PISCSIIMAGE pImage)
+{
+    int rc;
+    PISCSICMD pIScsiCmd = NULL;
+
+    rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
+    AssertRC(rc);
+
+    pIScsiCmd = pImage->pScsiReqQueue;
+    if (pIScsiCmd)
+    {
+        pImage->pScsiReqQueue = pIScsiCmd->pNext;
+        pIScsiCmd->pNext = NULL;
+    }
+
+    rc = RTSemMutexRelease(pImage->MutexReqQueue);
+    AssertRC(rc);
+
+    return pIScsiCmd;
+}
+
+
+/**
+ * Internal. - Adds the given command to the queue.
+ */
+DECLINLINE(int) iscsiCmdPut(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd)
+{
+    int rc = RTSemMutexRequest(pImage->MutexReqQueue, RT_INDEFINITE_WAIT);
+    AssertRC(rc);
+
+    pIScsiCmd->pNext = pImage->pScsiReqQueue;
+    pImage->pScsiReqQueue = pIScsiCmd;
+
+    rc = RTSemMutexRelease(pImage->MutexReqQueue);
+    AssertRC(rc);
+
+    iscsiIoThreadPoke(pImage);
+
+    return rc;
+}
+
+/**
+ * Internal. - Completes the request with the appropriate action.
+ *             Synchronous requests are completed with waking up the thread
+ *             and asynchronous ones by continuing the associated I/O context.
+ */
+static void iscsiCmdComplete(PISCSIIMAGE pImage, PISCSICMD pIScsiCmd, int rcCmd)
+{
+    LogFlowFunc(("pImage=%#p pIScsiCmd=%#p rcCmd=%Rrc\n", pImage, pIScsiCmd, rcCmd));
+
+    /* Remove from the table first. */
+    iscsiCmdRemove(pImage, pIScsiCmd->Itt);
+
+    /* Call completion callback. */
+    pIScsiCmd->pfnComplete(pImage, rcCmd, pIScsiCmd->pvUser);
+
+    /* Free command structure. */
+#ifdef DEBUG
+    memset(pIScsiCmd, 0xff, sizeof(ISCSICMD));
+#endif
+    RTMemFree(pIScsiCmd);
+}
+
+/**
+ * Reattaches the to the target after an error aborting
+ * pending commands and resending them.
+ *
+ * @param    pImage    iSCSI connection state.
+ */
+static void iscsiReattach(PISCSIIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+    PISCSICMD pIScsiCmdHead = NULL;
+    PISCSICMD pIScsiCmd = NULL;
+    PISCSICMD pIScsiCmdCur = NULL;
+    PISCSIPDUTX pIScsiPDUTx = NULL;
+
+    /* Close connection. */
+    iscsiTransportClose(pImage);
+    pImage->state = ISCSISTATE_FREE;
+
+    /* Reset PDU we are receiving. */
+    iscsiRecvPDUReset(pImage);
+
+    /*
+     * Abort all PDUs we are about to transmit,
+     * the command need a new Itt if the relogin is successful.
+     */
+    while (pImage->pIScsiPDUTxHead)
+    {
+        pIScsiPDUTx = pImage->pIScsiPDUTxHead;
+        pImage->pIScsiPDUTxHead = pIScsiPDUTx->pNext;
+
+        pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
+
+        if (pIScsiCmd)
+        {
+            /* Place on command list. */
+            pIScsiCmd->pNext = pIScsiCmdHead;
+            pIScsiCmdHead = pIScsiCmd;
+        }
+        RTMemFree(pIScsiPDUTx);
+    }
+
+    /* Clear the current PDU too. */
+    if (pImage->pIScsiPDUTxCur)
+    {
+        pIScsiPDUTx = pImage->pIScsiPDUTxCur;
+
+        pImage->pIScsiPDUTxCur = NULL;
+        pIScsiCmd = pIScsiPDUTx->pIScsiCmd;
+
+        if (pIScsiCmd)
+        {
+            pIScsiCmd->pNext = pIScsiCmdHead;
+            pIScsiCmdHead = pIScsiCmd;
+        }
+        RTMemFree(pIScsiPDUTx);
+    }
+
+    /*
+     * Get all commands which are waiting for a response
+     * They need to be resend too after a successful reconnect.
+     */
+    pIScsiCmd = iscsiCmdRemoveAll(pImage);
+
+    if (pIScsiCmd)
+    {
+        pIScsiCmdCur = pIScsiCmd;
+        while (pIScsiCmdCur->pNext)
+            pIScsiCmdCur = pIScsiCmdCur->pNext;
+
+        /*
+         * Place them in front of the list because they are the oldest requests
+         * and need to be processed first to minimize the risk to time out.
+         */
+        pIScsiCmdCur->pNext = pIScsiCmdHead;
+        pIScsiCmdHead = pIScsiCmd;
+    }
+
+    /* Try to attach. */
+    rc = iscsiAttach(pImage);
+    if (RT_SUCCESS(rc))
+    {
+        /* Phew, we have a connection again.
+         * Prepare new PDUs for the aborted commands.
+         */
+        while (pIScsiCmdHead)
+        {
+            pIScsiCmd = pIScsiCmdHead;
+            pIScsiCmdHead = pIScsiCmdHead->pNext;
+
+            pIScsiCmd->pNext = NULL;
+
+            rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
+            AssertRC(rc);
+        }
+    }
+    else
+    {
+        /*
+         * Still no luck, complete commands with error so the caller
+         * has a chance to inform the user and maybe resend the command.
+         */
+        while (pIScsiCmdHead)
+        {
+            pIScsiCmd = pIScsiCmdHead;
+            pIScsiCmdHead = pIScsiCmdHead->pNext;
+
+            iscsiCmdComplete(pImage, pIScsiCmd, VERR_BROKEN_PIPE);
+        }
+    }
+}
+
+/**
+ * Internal. Main iSCSI I/O worker.
+ */
+static DECLCALLBACK(int) iscsiIoThreadWorker(RTTHREAD ThreadSelf, void *pvUser)
+{
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pvUser;
+
+    /* Initialize the initial event mask. */
+    pImage->fPollEvents = VD_INTERFACETCPNET_EVT_READ | VD_INTERFACETCPNET_EVT_ERROR;
+
+    while (pImage->fRunning)
+    {
+        uint32_t fEvents;
+        int rc;
+
+        fEvents = 0;
+
+        /* Wait for work or for data from the target. */
+        RTMSINTERVAL msWait;
+
+        if (pImage->cCmdsWaiting)
+        {
+            pImage->fPollEvents &= ~VD_INTERFACETCPNET_HINT_INTERRUPT;
+            msWait = pImage->uReadTimeout;
+        }
+        else
+        {
+            pImage->fPollEvents |= VD_INTERFACETCPNET_HINT_INTERRUPT;
+            msWait = RT_INDEFINITE_WAIT;
+        }
+
+        LogFlow(("Waiting for events fPollEvents=%#x\n", pImage->fPollEvents));
+        rc = iscsiIoThreadWait(pImage, msWait, pImage->fPollEvents, &fEvents);
+        if (rc == VERR_INTERRUPTED)
+        {
+            /* Check the queue. */
+            PISCSICMD pIScsiCmd = iscsiCmdGet(pImage);
+
+            while (pIScsiCmd)
+            {
+                switch (pIScsiCmd->enmCmdType)
+                {
+                    case ISCSICMDTYPE_REQ:
+                    {
+                        /* If there is no connection complete the command with an error. */
+                        if (RT_LIKELY(iscsiIsClientConnected(pImage)))
+                        {
+                            rc = iscsiPDUTxPrepare(pImage, pIScsiCmd);
+                            AssertRC(rc);
+                        }
+                        else
+                            iscsiCmdComplete(pImage, pIScsiCmd, VERR_NET_CONNECTION_REFUSED);
+                        break;
+                    }
+                    case ISCSICMDTYPE_EXEC:
+                    {
+                        rc = pIScsiCmd->CmdType.Exec.pfnExec(pIScsiCmd->CmdType.Exec.pvUser);
+                        iscsiCmdComplete(pImage, pIScsiCmd, rc);
+                        break;
+                    }
+                    default:
+                        AssertMsgFailed(("Invalid command type %d\n", pIScsiCmd->enmCmdType));
+                }
+
+                pIScsiCmd = iscsiCmdGet(pImage);
+            }
+        }
+        else if (rc == VERR_TIMEOUT && pImage->cCmdsWaiting)
+        {
+            /*
+             * We are waiting for a response from the target but
+             * it didn't answered yet.
+             * We assume the connection is broken and try to reconnect.
+             */
+            LogFlow(("Timed out while waiting for an answer from the target, reconnecting\n"));
+            iscsiReattach(pImage);
+        }
+        else if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
+        {
+            Assert(pImage->state == ISCSISTATE_NORMAL);
+            LogFlow(("Got socket events %#x\n", fEvents));
+
+            if (fEvents & VD_INTERFACETCPNET_EVT_READ)
+            {
+                /* Continue or start a new PDU receive task */
+                LogFlow(("There is data on the socket\n"));
+                rc = iscsiRecvPDUAsync(pImage);
+                if (rc == VERR_BROKEN_PIPE)
+                    iscsiReattach(pImage);
+                else if (RT_FAILURE(rc))
+                    LogRel(("iSCSI: Handling incoming request failed %Rrc\n", rc));
+            }
+
+            if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
+            {
+                LogFlow(("The socket is writable\n"));
+                rc = iscsiSendPDUAsync(pImage);
+                if (RT_FAILURE(rc))
+                    LogRel(("iSCSI: Sending PDU failed %Rrc\n", rc));
+            }
+
+            if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
+            {
+                LogFlow(("An error ocurred\n"));
+                iscsiReattach(pImage);
+            }
+        }
+        else
+        {
+            LogRel(("iSCSI: Waiting for I/O failed rc=%Rrc\n", rc));
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal. - Enqueues a request asynchronously.
+ */
+static int iscsiCommandAsync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq,
+                             PFNISCSICMDCOMPLETED pfnComplete, void *pvUser)
+{
+    int rc;
+
+    if (pImage->fExtendedSelectSupported)
+    {
+        PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
+        if (!pIScsiCmd)
+            return VERR_NO_MEMORY;
+
+        /* Init the command structure. */
+        pIScsiCmd->pNext                    = NULL;
+        pIScsiCmd->enmCmdType               = ISCSICMDTYPE_REQ;
+        pIScsiCmd->pfnComplete              = pfnComplete;
+        pIScsiCmd->pvUser                   = pvUser;
+        pIScsiCmd->CmdType.ScsiReq.pScsiReq = pScsiReq;
+
+        rc = iscsiCmdPut(pImage, pIScsiCmd);
+        if (RT_FAILURE(rc))
+            RTMemFree(pIScsiCmd);
+    }
+    else
+        rc = VERR_NOT_SUPPORTED;
+
+    return rc;
+}
+
+static void iscsiCommandCompleteSync(PISCSIIMAGE pImage, int rcReq, void *pvUser)
+{
+    PISCSICMDSYNC pIScsiCmdSync = (PISCSICMDSYNC)pvUser;
+
+    pIScsiCmdSync->rcCmd = rcReq;
+    int rc = RTSemEventSignal(pIScsiCmdSync->EventSem);
+    AssertRC(rc);
+}
+
+/**
+ * Internal. - Enqueues a request in a synchronous fashion
+ * i.e. returns when the request completed.
+ */
+static int iscsiCommandSync(PISCSIIMAGE pImage, PSCSIREQ pScsiReq, bool fRetry, int rcSense)
+{
+    int rc;
+
+    if (pImage->fExtendedSelectSupported)
+    {
+        ISCSICMDSYNC IScsiCmdSync;
+
+        /* Create event semaphore. */
+        rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        if (fRetry)
+        {
+            for (unsigned i = 0; i < 10; i++)
+            {
+                rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
+                if (RT_FAILURE(rc))
+                    break;
+
+                rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
+                AssertRC(rc);
+                rc = IScsiCmdSync.rcCmd;
+
+                if (    (RT_SUCCESS(rc) && !pScsiReq->cbSense)
+                    ||  RT_FAILURE(rc))
+                    break;
+                rc = rcSense;
+            }
+        }
+        else
+        {
+            rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandCompleteSync, &IScsiCmdSync);
+            if (RT_SUCCESS(rc))
+            {
+                rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
+                AssertRC(rc);
+                rc = IScsiCmdSync.rcCmd;
+
+                if (RT_FAILURE(rc) || pScsiReq->cbSense > 0)
+                        rc = rcSense;
+            }
+        }
+
+        RTSemEventDestroy(IScsiCmdSync.EventSem);
+    }
+    else
+    {
+        if (fRetry)
+        {
+            for (unsigned i = 0; i < 10; i++)
+            {
+                rc = iscsiCommand(pImage, pScsiReq);
+                if (    (RT_SUCCESS(rc) && !pScsiReq->cbSense)
+                    ||  RT_FAILURE(rc))
+                    break;
+                rc = rcSense;
+            }
+        }
+        else
+        {
+            rc = iscsiCommand(pImage, pScsiReq);
+            if (RT_SUCCESS(rc) && pScsiReq->cbSense > 0)
+                rc = rcSense;
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Internal. - Executes a given function in a synchronous fashion
+ *             on the I/O thread if available.
+ */
+static int iscsiExecSync(PISCSIIMAGE pImage, PFNISCSIEXEC pfnExec, void *pvUser)
+{
+    int rc;
+
+    if (pImage->fExtendedSelectSupported)
+    {
+        ISCSICMDSYNC IScsiCmdSync;
+        PISCSICMD pIScsiCmd = (PISCSICMD)RTMemAllocZ(sizeof(ISCSICMD));
+        if (!pIScsiCmd)
+            return VERR_NO_MEMORY;
+
+        /* Create event semaphore. */
+        rc = RTSemEventCreate(&IScsiCmdSync.EventSem);
+        if (RT_FAILURE(rc))
+        {
+            RTMemFree(pIScsiCmd);
+            return rc;
+        }
+
+        /* Init the command structure. */
+        pIScsiCmd->pNext                = NULL;
+        pIScsiCmd->enmCmdType           = ISCSICMDTYPE_EXEC;
+        pIScsiCmd->pfnComplete          = iscsiCommandCompleteSync;
+        pIScsiCmd->pvUser               = &IScsiCmdSync;
+        pIScsiCmd->CmdType.Exec.pfnExec = pfnExec;
+        pIScsiCmd->CmdType.Exec.pvUser  = pvUser;
+
+        rc = iscsiCmdPut(pImage, pIScsiCmd);
+        if (RT_FAILURE(rc))
+            RTMemFree(pIScsiCmd);
+        else
+        {
+            rc = RTSemEventWait(IScsiCmdSync.EventSem, RT_INDEFINITE_WAIT);
+            AssertRC(rc);
+            rc = IScsiCmdSync.rcCmd;
+        }
+
+        RTSemEventDestroy(IScsiCmdSync.EventSem);
+    }
+    else
+    {
+        /* No I/O thread, execute in the current thread. */
+        rc = pfnExec(pvUser);
+    }
+
+    return rc;
+}
+
+
+static void iscsiCommandAsyncComplete(PISCSIIMAGE pImage, int rcReq, void *pvUser)
+{
+    bool fComplete = true;
+    size_t cbTransfered = 0;
+    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)pvUser;
+    PSCSIREQ pScsiReq = pReqAsync->pScsiReq;
+
+    if (   RT_SUCCESS(rcReq)
+        && pScsiReq->cbSense > 0)
+    {
+        /* Try again if possible. */
+        if (pReqAsync->cSenseRetries > 0)
+        {
+            pReqAsync->cSenseRetries--;
+            pScsiReq->cbSense = sizeof(pReqAsync->abSense);
+            int rc = iscsiCommandAsync(pImage, pScsiReq, iscsiCommandAsyncComplete, pReqAsync);
+            if (RT_SUCCESS(rc))
+                fComplete = false;
+            else
+                rcReq = pReqAsync->rcSense;
+        }
+        else
+            rcReq = pReqAsync->rcSense;
+    }
+
+    if (fComplete)
+    {
+        if (pScsiReq->enmXfer == SCSIXFER_FROM_TARGET)
+            cbTransfered = pScsiReq->cbT2IData;
+        else if (pScsiReq->enmXfer == SCSIXFER_TO_TARGET)
+            cbTransfered = pScsiReq->cbI2TData;
+        else
+            AssertMsg(pScsiReq->enmXfer == SCSIXFER_NONE, ("To/From transfers are not supported yet\n"));
+
+        /* Continue I/O context. */
+        pImage->pInterfaceIoCallbacks->pfnIoCtxCompleted(pImage->pInterfaceIo->pvUser,
+                                                         pReqAsync->pIoCtx, rcReq,
+                                                         cbTransfered);
+
+        RTMemFree(pScsiReq);
+        RTMemFree(pReqAsync);
+    }
+}
+
+
+/**
+ * Internal. Free all allocated space for representing an image, and optionally
+ * delete the image from disk.
+ */
+static int iscsiFreeImage(PISCSIIMAGE pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+    Assert(!fDelete); /* This MUST be false, the flag isn't supported. */
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (pImage->Mutex != NIL_RTSEMMUTEX)
+        {
+            /* Detaching only makes sense when the mutex is there. Otherwise the
+             * failure happened long before we could attach to the target. */
+            iscsiExecSync(pImage, iscsiDetach, pImage);
+            RTSemMutexDestroy(pImage->Mutex);
+            pImage->Mutex = NIL_RTSEMMUTEX;
+        }
+        if (pImage->hThreadIo != NIL_RTTHREAD)
+        {
+            ASMAtomicXchgBool(&pImage->fRunning, false);
+            rc = iscsiIoThreadPoke(pImage);
+            AssertRC(rc);
+
+            /* Wait for the thread to terminate. */
+            rc = RTThreadWait(pImage->hThreadIo, RT_INDEFINITE_WAIT, NULL);
+            AssertRC(rc);
+        }
+        /* Destroy the socket. */
+        if (pImage->Socket != NIL_VDSOCKET)
+        {
+            pImage->pInterfaceNetCallbacks->pfnSocketDestroy(pImage->Socket);
+        }
+        if (pImage->MutexReqQueue != NIL_RTSEMMUTEX)
+        {
+            RTSemMutexDestroy(pImage->MutexReqQueue);
+            pImage->MutexReqQueue = NIL_RTSEMMUTEX;
+        }
+        if (pImage->pszTargetName)
+        {
+            RTMemFree(pImage->pszTargetName);
+            pImage->pszTargetName = NULL;
+        }
+        if (pImage->pszInitiatorName)
+        {
+            if (pImage->fAutomaticInitiatorName)
+                RTStrFree(pImage->pszInitiatorName);
+            else
+                RTMemFree(pImage->pszInitiatorName);
+            pImage->pszInitiatorName = NULL;
+        }
+        if (pImage->pszInitiatorUsername)
+        {
+            RTMemFree(pImage->pszInitiatorUsername);
+            pImage->pszInitiatorUsername = NULL;
+        }
+        if (pImage->pbInitiatorSecret)
+        {
+            RTMemFree(pImage->pbInitiatorSecret);
+            pImage->pbInitiatorSecret = NULL;
+        }
+        if (pImage->pszTargetUsername)
+        {
+            RTMemFree(pImage->pszTargetUsername);
+            pImage->pszTargetUsername = NULL;
+        }
+        if (pImage->pbTargetSecret)
+        {
+            RTMemFree(pImage->pbTargetSecret);
+            pImage->pbTargetSecret = NULL;
+        }
+        if (pImage->pvRecvPDUBuf)
+        {
+            RTMemFree(pImage->pvRecvPDUBuf);
+            pImage->pvRecvPDUBuf = NULL;
+        }
+
+        pImage->cbRecvPDUResidual = 0;
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Internal: Open an image, constructing all necessary data structures.
+ */
+static int iscsiOpenImage(PISCSIIMAGE pImage, unsigned uOpenFlags)
+{
+    int rc;
+    char *pszLUN = NULL, *pszLUNInitial = NULL;
+    bool fLunEncoded = false;
+    uint32_t uWriteSplitDef = 0;
+    uint32_t uTimeoutDef = 0;
+    uint64_t uHostIPTmp = 0;
+    bool fHostIPDef = 0;
+    rc = RTStrToUInt32Full(s_iscsiConfigDefaultWriteSplit, 0, &uWriteSplitDef);
+    AssertRC(rc);
+    rc = RTStrToUInt32Full(s_iscsiConfigDefaultTimeout, 0, &uTimeoutDef);
+    AssertRC(rc);
+    rc = RTStrToUInt64Full(s_iscsiConfigDefaultHostIPStack, 0, &uHostIPTmp);
+    AssertRC(rc);
+    fHostIPDef = !!uHostIPTmp;
+
+    pImage->uOpenFlags      = uOpenFlags;
+
+    /* Get error signalling interface. */
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get TCP network stack interface. */
+    pImage->pInterfaceNet = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_TCPNET);
+    if (pImage->pInterfaceNet)
+        pImage->pInterfaceNetCallbacks = VDGetInterfaceTcpNet(pImage->pInterfaceNet);
+    else
+    {
+        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
+                        RT_SRC_POS, N_("iSCSI: TCP network stack interface missing"));
+        goto out;
+    }
+
+    /* Get configuration interface. */
+    pImage->pInterfaceConfig = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_CONFIG);
+    if (pImage->pInterfaceConfig)
+        pImage->pInterfaceConfigCallbacks = VDGetInterfaceConfig(pImage->pInterfaceConfig);
+    else
+    {
+        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
+                        RT_SRC_POS, N_("iSCSI: configuration interface missing"));
+        goto out;
+    }
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIo = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    if (pImage->pInterfaceIo)
+        pImage->pInterfaceIoCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIo);
+    else
+    {
+        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_INTERFACE,
+                        RT_SRC_POS, N_("iSCSI: I/O interface missing"));
+        goto out;
+    }
+
+    /* This ISID will be adjusted later to make it unique on this host. */
+    pImage->ISID            = 0x800000000000ULL | 0x001234560000ULL;
+    pImage->cISCSIRetries   = 10;
+    pImage->state           = ISCSISTATE_FREE;
+    pImage->pvRecvPDUBuf    = RTMemAlloc(ISCSI_RECV_PDU_BUFFER_SIZE);
+    pImage->cbRecvPDUBuf    = ISCSI_RECV_PDU_BUFFER_SIZE;
+    if (pImage->pvRecvPDUBuf == NULL)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->Mutex           = NIL_RTSEMMUTEX;
+    pImage->MutexReqQueue   = NIL_RTSEMMUTEX;
+    rc = RTSemMutexCreate(&pImage->Mutex);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = RTSemMutexCreate(&pImage->MutexReqQueue);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Validate configuration, detect unknown keys. */
+    if (!VDCFGAreKeysValid(pImage->pInterfaceConfigCallbacks,
+                           pImage->pInterfaceConfig->pvUser,
+                           "TargetName\0InitiatorName\0LUN\0TargetAddress\0InitiatorUsername\0InitiatorSecret\0TargetUsername\0TargetSecret\0WriteSplit\0Timeout\0HostIPStack\0"))
+    {
+        rc = iscsiError(pImage, VERR_VD_ISCSI_UNKNOWN_CFG_VALUES, RT_SRC_POS, N_("iSCSI: configuration error: unknown configuration keys present"));
+        goto out;
+    }
+
+    /* Query the iSCSI upper level configuration. */
+    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
+                               pImage->pInterfaceConfig->pvUser,
+                               "TargetName", &pImage->pszTargetName);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetName as string"));
+        goto out;
+    }
+    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
+                               pImage->pInterfaceConfig->pvUser,
+                               "InitiatorName", &pImage->pszInitiatorName);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+    {
+        pImage->fAutomaticInitiatorName = true;
+        rc = VINF_SUCCESS;
+    }
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorName as string"));
+        goto out;
+    }
+    rc = VDCFGQueryStringAllocDef(pImage->pInterfaceConfigCallbacks,
+                                  pImage->pInterfaceConfig->pvUser,
+                                  "LUN", &pszLUN, s_iscsiConfigDefaultLUN);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read LUN as string"));
+        goto out;
+    }
+    pszLUNInitial = pszLUN;
+    if (!strncmp(pszLUN, "enc", 3))
+    {
+        fLunEncoded = true;
+        pszLUN += 3;
+    }
+    rc = RTStrToUInt64Full(pszLUN, 0, &pImage->LUN);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to convert LUN to integer"));
+        goto out;
+    }
+    if (!fLunEncoded)
+    {
+        if (pImage->LUN <= 255)
+        {
+            pImage->LUN = pImage->LUN << 48; /* uses peripheral device addressing method */
+        }
+        else if (pImage->LUN <= 16383)
+        {
+            pImage->LUN = (pImage->LUN << 48) | RT_BIT_64(62); /* uses flat space addressing method */
+        }
+        else
+        {
+            rc = VERR_OUT_OF_RANGE;
+            rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: LUN number out of range (0-16383)"));
+            goto out;
+        }
+    }
+    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
+                               pImage->pInterfaceConfig->pvUser,
+                               "TargetAddress", &pImage->pszTargetAddress);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetAddress as string"));
+        goto out;
+    }
+    pImage->pszInitiatorUsername = NULL;
+    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
+                               pImage->pInterfaceConfig->pvUser,
+                               "InitiatorUsername",
+                               &pImage->pszInitiatorUsername);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorUsername as string"));
+        goto out;
+    }
+    pImage->pbInitiatorSecret = NULL;
+    pImage->cbInitiatorSecret = 0;
+    rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
+                              pImage->pInterfaceConfig->pvUser,
+                              "InitiatorSecret",
+                              (void **)&pImage->pbInitiatorSecret,
+                              &pImage->cbInitiatorSecret);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read InitiatorSecret as byte string"));
+        goto out;
+    }
+    pImage->pszTargetUsername = NULL;
+    rc = VDCFGQueryStringAlloc(pImage->pInterfaceConfigCallbacks,
+                               pImage->pInterfaceConfig->pvUser,
+                               "TargetUsername",
+                               &pImage->pszTargetUsername);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetUsername as string"));
+        goto out;
+    }
+    pImage->pbTargetSecret = NULL;
+    pImage->cbTargetSecret = 0;
+    rc = VDCFGQueryBytesAlloc(pImage->pInterfaceConfigCallbacks,
+                              pImage->pInterfaceConfig->pvUser,
+                              "TargetSecret", (void **)&pImage->pbTargetSecret,
+                              &pImage->cbTargetSecret);
+    if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
+        rc = VINF_SUCCESS;
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read TargetSecret as byte string"));
+        goto out;
+    }
+    rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
+                          pImage->pInterfaceConfig->pvUser,
+                          "WriteSplit", &pImage->cbWriteSplit,
+                          uWriteSplitDef);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read WriteSplit as U32"));
+        goto out;
+    }
+
+    pImage->pszHostname    = NULL;
+    pImage->uPort          = 0;
+    pImage->Socket         = NIL_VDSOCKET;
+    /* Query the iSCSI lower level configuration. */
+    rc = VDCFGQueryU32Def(pImage->pInterfaceConfigCallbacks,
+                          pImage->pInterfaceConfig->pvUser,
+                          "Timeout", &pImage->uReadTimeout,
+                          uTimeoutDef);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read Timeout as U32"));
+        goto out;
+    }
+    rc = VDCFGQueryBoolDef(pImage->pInterfaceConfigCallbacks,
+                           pImage->pInterfaceConfig->pvUser,
+                           "HostIPStack", &pImage->fHostIP,
+                           fHostIPDef);
+    if (RT_FAILURE(rc))
+    {
+        rc = iscsiError(pImage, rc, RT_SRC_POS, N_("iSCSI: configuration error: failed to read HostIPStack as boolean"));
+        goto out;
+    }
+
+    /* Don't actually establish iSCSI transport connection if this is just an
+     * open to query the image information and the host IP stack isn't used.
+     * Even trying is rather useless, as in this context the InTnet IP stack
+     * isn't present. Returning dummies is the best possible result anyway. */
+    if ((uOpenFlags & VD_OPEN_FLAGS_INFO) && !pImage->fHostIP)
+    {
+        LogFunc(("Not opening the transport connection as IntNet IP stack is not available. Will return dummies\n"));
+        goto out;
+    }
+
+    memset(pImage->aCmdsWaiting, 0, sizeof(pImage->aCmdsWaiting));
+    pImage->cbRecvPDUResidual = 0;
+
+    /* Create the socket structure. */
+    rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT,
+                                                         &pImage->Socket);
+    if (RT_SUCCESS(rc))
+    {
+        pImage->fExtendedSelectSupported = true;
+        pImage->fRunning = true;
+        rc = RTThreadCreate(&pImage->hThreadIo, iscsiIoThreadWorker, pImage, 0,
+                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "iSCSI-Io");
+        if (RT_FAILURE(rc))
+        {
+            LogFunc(("Creating iSCSI I/O thread failed rc=%Rrc\n", rc));
+            goto out;
+        }
+    }
+    else if (rc == VERR_NOT_SUPPORTED)
+    {
+        /* Async I/O is not supported without extended select. */
+        if ((uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO))
+        {
+            LogFunc(("Extended select is not supported by the interface but async I/O is requested -> %Rrc\n", rc));
+            goto out;
+        }
+        else
+        {
+            pImage->fExtendedSelectSupported = false;
+            rc = pImage->pInterfaceNetCallbacks->pfnSocketCreate(0, &pImage->Socket);
+            if (RT_FAILURE(rc))
+            {
+                LogFunc(("Creating socket failed -> %Rrc\n", rc));
+                goto out;
+            }
+        }
+    }
+    else
+    {
+        LogFunc(("Creating socket failed -> %Rrc\n", rc));
+        goto out;
+    }
+
+    /*
+     * Attach to the iSCSI target. This implicitly establishes the iSCSI
+     * transport connection.
+     */
+    rc = iscsiExecSync(pImage, iscsiAttach, pImage);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("iSCSI: could not open target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
+        goto out;
+    }
+    LogFlowFunc(("target '%s' opened successfully\n", pImage->pszTargetName));
+
+    SCSIREQ sr;
+    RTSGSEG DataSeg;
+    uint8_t sense[96];
+    uint8_t data8[8];
+    uint8_t data12[12];
+
+    /*
+     * Inquire available LUNs - purely dummy request.
+     */
+    uint8_t CDB_rlun[12];
+    uint8_t rlundata[16];
+    CDB_rlun[0] = SCSI_REPORT_LUNS;
+    CDB_rlun[1] = 0;        /* reserved */
+    CDB_rlun[2] = 0;        /* reserved */
+    CDB_rlun[3] = 0;        /* reserved */
+    CDB_rlun[4] = 0;        /* reserved */
+    CDB_rlun[5] = 0;        /* reserved */
+    CDB_rlun[6] = sizeof(rlundata) >> 24;
+    CDB_rlun[7] = (sizeof(rlundata) >> 16) & 0xff;
+    CDB_rlun[8] = (sizeof(rlundata) >> 8) & 0xff;
+    CDB_rlun[9] = sizeof(rlundata) & 0xff;
+    CDB_rlun[10] = 0;       /* reserved */
+    CDB_rlun[11] = 0;       /* control */
+
+    DataSeg.pvSeg = rlundata;
+    DataSeg.cbSeg = sizeof(rlundata);
+
+    sr.enmXfer = SCSIXFER_FROM_TARGET;
+    sr.cbCDB = sizeof(CDB_rlun);
+    sr.pvCDB = CDB_rlun;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = DataSeg.cbSeg;
+    sr.paT2ISegs = &DataSeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense   = sizeof(sense);
+    sr.pvSense   = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, false, VERR_INVALID_STATE);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("iSCSI: Could not get LUN info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
+        return rc;
+    }
+
+    /*
+     * Inquire device characteristics - no tapes, scanners etc., please.
+     */
+    uint8_t CDB_inq[6];
+    CDB_inq[0] = SCSI_INQUIRY;
+    CDB_inq[1] = 0;         /* reserved */
+    CDB_inq[2] = 0;         /* reserved */
+    CDB_inq[3] = 0;         /* reserved */
+    CDB_inq[4] = sizeof(data8);
+    CDB_inq[5] = 0;         /* control */
+
+    DataSeg.pvSeg = data8;
+    DataSeg.cbSeg = sizeof(data8);
+
+    sr.enmXfer = SCSIXFER_FROM_TARGET;
+    sr.cbCDB = sizeof(CDB_inq);
+    sr.pvCDB = CDB_inq;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = DataSeg.cbSeg;
+    sr.paT2ISegs = &DataSeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense = sizeof(sense);
+    sr.pvSense = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
+    if (RT_SUCCESS(rc))
+    {
+        uint8_t devType = (sr.cbT2IData > 0) ? data8[0] & SCSI_DEVTYPE_MASK : 255;
+        if (devType != SCSI_DEVTYPE_DISK)
+        {
+            rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
+                            RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports device type=%u"),
+                            pImage->pszTargetAddress, pImage->pszTargetName,
+                            pImage->LUN, devType);
+            LogRel(("iSCSI: Unsupported SCSI peripheral device type %d for target %s\n", devType & SCSI_DEVTYPE_MASK, pImage->pszTargetName));
+            goto out;
+        }
+        uint8_t uCmdQueue = (sr.cbT2IData >= 8) ? data8[7] & SCSI_INQUIRY_CMDQUE_MASK : 0;
+        if (uCmdQueue > 0)
+            pImage->fCmdQueuingSupported = true;
+        else if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
+        {
+            rc = VERR_NOT_SUPPORTED;
+            goto out;
+        }
+
+        LogRel(("iSCSI: target address %s, target name %s, %s command queuing\n",
+                pImage->pszTargetAddress, pImage->pszTargetName,
+                pImage->fCmdQueuingSupported ? "supports" : "doesn't support"));
+    }
+    else
+    {
+        LogRel(("iSCSI: Could not get INQUIRY info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
+        goto out;
+    }
+
+    /*
+     * Query write disable bit in the device specific parameter entry in the
+     * mode parameter header. Refuse read/write opening of read only disks.
+     */
+
+    uint8_t CDB_ms[6];
+    uint8_t data4[4];
+    CDB_ms[0] = SCSI_MODE_SENSE_6;
+    CDB_ms[1] = 0;          /* dbd=0/reserved */
+    CDB_ms[2] = 0x3f;       /* pc=0/page code=0x3f, ask for all pages */
+    CDB_ms[3] = 0;          /* subpage code=0, return everything in page_0 format */
+    CDB_ms[4] = sizeof(data4); /* allocation length=4 */
+    CDB_ms[5] = 0;          /* control */
+
+    DataSeg.pvSeg = data4;
+    DataSeg.cbSeg = sizeof(data4);
+
+    sr.enmXfer = SCSIXFER_FROM_TARGET;
+    sr.cbCDB = sizeof(CDB_ms);
+    sr.pvCDB = CDB_ms;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = DataSeg.cbSeg;
+    sr.paT2ISegs = &DataSeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense = sizeof(sense);
+    sr.pvSense = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, true /* fRetry */, VERR_INVALID_STATE);
+    if (RT_SUCCESS(rc))
+    {
+        if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY) && data4[2] & 0x80)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+    }
+    else
+    {
+        LogRel(("iSCSI: Could not get MODE SENSE info for target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
+        goto out;
+    }
+
+    /*
+     * Determine sector size and capacity of the volume immediately.
+     */
+    uint8_t CDB_cap[16];
+
+    RT_ZERO(data12);
+    RT_ZERO(CDB_cap);
+    CDB_cap[0] = SCSI_SERVICE_ACTION_IN_16;
+    CDB_cap[1] = SCSI_SVC_ACTION_IN_READ_CAPACITY_16;   /* subcommand */
+    CDB_cap[10+3] = sizeof(data12);                     /* allocation length (dword) */
+
+    DataSeg.pvSeg = data12;
+    DataSeg.cbSeg = sizeof(data12);
+
+    sr.enmXfer = SCSIXFER_FROM_TARGET;
+    sr.cbCDB = sizeof(CDB_cap);
+    sr.pvCDB = CDB_cap;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = DataSeg.cbSeg;
+    sr.paT2ISegs = &DataSeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense = sizeof(sense);
+    sr.pvSense = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+    if (   RT_SUCCESS(rc)
+        && sr.status == SCSI_STATUS_OK)
+    {
+        pImage->cVolume = RT_BE2H_U64(*(uint64_t *)&data12[0]);
+        pImage->cVolume++;
+        pImage->cbSector = RT_BE2H_U32(*(uint32_t *)&data12[8]);
+        pImage->cbSize = pImage->cVolume * pImage->cbSector;
+        if (pImage->cVolume == 0 || pImage->cbSector != 512 || pImage->cbSize < pImage->cVolume)
+        {
+            rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
+                            RT_SRC_POS, N_("iSCSI: target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
+                            pImage->pszTargetAddress, pImage->pszTargetName,
+                            pImage->LUN, pImage->cVolume, pImage->cbSector);
+        }
+    }
+    else
+    {
+        uint8_t CDB_capfb[10];
+
+        RT_ZERO(data8);
+        CDB_capfb[0] = SCSI_READ_CAPACITY;
+        CDB_capfb[1] = 0;   /* reserved */
+        CDB_capfb[2] = 0;   /* reserved */
+        CDB_capfb[3] = 0;   /* reserved */
+        CDB_capfb[4] = 0;   /* reserved */
+        CDB_capfb[5] = 0;   /* reserved */
+        CDB_capfb[6] = 0;   /* reserved */
+        CDB_capfb[7] = 0;   /* reserved */
+        CDB_capfb[8] = 0;   /* reserved */
+        CDB_capfb[9] = 0;   /* control */
+
+        DataSeg.pvSeg = data8;
+        DataSeg.cbSeg = sizeof(data8);
+
+        sr.enmXfer = SCSIXFER_FROM_TARGET;
+        sr.cbCDB = sizeof(CDB_capfb);
+        sr.pvCDB = CDB_capfb;
+        sr.cbI2TData = 0;
+        sr.paI2TSegs = NULL;
+        sr.cI2TSegs  = 0;
+        sr.cbT2IData = DataSeg.cbSeg;
+        sr.paT2ISegs = &DataSeg;
+        sr.cT2ISegs  = 1;
+        sr.cbSense = sizeof(sense);
+        sr.pvSense = sense;
+
+        rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+        if (RT_SUCCESS(rc))
+        {
+            pImage->cVolume = (data8[0] << 24) | (data8[1] << 16) | (data8[2] << 8) | data8[3];
+            pImage->cVolume++;
+            pImage->cbSector = (data8[4] << 24) | (data8[5] << 16) | (data8[6] << 8) | data8[7];
+            pImage->cbSize = pImage->cVolume * pImage->cbSector;
+            if (pImage->cVolume == 0 || pImage->cbSector != 512)
+            {
+                rc = iscsiError(pImage, VERR_VD_ISCSI_INVALID_TYPE,
+                                RT_SRC_POS, N_("iSCSI: fallback capacity detectio for target address %s, target name %s, SCSI LUN %lld reports media sector count=%llu sector size=%u"),
+                                pImage->pszTargetAddress, pImage->pszTargetName,
+                                pImage->LUN, pImage->cVolume, pImage->cbSector);
+            }
+        }
+        else
+        {
+            LogRel(("iSCSI: Could not determine capacity of target %s, rc=%Rrc\n", pImage->pszTargetName, rc));
+            goto out;
+        }
+    }
+
+    /*
+     * Check the read and write cache bits.
+     * Try to enable the cache if it is disabled.
+     *
+     * We already checked that this is a block access device. No need
+     * to do it again.
+     */
+    uint8_t aCachingModePage[32];
+    uint8_t aCDBModeSense6[6];
+
+    memset(aCachingModePage, '\0', sizeof(aCachingModePage));
+    aCDBModeSense6[0] = SCSI_MODE_SENSE_6;
+    aCDBModeSense6[1] = 0;
+    aCDBModeSense6[2] = (0x00 << 6) | (0x08 & 0x3f); /* Current values and caching mode page */
+    aCDBModeSense6[3] = 0; /* Sub page code. */
+    aCDBModeSense6[4] = sizeof(aCachingModePage) & 0xff;
+    aCDBModeSense6[5] = 0;
+
+    DataSeg.pvSeg = aCachingModePage;
+    DataSeg.cbSeg = sizeof(aCachingModePage);
+
+    sr.enmXfer = SCSIXFER_FROM_TARGET;
+    sr.cbCDB = sizeof(aCDBModeSense6);
+    sr.pvCDB = aCDBModeSense6;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = DataSeg.cbSeg;
+    sr.paT2ISegs = &DataSeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense = sizeof(sense);
+    sr.pvSense = sense;
+    rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+    if (   RT_SUCCESS(rc)
+        && (sr.status == SCSI_STATUS_OK)
+        && (aCachingModePage[0] >= 15)
+        && (aCachingModePage[4 + aCachingModePage[3]] & 0x3f) == 0x08
+        && (aCachingModePage[4 + aCachingModePage[3] + 1] > 3))
+    {
+        uint32_t Offset = 4 + aCachingModePage[3];
+        /*
+         * Check if the read and/or the write cache is disabled.
+         * The write cache is disabled if bit 2 (WCE) is zero and
+         * the read cache is disabled if bit 0 (RCD) is set.
+         */
+        if (!ASMBitTest(&aCachingModePage[Offset + 2], 2) || ASMBitTest(&aCachingModePage[Offset + 2], 0))
+        {
+            /*
+             * Write Cache Enable (WCE) bit is zero or the Read Cache Disable (RCD) is one
+             * So one of the caches is disabled. Enable both caches.
+             * The rest is unchanged.
+             */
+            ASMBitSet(&aCachingModePage[Offset + 2], 2);
+            ASMBitClear(&aCachingModePage[Offset + 2], 0);
+
+            uint8_t aCDBCaching[6];
+            aCDBCaching[0] = SCSI_MODE_SELECT_6;
+            aCDBCaching[1] = 0; /* Don't write the page into NV RAM. */
+            aCDBCaching[2] = 0;
+            aCDBCaching[3] = 0;
+            aCDBCaching[4] = sizeof(aCachingModePage) & 0xff;
+            aCDBCaching[5] = 0;
+
+            DataSeg.pvSeg = aCachingModePage;
+            DataSeg.cbSeg = sizeof(aCachingModePage);
+
+            sr.enmXfer = SCSIXFER_TO_TARGET;
+            sr.cbCDB = sizeof(aCDBCaching);
+            sr.pvCDB = aCDBCaching;
+            sr.cbI2TData = DataSeg.cbSeg;
+            sr.paI2TSegs = &DataSeg;
+            sr.cI2TSegs  = 1;
+            sr.cbT2IData = 0;
+            sr.paT2ISegs = NULL;
+            sr.cT2ISegs  = 0;
+            sr.cbSense = sizeof(sense);
+            sr.pvSense = sense;
+            sr.status  = 0;
+            rc = iscsiCommandSync(pImage, &sr, false /* fRetry */, VINF_SUCCESS);
+            if (   RT_SUCCESS(rc)
+                && (sr.status == SCSI_STATUS_OK))
+            {
+                LogRel(("iSCSI: Enabled read and write cache of target %s\n", pImage->pszTargetName));
+            }
+            else
+            {
+                /* Log failures but continue. */
+                LogRel(("iSCSI: Could not enable read and write cache of target %s, rc=%Rrc status=%#x\n",
+                        pImage->pszTargetName, rc, sr.status));
+                LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
+                rc = VINF_SUCCESS;
+            }
+        }
+    }
+    else
+    {
+        /* Log errors but continue. */
+        LogRel(("iSCSI: Could not check write cache of target %s, rc=%Rrc, got mode page %#x\n", pImage->pszTargetName, rc,aCachingModePage[0] & 0x3f));
+        LogRel(("iSCSI: Sense:\n%.*Rhxd\n", sr.cbSense, sense));
+        rc = VINF_SUCCESS;
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        iscsiFreeImage(pImage, false);
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int iscsiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                             PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
+
+    /* iSCSI images can't be checked for validity this way, as the filename
+     * just can't supply enough configuration information. */
+    int rc = VERR_VD_ISCSI_INVALID_HEADER;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int iscsiOpen(const char *pszFilename, unsigned uOpenFlags,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PISCSIIMAGE pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename
+        || strchr(pszFilename, '"'))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PISCSIIMAGE)RTMemAllocZ(sizeof(ISCSIIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    pImage->pszFilename = pszFilename;
+    pImage->pszInitiatorName = NULL;
+    pImage->pszTargetName = NULL;
+    pImage->pszTargetAddress = NULL;
+    pImage->pszInitiatorUsername = NULL;
+    pImage->pbInitiatorSecret = NULL;
+    pImage->pszTargetUsername = NULL;
+    pImage->pbTargetSecret = NULL;
+    pImage->paCurrReq = NULL;
+    pImage->pvRecvPDUBuf = NULL;
+    pImage->pszHostname = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = iscsiOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+    {
+        LogFlowFunc(("target %s cVolume %d, cbSector %d\n", pImage->pszTargetName, pImage->cVolume, pImage->cbSector));
+        LogRel(("iSCSI: target address %s, target name %s, SCSI LUN %lld\n", pImage->pszTargetAddress, pImage->pszTargetName, pImage->LUN));
+        *ppBackendData = pImage;
+    }
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int iscsiCreate(const char *pszFilename, uint64_t cbSize,
+                       unsigned uImageFlags, const char *pszComment,
+                       PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                       PCRTUUID pUuid, unsigned uOpenFlags,
+                       unsigned uPercentStart, unsigned uPercentSpan,
+                       PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                       PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc = VERR_NOT_SUPPORTED;
+
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int iscsiClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(!fDelete);   /* This flag is unsupported. */
+
+    rc = iscsiFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int iscsiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                     size_t cbToRead, size_t *pcbActuallyRead)
+{
+    /** @todo reinstate logging of the target everywhere - dropped temporarily */
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    uint64_t lba;
+    uint16_t tls;
+    int rc;
+
+    Assert(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToRead % 512 == 0);
+
+    Assert(pImage->cbSector);
+    AssertPtr(pvBuf);
+
+    if (   uOffset + cbToRead > pImage->cbSize
+        || cbToRead == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /*
+     * Clip read size to a value which is supported by the target.
+     */
+    cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
+
+    lba = uOffset / pImage->cbSector;
+    tls = (uint16_t)(cbToRead / pImage->cbSector);
+    SCSIREQ sr;
+    RTSGSEG T2ISeg;
+    size_t cbCDB;
+    uint8_t abCDB[16];
+    uint8_t sense[96];
+
+    if (pImage->cVolume < _4G)
+    {
+        cbCDB = 10;
+        abCDB[0] = SCSI_READ_10;
+        abCDB[1] = 0;       /* reserved */
+        abCDB[2] = (lba >> 24) & 0xff;
+        abCDB[3] = (lba >> 16) & 0xff;
+        abCDB[4] = (lba >> 8) & 0xff;
+        abCDB[5] = lba & 0xff;
+        abCDB[6] = 0;       /* reserved */
+        abCDB[7] = (tls >> 8) & 0xff;
+        abCDB[8] = tls & 0xff;
+        abCDB[9] = 0;       /* control */
+    }
+    else
+    {
+        cbCDB = 16;
+        abCDB[0] = SCSI_READ_16;
+        abCDB[1] = 0;       /* reserved */
+        abCDB[2] = (lba >> 56) & 0xff;
+        abCDB[3] = (lba >> 48) & 0xff;
+        abCDB[4] = (lba >> 40) & 0xff;
+        abCDB[5] = (lba >> 32) & 0xff;
+        abCDB[6] = (lba >> 24) & 0xff;
+        abCDB[7] = (lba >> 16) & 0xff;
+        abCDB[8] = (lba >> 8) & 0xff;
+        abCDB[9] = lba & 0xff;
+        abCDB[10] = 0;      /* tls unused */
+        abCDB[11] = 0;      /* tls unused */
+        abCDB[12] = (tls >> 8) & 0xff;
+        abCDB[13] = tls & 0xff;
+        abCDB[14] = 0;      /* reserved */
+        abCDB[15] = 0;      /* reserved */
+    }
+
+    T2ISeg.pvSeg = pvBuf;
+    T2ISeg.cbSeg = cbToRead;
+
+    sr.enmXfer   = SCSIXFER_FROM_TARGET;
+    sr.cbCDB     = cbCDB;
+    sr.pvCDB     = abCDB;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = cbToRead;
+    sr.paT2ISegs = &T2ISeg;
+    sr.cT2ISegs  = 1;
+    sr.cbSense   = sizeof(sense);
+    sr.pvSense   = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, true, VERR_READ_ERROR);
+    if (RT_FAILURE(rc))
+    {
+        LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+        *pcbActuallyRead = 0;
+    }
+    else
+        *pcbActuallyRead = sr.cbT2IData;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int iscsiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                     size_t cbToWrite, size_t *pcbWriteProcess,
+                     size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+     LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    uint64_t lba;
+    uint16_t tls;
+    int rc;
+
+    Assert(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    Assert(pImage->cbSector);
+    Assert(pvBuf);
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    *pcbPreRead = 0;
+    *pcbPostRead = 0;
+
+    /*
+     * Clip write size to a value which is supported by the target.
+     */
+    cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
+
+    lba = uOffset / pImage->cbSector;
+    tls = (uint16_t)(cbToWrite / pImage->cbSector);
+    SCSIREQ sr;
+    RTSGSEG I2TSeg;
+    size_t cbCDB;
+    uint8_t abCDB[16];
+    uint8_t sense[96];
+
+    if (pImage->cVolume < _4G)
+    {
+        cbCDB = 10;
+        abCDB[0] = SCSI_WRITE_10;
+        abCDB[1] = 0;       /* reserved */
+        abCDB[2] = (lba >> 24) & 0xff;
+        abCDB[3] = (lba >> 16) & 0xff;
+        abCDB[4] = (lba >> 8) & 0xff;
+        abCDB[5] = lba & 0xff;
+        abCDB[6] = 0;       /* reserved */
+        abCDB[7] = (tls >> 8) & 0xff;
+        abCDB[8] = tls & 0xff;
+        abCDB[9] = 0;       /* control */
+    }
+    else
+    {
+        cbCDB = 16;
+        abCDB[0] = SCSI_WRITE_16;
+        abCDB[1] = 0;       /* reserved */
+        abCDB[2] = (lba >> 56) & 0xff;
+        abCDB[3] = (lba >> 48) & 0xff;
+        abCDB[4] = (lba >> 40) & 0xff;
+        abCDB[5] = (lba >> 32) & 0xff;
+        abCDB[6] = (lba >> 24) & 0xff;
+        abCDB[7] = (lba >> 16) & 0xff;
+        abCDB[8] = (lba >> 8) & 0xff;
+        abCDB[9] = lba & 0xff;
+        abCDB[10] = 0;      /* tls unused */
+        abCDB[11] = 0;      /* tls unused */
+        abCDB[12] = (tls >> 8) & 0xff;
+        abCDB[13] = tls & 0xff;
+        abCDB[14] = 0;      /* reserved */
+        abCDB[15] = 0;      /* reserved */
+    }
+
+    I2TSeg.pvSeg = (void *)pvBuf;
+    I2TSeg.cbSeg = cbToWrite;
+
+    sr.enmXfer              = SCSIXFER_TO_TARGET;
+    sr.cbCDB                = cbCDB;
+    sr.pvCDB                = abCDB;
+    sr.cbI2TData            = cbToWrite;
+    sr.paI2TSegs            = &I2TSeg;
+    sr.cI2TSegs             = 1;
+    sr.cbT2IData            = 0;
+    sr.paT2ISegs            = NULL;
+    sr.cT2ISegs             = 0;
+    sr.cbSense              = sizeof(sense);
+    sr.pvSense              = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, true, VERR_WRITE_ERROR);
+    if (RT_FAILURE(rc))
+    {
+        LogFlow(("iscsiCommandSync(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+        *pcbWriteProcess = 0;
+    }
+    else
+        *pcbWriteProcess = cbToWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int iscsiFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    SCSIREQ sr;
+    uint8_t abCDB[10];
+    uint8_t sense[96];
+
+    abCDB[0] = SCSI_SYNCHRONIZE_CACHE;
+    abCDB[1] = 0;       /* reserved */
+    abCDB[2] = 0;       /* LBA 0 */
+    abCDB[3] = 0;       /* LBA 0 */
+    abCDB[4] = 0;       /* LBA 0 */
+    abCDB[5] = 0;       /* LBA 0 */
+    abCDB[6] = 0;       /* reserved */
+    abCDB[7] = 0;       /* transfer everything to disk */
+    abCDB[8] = 0;       /* transfer everything to disk */
+    abCDB[9] = 0;       /* control */
+
+    sr.enmXfer   = SCSIXFER_NONE;
+    sr.cbCDB     = sizeof(abCDB);
+    sr.pvCDB     = abCDB;
+    sr.cbI2TData = 0;
+    sr.paI2TSegs = NULL;
+    sr.cI2TSegs  = 0;
+    sr.cbT2IData = 0;
+    sr.paT2ISegs = NULL;
+    sr.cT2ISegs  = 0;
+    sr.cbSense   = sizeof(sense);
+    sr.pvSense   = sense;
+
+    rc = iscsiCommandSync(pImage, &sr, false, VINF_SUCCESS);
+    if (RT_FAILURE(rc))
+        AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned iscsiGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+
+    Assert(pImage);
+    NOREF(pImage);
+
+    return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t iscsiGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+
+    Assert(pImage);
+
+    if (pImage)
+        return pImage->cbSize;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t iscsiGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+
+    Assert(pImage);
+    NOREF(pImage);
+
+    if (pImage)
+        return pImage->cbSize;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int iscsiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_VD_GEOMETRY_NOT_SET;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int iscsiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+        rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int iscsiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_VD_GEOMETRY_NOT_SET;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int iscsiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+        rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned iscsiGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    Assert(pImage);
+    NOREF(pImage);
+
+    uImageFlags = VD_IMAGE_FLAGS_FIXED;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned iscsiGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    Assert(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int iscsiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image if we actually need
+     * to do something. A read/write -> readonly transition doesn't need a
+     * reopen. In the other direction we don't have the necessary information
+     * as the "disk is readonly" flag is thrown away. Can be optimized too,
+     * but it's not worth the effort at the moment. */
+    if (   !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        && (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+    {
+        iscsiFreeImage(pImage, false);
+        rc = iscsiOpenImage(pImage, uOpenFlags);
+    }
+    else
+    {
+        pImage->uOpenFlags = uOpenFlags;
+        rc = VINF_SUCCESS;
+    }
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int iscsiGetComment(void *pBackendData, char *pszComment,
+                          size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int iscsiSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int iscsiGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int iscsiSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int iscsiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int iscsiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int iscsiGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int iscsiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int iscsiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    Assert(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int iscsiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    Assert(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void iscsiDump(void *pBackendData)
+{
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+
+    Assert(pImage);
+    if (pImage)
+    {
+        /** @todo put something useful here */
+        iscsiMessage(pImage, "Header: cVolume=%u\n", pImage->cVolume);
+    }
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
+static bool iscsiIsAsyncIOSupported(void *pBackendData)
+{
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    return pImage->fCmdQueuingSupported;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
+static int iscsiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+                          PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
+                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    if (uOffset + cbToRead > pImage->cbSize)
+        return VERR_INVALID_PARAMETER;
+
+    /*
+     * Clip read size to a value which is supported by the target.
+     */
+    cbToRead = RT_MIN(cbToRead, pImage->cbRecvDataLength);
+
+    unsigned cT2ISegs = 0;
+    size_t   cbSegs = 0;
+
+    /* Get the number of segments. */
+    cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
+                                                                   NULL, &cT2ISegs, cbToRead);
+    Assert(cbSegs == cbToRead);
+
+    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cT2ISegs]));
+    if (RT_LIKELY(pReqAsync))
+    {
+        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
+        if (pReq)
+        {
+            uint64_t lba;
+            uint16_t tls;
+            uint8_t *pbCDB = &pReqAsync->abCDB[0];
+            size_t cbCDB;
+
+            lba = uOffset / pImage->cbSector;
+            tls = (uint16_t)(cbToRead / pImage->cbSector);
+
+            cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
+                                                                           &pReqAsync->aSegs[0],
+                                                                           &cT2ISegs, cbToRead);
+            Assert(cbSegs == cbToRead);
+            pReqAsync->cT2ISegs = cT2ISegs;
+            pReqAsync->pIoCtx = pIoCtx;
+            pReqAsync->pScsiReq = pReq;
+            pReqAsync->cSenseRetries = 10;
+            pReqAsync->rcSense       = VERR_READ_ERROR;
+
+            if (pImage->cVolume < _4G)
+            {
+                cbCDB = 10;
+                pbCDB[0] = SCSI_READ_10;
+                pbCDB[1] = 0;       /* reserved */
+                pbCDB[2] = (lba >> 24) & 0xff;
+                pbCDB[3] = (lba >> 16) & 0xff;
+                pbCDB[4] = (lba >> 8) & 0xff;
+                pbCDB[5] = lba & 0xff;
+                pbCDB[6] = 0;       /* reserved */
+                pbCDB[7] = (tls >> 8) & 0xff;
+                pbCDB[8] = tls & 0xff;
+                pbCDB[9] = 0;       /* control */
+            }
+            else
+            {
+                cbCDB = 16;
+                pbCDB[0] = SCSI_READ_16;
+                pbCDB[1] = 0;       /* reserved */
+                pbCDB[2] = (lba >> 56) & 0xff;
+                pbCDB[3] = (lba >> 48) & 0xff;
+                pbCDB[4] = (lba >> 40) & 0xff;
+                pbCDB[5] = (lba >> 32) & 0xff;
+                pbCDB[6] = (lba >> 24) & 0xff;
+                pbCDB[7] = (lba >> 16) & 0xff;
+                pbCDB[8] = (lba >> 8) & 0xff;
+                pbCDB[9] = lba & 0xff;
+                pbCDB[10] = 0;      /* tls unused */
+                pbCDB[11] = 0;      /* tls unused */
+                pbCDB[12] = (tls >> 8) & 0xff;
+                pbCDB[13] = tls & 0xff;
+                pbCDB[14] = 0;      /* reserved */
+                pbCDB[15] = 0;      /* reserved */
+            }
+
+            pReq->enmXfer = SCSIXFER_FROM_TARGET;
+            pReq->cbCDB   = cbCDB;
+            pReq->pvCDB   = pReqAsync->abCDB;
+            pReq->cbI2TData = 0;
+            pReq->paI2TSegs = NULL;
+            pReq->cI2TSegs  = 0;
+            pReq->cbT2IData = cbToRead;
+            pReq->paT2ISegs = &pReqAsync->aSegs[pReqAsync->cI2TSegs];
+            pReq->cT2ISegs  = pReqAsync->cT2ISegs;
+            pReq->cbSense = sizeof(pReqAsync->abSense);
+            pReq->pvSense = pReqAsync->abSense;
+
+            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
+            if (RT_FAILURE(rc))
+                AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+            else
+            {
+                *pcbActuallyRead = cbToRead;
+                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+            }
+
+            RTMemFree(pReq);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+
+        RTMemFree(pReqAsync);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
+static int iscsiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+                           PVDIOCTX pIoCtx,
+                           size_t *pcbWriteProcess, size_t *pcbPreRead,
+                           size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
+                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    if (uOffset + cbToWrite > pImage->cbSize)
+        return VERR_INVALID_PARAMETER;
+
+    /*
+     * Clip read size to a value which is supported by the target.
+     */
+    cbToWrite = RT_MIN(cbToWrite, pImage->cbSendDataLength);
+
+    unsigned cI2TSegs = 0;
+    size_t   cbSegs = 0;
+
+    /* Get the number of segments. */
+    cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
+                                                                   NULL, &cI2TSegs, cbToWrite);
+    Assert(cbSegs == cbToWrite);
+
+    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(RT_OFFSETOF(SCSIREQASYNC, aSegs[cI2TSegs]));
+    if (RT_LIKELY(pReqAsync))
+    {
+        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
+        if (pReq)
+        {
+            uint64_t lba;
+            uint16_t tls;
+            uint8_t *pbCDB = &pReqAsync->abCDB[0];
+            size_t cbCDB;
+
+            lba = uOffset / pImage->cbSector;
+            tls = (uint16_t)(cbToWrite / pImage->cbSector);
+
+            cbSegs = pImage->pInterfaceIoCallbacks->pfnIoCtxSegArrayCreate(pImage->pInterfaceIo->pvUser, pIoCtx,
+                                                                           &pReqAsync->aSegs[0],
+                                                                           &cI2TSegs, cbToWrite);
+            Assert(cbSegs == cbToWrite);
+            pReqAsync->cI2TSegs = cI2TSegs;
+            pReqAsync->pIoCtx = pIoCtx;
+            pReqAsync->pScsiReq = pReq;
+            pReqAsync->cSenseRetries = 10;
+            pReqAsync->rcSense       = VERR_WRITE_ERROR;
+
+            if (pImage->cVolume < _4G)
+            {
+                cbCDB = 10;
+                pbCDB[0] = SCSI_WRITE_10;
+                pbCDB[1] = 0;       /* reserved */
+                pbCDB[2] = (lba >> 24) & 0xff;
+                pbCDB[3] = (lba >> 16) & 0xff;
+                pbCDB[4] = (lba >> 8) & 0xff;
+                pbCDB[5] = lba & 0xff;
+                pbCDB[6] = 0;       /* reserved */
+                pbCDB[7] = (tls >> 8) & 0xff;
+                pbCDB[8] = tls & 0xff;
+                pbCDB[9] = 0;       /* control */
+            }
+            else
+            {
+                cbCDB = 16;
+                pbCDB[0] = SCSI_WRITE_16;
+                pbCDB[1] = 0;       /* reserved */
+                pbCDB[2] = (lba >> 56) & 0xff;
+                pbCDB[3] = (lba >> 48) & 0xff;
+                pbCDB[4] = (lba >> 40) & 0xff;
+                pbCDB[5] = (lba >> 32) & 0xff;
+                pbCDB[6] = (lba >> 24) & 0xff;
+                pbCDB[7] = (lba >> 16) & 0xff;
+                pbCDB[8] = (lba >> 8) & 0xff;
+                pbCDB[9] = lba & 0xff;
+                pbCDB[10] = 0;      /* tls unused */
+                pbCDB[11] = 0;      /* tls unused */
+                pbCDB[12] = (tls >> 8) & 0xff;
+                pbCDB[13] = tls & 0xff;
+                pbCDB[14] = 0;      /* reserved */
+                pbCDB[15] = 0;      /* reserved */
+            }
+
+            pReq->enmXfer = SCSIXFER_TO_TARGET;
+            pReq->cbCDB   = cbCDB;
+            pReq->pvCDB   = pReqAsync->abCDB;
+            pReq->cbI2TData = cbToWrite;
+            pReq->paI2TSegs = &pReqAsync->aSegs[0];
+            pReq->cI2TSegs  = pReqAsync->cI2TSegs;
+            pReq->cbT2IData = 0;
+            pReq->paT2ISegs = NULL;
+            pReq->cT2ISegs  = 0;
+            pReq->cbSense = sizeof(pReqAsync->abSense);
+            pReq->pvSense = pReqAsync->abSense;
+
+            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
+            if (RT_FAILURE(rc))
+                AssertMsgFailed(("iscsiCommand(%s, %#llx) -> %Rrc\n", pImage->pszTargetName, uOffset, rc));
+            else
+            {
+                *pcbWriteProcess = cbToWrite;
+                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+            }
+
+            RTMemFree(pReq);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+
+        RTMemFree(pReqAsync);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
+static int iscsiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    LogFlowFunc(("pBackendData=%p pIoCtx=%#p\n", pBackendData, pIoCtx));
+    PISCSIIMAGE pImage = (PISCSIIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    PSCSIREQASYNC pReqAsync = (PSCSIREQASYNC)RTMemAllocZ(sizeof(SCSIREQASYNC));
+    if (RT_LIKELY(pReqAsync))
+    {
+        PSCSIREQ pReq = (PSCSIREQ)RTMemAllocZ(sizeof(SCSIREQ));
+        if (pReq)
+        {
+            uint8_t *pbCDB = &pReqAsync->abCDB[0];
+
+            pReqAsync->pIoCtx        = pIoCtx;
+            pReqAsync->pScsiReq      = pReq;
+            pReqAsync->cSenseRetries = 0;
+            pReqAsync->rcSense       = VINF_SUCCESS;
+
+            pbCDB[0] = SCSI_SYNCHRONIZE_CACHE;
+            pbCDB[1] = 0;         /* reserved */
+            pbCDB[2] = 0;         /* reserved */
+            pbCDB[3] = 0;         /* reserved */
+            pbCDB[4] = 0;         /* reserved */
+            pbCDB[5] = 0;         /* reserved */
+            pbCDB[6] = 0;         /* reserved */
+            pbCDB[7] = 0;         /* reserved */
+            pbCDB[8] = 0;         /* reserved */
+            pbCDB[9] = 0;         /* control */
+
+            pReq->enmXfer = SCSIXFER_NONE;
+            pReq->cbCDB   = 10;
+            pReq->pvCDB   = pReqAsync->abCDB;
+            pReq->cbI2TData = 0;
+            pReq->paI2TSegs = NULL;
+            pReq->cI2TSegs  = 0;
+            pReq->cbT2IData = 0;
+            pReq->paT2ISegs = NULL;
+            pReq->cT2ISegs  = 0;
+            pReq->cbSense = sizeof(pReqAsync->abSense);
+            pReq->pvSense = pReqAsync->abSense;
+
+            rc = iscsiCommandAsync(pImage, pReq, iscsiCommandAsyncComplete, pReqAsync);
+            if (RT_FAILURE(rc))
+                AssertMsgFailed(("iscsiCommand(%s) -> %Rrc\n", pImage->pszTargetName, rc));
+            else
+                return VERR_VD_IOCTX_HALT; /* Halt the I/O context until further notification from the I/O thread. */
+
+            RTMemFree(pReq);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+
+        RTMemFree(pReqAsync);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnComposeLocation */
+static int iscsiComposeLocation(PVDINTERFACE pConfig, char **pszLocation)
+{
+    char *pszTarget  = NULL;
+    char *pszLUN     = NULL;
+    char *pszAddress = NULL;
+    int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
+    if (RT_SUCCESS(rc))
+    {
+        rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
+        if (RT_SUCCESS(rc))
+        {
+            rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
+            if (RT_SUCCESS(rc))
+            {
+                if (RTStrAPrintf(pszLocation, "iscsi://%s/%s/%s",
+                                 pszAddress, pszTarget, pszLUN) < 0)
+                    rc = VERR_NO_MEMORY;
+            }
+        }
+    }
+    RTMemFree(pszTarget);
+    RTMemFree(pszLUN);
+    RTMemFree(pszAddress);
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnComposeName */
+static int iscsiComposeName(PVDINTERFACE pConfig, char **pszName)
+{
+    char *pszTarget  = NULL;
+    char *pszLUN     = NULL;
+    char *pszAddress = NULL;
+    int rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetName", &pszTarget);
+    if (RT_SUCCESS(rc))
+    {
+        rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "LUN", &pszLUN);
+        if (RT_SUCCESS(rc))
+        {
+            rc = VDCFGQueryStringAlloc(VDGetInterfaceConfig(pConfig), pConfig->pvUser, "TargetAddress", &pszAddress);
+            if (RT_SUCCESS(rc))
+            {
+                /** @todo think about a nicer looking location scheme for iSCSI */
+                if (RTStrAPrintf(pszName, "%s/%s/%s",
+                                 pszAddress, pszTarget, pszLUN) < 0)
+                    rc = VERR_NO_MEMORY;
+            }
+        }
+    }
+    RTMemFree(pszTarget);
+    RTMemFree(pszLUN);
+    RTMemFree(pszAddress);
+
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_ISCSIBackend =
+{
+    /* pszBackendName */
+    "iSCSI",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+    VD_CAP_CONFIG | VD_CAP_TCPNET | VD_CAP_ASYNC,
+    /* papszFileExtensions */
+    NULL,
+    /* paConfigInfo */
+    s_iscsiConfigInfo,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    iscsiCheckIfValid,
+    /* pfnOpen */
+    iscsiOpen,
+    /* pfnCreate */
+    iscsiCreate,
+    /* pfnRename */
+    NULL,
+    /* pfnClose */
+    iscsiClose,
+    /* pfnRead */
+    iscsiRead,
+    /* pfnWrite */
+    iscsiWrite,
+    /* pfnFlush */
+    iscsiFlush,
+    /* pfnGetVersion */
+    iscsiGetVersion,
+    /* pfnGetSize */
+    iscsiGetSize,
+    /* pfnGetFileSize */
+    iscsiGetFileSize,
+    /* pfnGetPCHSGeometry */
+    iscsiGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    iscsiSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    iscsiGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    iscsiSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    iscsiGetImageFlags,
+    /* pfnGetOpenFlags */
+    iscsiGetOpenFlags,
+    /* pfnSetOpenFlags */
+    iscsiSetOpenFlags,
+    /* pfnGetComment */
+    iscsiGetComment,
+    /* pfnSetComment */
+    iscsiSetComment,
+    /* pfnGetUuid */
+    iscsiGetUuid,
+    /* pfnSetUuid */
+    iscsiSetUuid,
+    /* pfnGetModificationUuid */
+    iscsiGetModificationUuid,
+    /* pfnSetModificationUuid */
+    iscsiSetModificationUuid,
+    /* pfnGetParentUuid */
+    iscsiGetParentUuid,
+    /* pfnSetParentUuid */
+    iscsiSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    iscsiGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    iscsiSetParentModificationUuid,
+    /* pfnDump */
+    iscsiDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    iscsiIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    iscsiAsyncRead,
+    /* pfnAsyncWrite */
+    iscsiAsyncWrite,
+    /* pfnAsyncFlush */
+    iscsiAsyncFlush,
+    /* pfnComposeLocation */
+    iscsiComposeLocation,
+    /* pfnComposeName */
+    iscsiComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    NULL
+};
Index: /trunk/src/VBox/Storage/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Storage/Makefile.kmk	(revision 33567)
+++ /trunk/src/VBox/Storage/Makefile.kmk	(revision 33567)
@@ -0,0 +1,54 @@
+# $Id$
+## @file
+# Sub-Makefile for the Storage library.
+#
+
+#
+# Copyright (C) 2006-2007 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+ifndef VBOX_OSE
+ include $(PATH_SUB_CURRENT)/VDFormats/Makefile.kmk
+endif
+
+#
+# StorageLib - The storage Library.
+#
+LIBRARIES += StorageLib StorageLibNoDB
+
+StorageLib_TEMPLATE = VBoxR3Static
+StorageLib_SDKS.win = WINPSDK $(VBOX_WINDDK) VBOX_NTDLL
+StorageLib_DEFS     = IN_VBOXDDU
+StorageLib_SOURCES  = \
+	VD.cpp \
+	VDI.cpp \
+	VMDK.cpp \
+	VHD.cpp \
+	DMG.cpp \
+	Parallels.cpp \
+	ISCSI.cpp \
+	RAW.cpp \
+	VCICache.cpp
+
+StorageLibNoDB_TEMPLATE = VBoxR3Static
+StorageLibNoDB_SDKS.win = WINPSDK $(VBOX_WINDDK)
+StorageLibNoDB_DEFS     = IN_VBOXDDU VBOX_HDD_NO_DYNAMIC_BACKENDS
+StorageLibNoDB_SOURCES  = \
+	$(StorageLib_SOURCES)
+
+# generate rules
+include $(KBUILD_PATH)/subfooter.kmk
+
Index: /trunk/src/VBox/Storage/Parallels.cpp
===================================================================
--- /trunk/src/VBox/Storage/Parallels.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/Parallels.cpp	(revision 33567)
@@ -0,0 +1,1451 @@
+/* $Id$ */
+/** @file
+ *
+ * Parallels hdd disk image, core code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#define LOG_GROUP LOG_GROUP_VD_PARALLELS
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#define PARALLELS_HEADER_MAGIC "WithoutFreeSpace"
+#define PARALLELS_DISK_VERSION 2
+
+/** The header of the parallels disk. */
+#pragma pack(1)
+typedef struct ParallelsHeader
+{
+    /** The magic header to identify a parallels hdd image. */
+    char        HeaderIdentifier[16];
+    /** The version of the disk image. */
+    uint32_t    uVersion;
+    /** The number of heads the hdd has. */
+    uint32_t    cHeads;
+    /** Number of cylinders. */
+    uint32_t    cCylinders;
+    /** Number of sectors per track. */
+    uint32_t    cSectorsPerTrack;
+    /** Number of entries in the allocation bitmap. */
+    uint32_t    cEntriesInAllocationBitmap;
+    /** Total number of sectors. */
+    uint32_t    cSectors;
+    /** Padding. */
+    char        Padding[24];
+} ParallelsHeader;
+#pragma pack()
+
+/**
+ * Parallels image structure.
+ */
+typedef struct PARALLELSIMAGE
+{
+    /** Image file name. */
+    const char         *pszFilename;
+    /** Opaque storage handle. */
+    PVDIOSTORAGE        pStorage;
+
+    /** I/O interface. */
+    PVDINTERFACE        pInterfaceIO;
+    /** I/O interface callbacks. */
+    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE        pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE        pVDIfsImage;
+    /** Error interface. */
+    PVDINTERFACE        pInterfaceError;
+    /** Error interface callbacks. */
+    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
+
+    /** Open flags passed by VBoxHDD layer. */
+    unsigned            uOpenFlags;
+    /** Image flags defined during creation or determined during open. */
+    unsigned            uImageFlags;
+    /** Total size of the image. */
+    uint64_t            cbSize;
+
+    /** Physical geometry of this image. */
+    VDGEOMETRY          PCHSGeometry;
+    /** Logical geometry of this image. */
+    VDGEOMETRY          LCHSGeometry;
+
+    /** Pointer to the allocation bitmap. */
+    uint32_t           *pAllocationBitmap;
+    /** Entries in the allocation bitmap. */
+    uint64_t            cAllocationBitmapEntries;
+    /** Flag whether the allocation bitmap was changed. */
+    bool                fAllocationBitmapChanged;
+    /** Current file size. */
+    uint64_t            cbFileCurrent;
+} PARALLELSIMAGE, *PPARALLELSIMAGE;
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aParallelsFileExtensions[] =
+{
+    {"hdd", VDTYPE_HDD},
+    {NULL, VDTYPE_INVALID}
+};
+
+/***************************************************
+ * Internal functions                              *
+ **************************************************/
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) parallelsError(PPARALLELSIMAGE pImage, int rc, RT_SRC_POS_DECL,
+                               const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) parallelsMessage(PPARALLELSIMAGE pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) parallelsFileOpen(PPARALLELSIMAGE pImage, const char *pszFilename,
+                                  uint32_t fOpen)
+{
+    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                  pszFilename, fOpen,
+                                                  &pImage->pStorage);
+}
+
+DECLINLINE(int) parallelsFileClose(PPARALLELSIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                   pImage->pStorage);
+}
+
+DECLINLINE(int) parallelsFileDelete(PPARALLELSIMAGE pImage, const char *pszFilename)
+{
+    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                    pszFilename);
+}
+
+DECLINLINE(int) parallelsFileMove(PPARALLELSIMAGE pImage, const char *pszSrc,
+                                  const char *pszDst, unsigned fMove)
+{
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) parallelsFileGetSize(PPARALLELSIMAGE pImage, uint64_t *pcbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, pcbSize);
+}
+
+DECLINLINE(int) parallelsFileSetSize(PPARALLELSIMAGE pImage, uint64_t cbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, cbSize);
+}
+
+DECLINLINE(int) parallelsFileWriteSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
+                                       const void *pvBuffer, size_t cbBuffer,
+                                       size_t *pcbWritten)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage, uOffset,
+                                                       pvBuffer, cbBuffer, pcbWritten);
+}
+
+DECLINLINE(int) parallelsFileReadSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
+                                      void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pImage->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, pcbRead);
+}
+
+DECLINLINE(int) parallelsFileFlushSync(PPARALLELSIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage);
+}
+
+DECLINLINE(int) parallelsFileReadUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
+                                           PVDIOCTX pIoCtx, size_t cbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
+                                                           pImage->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+DECLINLINE(int) parallelsFileWriteUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
+                                            PVDIOCTX pIoCtx, size_t cbWrite,
+                                            PFNVDXFERCOMPLETED pfnComplete,
+                                            void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) parallelsFileWriteMetaAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
+                                            void *pvBuffer, size_t cbBuffer,
+                                            PVDIOCTX pIoCtx,
+                                            PFNVDXFERCOMPLETED pfnComplete,
+                                            void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pvBuffer,
+                                                            cbBuffer, pIoCtx,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) parallelsFileFlushAsync(PPARALLELSIMAGE pImage, PVDIOCTX pIoCtx,
+                                        PFNVDXFERCOMPLETED pfnComplete,
+                                        void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                        pImage->pStorage,
+                                                        pIoCtx, pfnComplete,
+                                                        pvCompleteUser);
+}
+
+
+/**
+ * Internal. Flush image data to disk.
+ */
+static int parallelsFlushImage(PPARALLELSIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        return VINF_SUCCESS;
+
+    if (   !(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        && (pImage->fAllocationBitmapChanged))
+    {
+        pImage->fAllocationBitmapChanged = false;
+        /* Write the allocation bitmap to the file. */
+        rc = parallelsFileWriteSync(pImage, sizeof(ParallelsHeader),
+                                    pImage->pAllocationBitmap,
+                                    pImage->cAllocationBitmapEntries * sizeof(uint32_t),
+                                    NULL);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /* Flush file. */
+    rc = parallelsFileFlushSync(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Internal. Free all allocated space for representing an image except pImage,
+ * and optionally delete the image from disk.
+ */
+static int parallelsFreeImage(PPARALLELSIMAGE pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (pImage->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+                parallelsFlushImage(pImage);
+
+            parallelsFileClose(pImage);
+            pImage->pStorage = NULL;
+        }
+
+        if (pImage->pAllocationBitmap)
+        {
+            RTMemFree(pImage->pAllocationBitmap);
+            pImage->pAllocationBitmap = NULL;
+        }
+
+        if (fDelete && pImage->pszFilename)
+            parallelsFileDelete(pImage, pImage->pszFilename);
+    }
+
+    return rc;
+}
+
+static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
+{
+    int rc = VINF_SUCCESS;
+    ParallelsHeader parallelsHeader;
+
+    /* Try to get error interface. */
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    rc = parallelsFileOpen(pImage, pImage->pszFilename,
+                           VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                      false /* fCreate */));
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = parallelsFileGetSize(pImage, &pImage->cbFileCurrent);
+    if (RT_FAILURE(rc))
+        goto out;
+    AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
+
+    rc = parallelsFileReadSync(pImage, 0, &parallelsHeader, sizeof(parallelsHeader), NULL);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    if (memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16))
+    {
+        /* Check if the file has hdd as extension. It is a fixed size raw image then. */
+        char *pszExtension = RTPathExt(pImage->pszFilename);
+        if (strcmp(pszExtension, ".hdd"))
+        {
+            rc = VERR_VD_PARALLELS_INVALID_HEADER;
+            goto out;
+        }
+
+        /* This is a fixed size image. */
+        pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+        pImage->cbSize = pImage->cbFileCurrent;
+
+        pImage->PCHSGeometry.cHeads     = 16;
+        pImage->PCHSGeometry.cSectors   = 63;
+        uint64_t cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
+        pImage->PCHSGeometry.cCylinders = (uint32_t)cCylinders;
+    }
+    else
+    {
+        if (parallelsHeader.uVersion != PARALLELS_DISK_VERSION)
+        {
+            rc = VERR_NOT_SUPPORTED;
+            goto out;
+        }
+
+        if (parallelsHeader.cEntriesInAllocationBitmap > (1 << 30))
+        {
+            rc = VERR_NOT_SUPPORTED;
+            goto out;
+        }
+
+        Log(("cSectors=%u\n", parallelsHeader.cSectors));
+        pImage->cbSize = ((uint64_t)parallelsHeader.cSectors) * 512;
+        pImage->uImageFlags = VD_IMAGE_FLAGS_NONE;
+        pImage->cAllocationBitmapEntries = parallelsHeader.cEntriesInAllocationBitmap;
+        pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ((uint32_t)pImage->cAllocationBitmapEntries * sizeof(uint32_t));
+        if (!pImage->pAllocationBitmap)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        rc = parallelsFileReadSync(pImage, sizeof(ParallelsHeader),
+                                   pImage->pAllocationBitmap,
+                                   pImage->cAllocationBitmapEntries * sizeof(uint32_t),
+                                   NULL);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        pImage->PCHSGeometry.cCylinders = parallelsHeader.cCylinders;
+        pImage->PCHSGeometry.cHeads     = parallelsHeader.cHeads;
+        pImage->PCHSGeometry.cSectors   = parallelsHeader.cSectorsPerTrack;
+    }
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int parallelsCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                                 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    int rc;
+    PVDIOSTORAGE pStorage;
+    ParallelsHeader parallelsHeader;
+
+    /* Get I/O interface. */
+    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
+    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
+                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
+                                                                   false /* fCreate */),
+                                        &pStorage);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
+                                            0, &parallelsHeader,
+                                            sizeof(ParallelsHeader), NULL);
+    if (RT_SUCCESS(rc))
+    {
+        if (   !memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16)
+            && (parallelsHeader.uVersion == PARALLELS_DISK_VERSION))
+            rc = VINF_SUCCESS;
+        else
+        {
+            /*
+             * The image may be an fixed size image.
+             * Unfortunately fixed sized parallels images
+             * are just raw files hence no magic header to
+             * check for.
+             * The code succeeds if the file is a multiple
+             * of 512 and if the file extensions is *.hdd
+             */
+            uint64_t cbFile;
+            char *pszExtension;
+
+            rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
+                                                   &cbFile);
+            if (RT_FAILURE(rc) || ((cbFile % 512) != 0))
+            {
+                pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+                return VERR_VD_PARALLELS_INVALID_HEADER;
+            }
+
+            pszExtension = RTPathExt(pszFilename);
+            if (!pszExtension || strcmp(pszExtension, ".hdd"))
+                rc = VERR_VD_PARALLELS_INVALID_HEADER;
+            else
+                rc = VINF_SUCCESS;
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+        *penmType = VDTYPE_HDD;
+
+    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int parallelsOpen(const char *pszFilename, unsigned uOpenFlags,
+                         PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                         VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PPARALLELSIMAGE pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /** @todo r=klaus why this duplicate check, async is not claimed... */
+    if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
+    {
+        rc = VERR_NOT_SUPPORTED;
+        goto out;
+    }
+
+    pImage = (PPARALLELSIMAGE)RTMemAllocZ(sizeof(PARALLELSIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+    pImage->fAllocationBitmapChanged = false;
+
+    rc = parallelsOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pImage;
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int parallelsCreate(const char *pszFilename, uint64_t cbSize,
+                           unsigned uImageFlags, const char *pszComment,
+                           PCVDGEOMETRY pPCHSGeometry,
+                           PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
+                           unsigned uOpenFlags, unsigned uPercentStart,
+                           unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
+                           PVDINTERFACE pVDIfsImage,
+                           PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc = VERR_NOT_IMPLEMENTED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int parallelsRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+    int rc = VINF_SUCCESS;
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+
+    /* Check arguments. */
+    if (   !pImage
+        || !pszFilename
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Close the image. */
+    rc = parallelsFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Rename the file. */
+    rc = parallelsFileMove(pImage, pImage->pszFilename, pszFilename, 0);
+    if (RT_FAILURE(rc))
+    {
+        /* The move failed, try to reopen the original image. */
+        int rc2 = parallelsOpenImage(pImage, pImage->uOpenFlags);
+        if (RT_FAILURE(rc2))
+            rc = rc2;
+
+        goto out;
+    }
+
+    /* Update pImage with the new information. */
+    pImage->pszFilename = pszFilename;
+
+    /* Open the old image with new name. */
+    rc = parallelsOpenImage(pImage, pImage->uOpenFlags);
+    if (RT_FAILURE(rc))
+        goto out;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int parallelsClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    rc = parallelsFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int parallelsRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                         size_t cbBuf, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+    uint64_t uSector;
+    uint64_t uOffsetInFile;
+    uint32_t iIndexInAllocationTable;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbBuf % 512 == 0);
+
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        rc = parallelsFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
+    }
+    else
+    {
+        /* Calculate offset in the real file. */
+        uSector = uOffset / 512;
+        /* One chunk in the file is always one track big. */
+        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
+        uSector = uSector % pImage->PCHSGeometry.cSectors;
+
+        cbBuf = RT_MIN(cbBuf, (pImage->PCHSGeometry.cSectors - uSector)*512);
+
+        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
+        {
+            rc = VERR_VD_BLOCK_FREE;
+        }
+        else
+        {
+            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+            rc = parallelsFileReadSync(pImage, uOffsetInFile, pvBuf, cbBuf, NULL);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbActuallyRead)
+            *pcbActuallyRead = cbBuf;
+
+        Log2(("parallelsRead: off=%#llx pvBuf=%p cbBuf=%d\n"
+                "%.*Rhxd\n",
+                uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int parallelsWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                          size_t cbBuf, size_t *pcbWriteProcess,
+                          size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+    uint64_t uSector;
+    uint64_t uOffsetInFile;
+    uint32_t iIndexInAllocationTable;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbBuf % 512 == 0);
+
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        rc = parallelsFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
+    }
+    else
+    {
+        /** Calculate offset in the real file. */
+        uSector = uOffset / 512;
+        /** One chunk in the file is always one track big. */
+        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
+        uSector = uSector % pImage->PCHSGeometry.cSectors;
+
+        cbBuf = RT_MIN(cbBuf, (pImage->PCHSGeometry.cSectors - uSector)*512);
+
+        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
+        {
+            /* Allocate new chunk in the file. */
+            AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
+            pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
+            pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
+            pImage->fAllocationBitmapChanged = true;
+
+            uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(pImage->PCHSGeometry.cSectors * 512);
+
+            if (!pNewBlock)
+            {
+                rc = VERR_NO_MEMORY;
+                goto out;
+            }
+
+            uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
+            memcpy(pNewBlock + (uOffset - ((uint64_t)iIndexInAllocationTable * pImage->PCHSGeometry.cSectors * 512)),
+                   pvBuf, cbBuf);
+
+            /*
+             * Write the new block at the current end of the file.
+             */
+            rc = parallelsFileWriteSync(pImage, uOffsetInFile, pNewBlock,
+                                        pImage->PCHSGeometry.cSectors * 512,
+                                        NULL);
+
+            RTMemFree(pNewBlock);
+        }
+        else
+        {
+            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+            rc = parallelsFileWriteSync(pImage, uOffsetInFile, pvBuf, cbBuf, NULL);
+        }
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbBuf;
+
+    /* Stay on the safe side. Do not run the risk of confusing the higher
+     * level, as that can be pretty lethal to image consistency. */
+    *pcbPreRead = 0;
+    *pcbPostRead = 0;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int parallelsFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    rc = parallelsFlushImage(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned parallelsGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        return PARALLELS_DISK_VERSION;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t parallelsGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+        cb = pImage->cbSize;
+
+    LogFlowFunc(("returns %llu\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t parallelsGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+        cb = pImage->cbFileCurrent;
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int parallelsGetPCHSGeometry(void *pBackendData,
+                                    PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pImage->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int parallelsSetPCHSGeometry(void *pBackendData,
+                                    PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int parallelsGetLCHSGeometry(void *pBackendData,
+                                    PVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->LCHSGeometry.cCylinders)
+        {
+            *pLCHSGeometry = pImage->LCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int parallelsSetLCHSGeometry(void *pBackendData,
+                                    PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->LCHSGeometry = *pLCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned parallelsGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uImageFlags = pImage->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned parallelsGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int parallelsSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    /** @todo r=klaus add VD_OPEN_FLAGS_ASYNC_IO when async io has been tested */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    parallelsFreeImage(pImage, true);
+    rc = parallelsOpenImage(pImage, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int parallelsGetComment(void *pBackendData, char *pszComment,
+                               size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int parallelsSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int parallelsGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int parallelsSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int parallelsGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int parallelsSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int parallelsGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int parallelsSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int parallelsGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int parallelsSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void parallelsDump(void *pBackendData)
+{
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        parallelsMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u\n",
+                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
+                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors);
+    }
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
+static bool parallelsIsAsyncIOSupported(void *pBackendData)
+{
+#if 0 /** @todo: Remove when tested */
+    return true;
+#else
+    return false;
+#endif
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
+static int parallelsAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+                              PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
+    int rc = VINF_SUCCESS;
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    uint64_t uSector;
+    uint64_t uOffsetInFile;
+    uint32_t iIndexInAllocationTable;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToRead % 512 == 0);
+
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        rc = parallelsFileReadUserAsync(pImage, uOffset, pIoCtx, cbToRead);
+    }
+    else
+    {
+        /* Calculate offset in the real file. */
+        uSector = uOffset / 512;
+        /* One chunk in the file is always one track big. */
+        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
+        uSector = uSector % pImage->PCHSGeometry.cSectors;
+
+        cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
+
+        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
+        {
+            rc = VERR_VD_BLOCK_FREE;
+        }
+        else
+        {
+            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+            rc = parallelsFileReadUserAsync(pImage, uOffsetInFile, pIoCtx, cbToRead);
+        }
+    }
+
+    *pcbActuallyRead = cbToRead;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
+static int parallelsAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+                               PVDIOCTX pIoCtx,
+                               size_t *pcbWriteProcess, size_t *pcbPreRead,
+                               size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
+    int rc = VINF_SUCCESS;
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+    uint64_t uSector;
+    uint64_t uOffsetInFile;
+    uint32_t iIndexInAllocationTable;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        rc = parallelsFileWriteUserAsync(pImage, uOffset, pIoCtx, cbToWrite, NULL, NULL);
+    }
+    else
+    {
+        /* Calculate offset in the real file. */
+        uSector = uOffset / 512;
+        /* One chunk in the file is always one track big. */
+        iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
+        uSector = uSector % pImage->PCHSGeometry.cSectors;
+
+        cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
+
+        if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
+        {
+            if (fWrite & VD_WRITE_NO_ALLOC)
+            {
+                *pcbPreRead  = uSector * 512;
+                *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
+
+                if (pcbWriteProcess)
+                    *pcbWriteProcess = cbToWrite;
+                return VERR_VD_BLOCK_FREE;
+            }
+
+            /* Allocate new chunk in the file. */
+            Assert(uSector == 0);
+            AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
+            pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
+            pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
+            pImage->fAllocationBitmapChanged = true;
+            uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
+
+            /*
+             * Write the new block at the current end of the file.
+             */
+            rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
+            if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
+            {
+                /* Write the changed allocation bitmap entry. */
+                /** @todo: Error handling. */
+                rc = parallelsFileWriteMetaAsync(pImage,
+                                                 sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
+                                                 &pImage->pAllocationBitmap[iIndexInAllocationTable],
+                                                 sizeof(uint32_t), pIoCtx,
+                                                 NULL, NULL);
+            }
+        }
+        else
+        {
+            uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
+            rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
+        }
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbToWrite;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
+static int parallelsAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
+
+    LogFlowFunc(("pImage=#%p\n", pImage));
+
+    /* Flush the file, everything is up to date already. */
+    rc = parallelsFileFlushAsync(pImage, pIoCtx, NULL, NULL);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_ParallelsBackend =
+{
+    /* pszBackendName */
+    "Parallels",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+    VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aParallelsFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    parallelsCheckIfValid,
+    /* pfnOpen */
+    parallelsOpen,
+    /* pfnCreate */
+    parallelsCreate,
+    /* pfnRename */
+    parallelsRename,
+    /* pfnClose */
+    parallelsClose,
+    /* pfnRead */
+    parallelsRead,
+    /* pfnWrite */
+    parallelsWrite,
+    /* pfnFlush */
+    parallelsFlush,
+    /* pfnGetVersion */
+    parallelsGetVersion,
+    /* pfnGetSize */
+    parallelsGetSize,
+    /* pfnGetFileSize */
+    parallelsGetFileSize,
+    /* pfnGetPCHSGeometry */
+    parallelsGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    parallelsSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    parallelsGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    parallelsSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    parallelsGetImageFlags,
+    /* pfnGetOpenFlags */
+    parallelsGetOpenFlags,
+    /* pfnSetOpenFlags */
+    parallelsSetOpenFlags,
+    /* pfnGetComment */
+    parallelsGetComment,
+    /* pfnSetComment */
+    parallelsSetComment,
+    /* pfnGetUuid */
+    parallelsGetUuid,
+    /* pfnSetUuid */
+    parallelsSetUuid,
+    /* pfnGetModificationUuid */
+    parallelsGetModificationUuid,
+    /* pfnSetModificationUuid */
+    parallelsSetModificationUuid,
+    /* pfnGetParentUuid */
+    parallelsGetParentUuid,
+    /* pfnSetParentUuid */
+    parallelsSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    parallelsGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    parallelsSetParentModificationUuid,
+    /* pfnDump */
+    parallelsDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    parallelsIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    parallelsAsyncRead,
+    /* pfnAsyncWrite */
+    parallelsAsyncWrite,
+    /* pfnAsyncFlush */
+    parallelsAsyncFlush,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    NULL
+};
Index: /trunk/src/VBox/Storage/RAW.cpp
===================================================================
--- /trunk/src/VBox/Storage/RAW.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/RAW.cpp	(revision 33567)
@@ -0,0 +1,1484 @@
+/* $Id$ */
+/** @file
+ * RawHDDCore - Raw Disk image, Core Code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_RAW
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/path.h>
+
+/*******************************************************************************
+*   Constants And Macros, Structures and Typedefs                              *
+*******************************************************************************/
+/**
+ * ISO volume descriptor.
+ */
+#pragma pack(1)
+typedef struct ISOVOLDESC
+{
+    union
+    {
+        /** Byte view. */
+        uint8_t au8[2048];
+        /** Field view. */
+        struct
+        {
+            /** Descriptor type. */
+            uint8_t u8Type;
+            /** Standard identifier */
+            uint8_t au8Id[5];
+            /** Descriptor version. */
+            uint8_t u8Version;
+            /** Rest depends on the descriptor type. */
+        } fields;
+    };
+} ISOVOLDESC, *PISOVOLDESC;
+#pragma pack()
+AssertCompileSize(ISOVOLDESC, 2048);
+
+/**
+ * Raw image data structure.
+ */
+typedef struct RAWIMAGE
+{
+    /** Image name. */
+    const char          *pszFilename;
+    /** Storage handle. */
+    PVDIOSTORAGE        pStorage;
+    /** I/O interface. */
+    PVDINTERFACE        pInterfaceIO;
+    /** Async I/O interface callbacks. */
+    PVDINTERFACEIOINT   pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE        pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE        pVDIfsImage;
+
+    /** Error callback. */
+    PVDINTERFACE        pInterfaceError;
+    /** Opaque data for error callback. */
+    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
+
+    /** Open flags passed by VBoxHD layer. */
+    unsigned            uOpenFlags;
+    /** Image flags defined during creation or determined during open. */
+    unsigned            uImageFlags;
+    /** Total size of the image. */
+    uint64_t            cbSize;
+    /** Position in the image (only truly used for sequential access). */
+    uint64_t            offAccess;
+    /** Flag if this is a newly created image. */
+    bool                fCreate;
+    /** Physical geometry of this image. */
+    VDGEOMETRY          PCHSGeometry;
+    /** Logical geometry of this image. */
+    VDGEOMETRY          LCHSGeometry;
+
+} RAWIMAGE, *PRAWIMAGE;
+
+
+/** Size of write operations when filling an image with zeroes. */
+#define RAW_FILL_SIZE (128 * _1K)
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aRawFileExtensions[] =
+{
+    {"iso", VDTYPE_DVD},
+    {"img", VDTYPE_FLOPPY},
+    {"ima", VDTYPE_FLOPPY},
+    {NULL, VDTYPE_INVALID}
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) rawError(PRAWIMAGE pImage, int rc, RT_SRC_POS_DECL,
+                         const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) rawMessage(PRAWIMAGE pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) rawFileOpen(PRAWIMAGE pImage, const char *pszFilename,
+                            uint32_t fOpen)
+{
+    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                  pszFilename, fOpen,
+                                                  &pImage->pStorage);
+}
+
+DECLINLINE(int) rawFileClose(PRAWIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                   pImage->pStorage);
+}
+
+DECLINLINE(int) rawFileDelete(PRAWIMAGE pImage, const char *pszFilename)
+{
+    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                    pszFilename);
+}
+
+DECLINLINE(int) rawFileMove(PRAWIMAGE pImage, const char *pszSrc,
+                            const char *pszDst, unsigned fMove)
+{
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) rawFileGetFreeSpace(PRAWIMAGE pImage, const char *pszFilename,
+                                    int64_t *pcbFree)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
+                                                          pszFilename, pcbFree);
+}
+
+DECLINLINE(int) rawFileGetSize(PRAWIMAGE pImage, uint64_t *pcbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, pcbSize);
+}
+
+DECLINLINE(int) rawFileSetSize(PRAWIMAGE pImage, uint64_t cbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, cbSize);
+}
+
+DECLINLINE(int) rawFileWriteSync(PRAWIMAGE pImage, uint64_t uOffset,
+                                 const void *pvBuffer, size_t cbBuffer,
+                                 size_t *pcbWritten)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage, uOffset,
+                                                       pvBuffer, cbBuffer, pcbWritten);
+}
+
+DECLINLINE(int) rawFileReadSync(PRAWIMAGE pImage, uint64_t uOffset,
+                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pImage->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, pcbRead);
+}
+
+DECLINLINE(int) rawFileFlushSync(PRAWIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage);
+}
+
+DECLINLINE(int) rawFileReadUserAsync(PRAWIMAGE pImage, uint64_t uOffset,
+                                     PVDIOCTX pIoCtx, size_t cbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
+                                                           pImage->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+DECLINLINE(int) rawFileWriteUserAsync(PRAWIMAGE pImage, uint64_t uOffset,
+                                      PVDIOCTX pIoCtx, size_t cbWrite,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) rawFileFlushAsync(PRAWIMAGE pImage, PVDIOCTX pIoCtx,
+                                  PFNVDXFERCOMPLETED pfnComplete,
+                                  void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                        pImage->pStorage,
+                                                        pIoCtx, pfnComplete,
+                                                        pvCompleteUser);
+}
+
+
+/**
+ * Internal. Flush image data to disk.
+ */
+static int rawFlushImage(PRAWIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+
+    if (   pImage->pStorage
+        && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        rc = rawFileFlushSync(pImage);
+
+    return rc;
+}
+
+/**
+ * Internal. Free all allocated space for representing an image except pImage,
+ * and optionally delete the image from disk.
+ */
+static int rawFreeImage(PRAWIMAGE pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (pImage->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+            {
+                /* For newly created images in sequential mode fill it to
+                 * the nominal size. */
+                if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
+                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                    && pImage->fCreate)
+                {
+                    /* Fill rest of image with zeroes, a must for sequential
+                     * images to reach the nominal size. */
+                    uint64_t uOff;
+                    void *pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE);
+                    if (!pvBuf)
+                        goto out;
+
+                    uOff = pImage->offAccess;
+                    /* Write data to all image blocks. */
+                    while (uOff < pImage->cbSize)
+                    {
+                        unsigned cbChunk = (unsigned)RT_MIN(pImage->cbSize,
+                                                            RAW_FILL_SIZE);
+
+                        rc = rawFileWriteSync(pImage, uOff, pvBuf, cbChunk,
+                                              NULL);
+                        if (RT_FAILURE(rc))
+                            goto out;
+
+                        uOff += cbChunk;
+                    }
+out:
+                    if (pvBuf)
+                        RTMemTmpFree(pvBuf);
+                }
+                rawFlushImage(pImage);
+            }
+
+            rawFileClose(pImage);
+            pImage->pStorage = NULL;
+        }
+
+        if (fDelete && pImage->pszFilename)
+            rawFileDelete(pImage, pImage->pszFilename);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Internal: Open an image, constructing all necessary data structures.
+ */
+static int rawOpenImage(PRAWIMAGE pImage, unsigned uOpenFlags)
+{
+    int rc;
+
+    pImage->uOpenFlags = uOpenFlags;
+    pImage->fCreate = false;
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the image.
+     */
+    rc = rawFileOpen(pImage, pImage->pszFilename,
+                     VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                false /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        goto out;
+    }
+
+    rc = rawFileGetSize(pImage, &pImage->cbSize);
+    if (RT_FAILURE(rc))
+        goto out;
+    if (pImage->cbSize % 512)
+    {
+        rc = VERR_VD_RAW_INVALID_HEADER;
+        goto out;
+    }
+    pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+
+out:
+    if (RT_FAILURE(rc))
+        rawFreeImage(pImage, false);
+    return rc;
+}
+
+/**
+ * Internal: Create a raw image.
+ */
+static int rawCreateImage(PRAWIMAGE pImage, uint64_t cbSize,
+                          unsigned uImageFlags, const char *pszComment,
+                          PCVDGEOMETRY pPCHSGeometry,
+                          PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags,
+                          PFNVDPROGRESS pfnProgress, void *pvUser,
+                          unsigned uPercentStart, unsigned uPercentSpan)
+{
+    int rc;
+    RTFOFF cbFree = 0;
+    uint64_t uOff;
+    void *pvBuf = NULL;
+    int32_t fOpen;
+
+    if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
+    {
+        rc = rawError(pImage, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("Raw: cannot create diff image '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+
+    pImage->uImageFlags = uImageFlags;
+
+    pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
+
+    pImage->uImageFlags = uImageFlags;
+    pImage->fCreate = true;
+    pImage->PCHSGeometry = *pPCHSGeometry;
+    pImage->LCHSGeometry = *pLCHSGeometry;
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /* Create image file. */
+    fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */);
+    if (uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)
+        fOpen &= ~RTFILE_O_READ;
+    rc = rawFileOpen(pImage, pImage->pszFilename, fOpen);
+    if (RT_FAILURE(rc))
+    {
+        rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: cannot create image '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    if (!(uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
+    {
+        /* Check the free space on the disk and leave early if there is not
+         * sufficient space available. */
+        rc = rawFileGetFreeSpace(pImage, pImage->pszFilename, &cbFree);
+        if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbSize))
+        {
+            rc = rawError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("Raw: disk would overflow creating image '%s'"), pImage->pszFilename);
+            goto out;
+        }
+
+        /* Allocate & commit whole file if fixed image, it must be more
+         * effective than expanding file by write operations. */
+        rc = rawFileSetSize(pImage, cbSize);
+        if (RT_FAILURE(rc))
+        {
+            rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: setting image size failed for '%s'"), pImage->pszFilename);
+            goto out;
+        }
+
+        /* Fill image with zeroes. We do this for every fixed-size image since
+         * on some systems (for example Windows Vista), it takes ages to write
+         * a block near the end of a sparse file and the guest could complain
+         * about an ATA timeout. */
+        pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE);
+        if (!pvBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        uOff = 0;
+        /* Write data to all image blocks. */
+        while (uOff < cbSize)
+        {
+            unsigned cbChunk = (unsigned)RT_MIN(cbSize, RAW_FILL_SIZE);
+
+            rc = rawFileWriteSync(pImage, uOff, pvBuf, cbChunk, NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: writing block failed for '%s'"), pImage->pszFilename);
+                goto out;
+            }
+
+            uOff += cbChunk;
+
+            if (pfnProgress)
+            {
+                rc = pfnProgress(pvUser,
+                                 uPercentStart + uOff * uPercentSpan * 98 / (cbSize * 100));
+                if (RT_FAILURE(rc))
+                    goto out;
+            }
+        }
+    }
+
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
+
+    pImage->cbSize = cbSize;
+
+    rc = rawFlushImage(pImage);
+
+out:
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan);
+
+    if (RT_FAILURE(rc))
+        rawFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int rawCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
+    PVDIOSTORAGE pStorage = NULL;
+    uint64_t cbFile;
+    int rc = VINF_SUCCESS;
+    ISOVOLDESC IsoVolDesc;
+    char *pszExtension = NULL;
+
+    /* Get I/O interface. */
+    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
+    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pszExtension = RTPathExt(pszFilename);
+
+    /*
+     * Open the file and read the footer.
+     */
+    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
+                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
+                                                                   false /* fCreate */),
+                                        &pStorage);
+    if (RT_SUCCESS(rc))
+        rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
+                                               &cbFile);
+
+    /* Try to guess the image type based on the extension. */
+    if (   RT_SUCCESS(rc)
+        && pszExtension)
+    {
+        if (!strcmp(pszExtension, ".iso")) /* DVD images. */
+        {
+            if (cbFile >= (32768 + sizeof(ISOVOLDESC)))
+            {
+                rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
+                                                        32768, &IsoVolDesc, sizeof(IsoVolDesc), NULL);
+            }
+            else
+                rc = VERR_VD_RAW_INVALID_HEADER;
+
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Do we recognize this stuff?
+                 */
+                if (   !memcmp(IsoVolDesc.fields.au8Id, "BEA01", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "BOOT2", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "CD001", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "CDW02", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "NSR02", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "NSR03", 5)
+                    || !memcmp(IsoVolDesc.fields.au8Id, "TEA01", 5))
+                {
+                    *penmType = VDTYPE_DVD;
+                    rc = VINF_SUCCESS;
+                }
+                else
+                    rc = VERR_VD_RAW_INVALID_HEADER;
+            }
+        }
+        else if (!strcmp(pszExtension, ".img") || !strcmp(pszExtension, ".ima")) /* Floppy images */
+        {
+            if (!(cbFile % 512))
+            {
+                *penmType = VDTYPE_FLOPPY;
+                rc = VINF_SUCCESS;
+            }
+            else
+                rc = VERR_VD_RAW_INVALID_HEADER;
+        }
+    }
+    else
+        rc = VERR_VD_RAW_INVALID_HEADER;
+
+    if (pStorage)
+        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int rawOpen(const char *pszFilename, unsigned uOpenFlags,
+                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                   VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PRAWIMAGE pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+
+    pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = rawOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pImage;
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int rawCreate(const char *pszFilename, uint64_t cbSize,
+                     unsigned uImageFlags, const char *pszComment,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     PCRTUUID pUuid, unsigned uOpenFlags,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc;
+    PRAWIMAGE pImage;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename
+        || !VALID_PTR(pPCHSGeometry)
+        || !VALID_PTR(pLCHSGeometry))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = rawCreateImage(pImage, cbSize, uImageFlags, pszComment,
+                        pPCHSGeometry, pLCHSGeometry, uOpenFlags,
+                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
+    if (RT_SUCCESS(rc))
+    {
+        /* So far the image is opened in read/write mode. Make sure the
+         * image is opened in read-only mode if the caller requested that. */
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rawFreeImage(pImage, false);
+            rc = rawOpenImage(pImage, uOpenFlags);
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pImage);
+                goto out;
+            }
+        }
+        *ppBackendData = pImage;
+    }
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int rawRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+    int rc = VINF_SUCCESS;
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    /* Check arguments. */
+    if (   !pImage
+        || !pszFilename
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Close the image. */
+    rc = rawFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Rename the file. */
+    rc = rawFileMove(pImage, pImage->pszFilename, pszFilename, 0);
+    if (RT_FAILURE(rc))
+    {
+        /* The move failed, try to reopen the original image. */
+        int rc2 = rawOpenImage(pImage, pImage->uOpenFlags);
+        if (RT_FAILURE(rc2))
+            rc = rc2;
+
+        goto out;
+    }
+
+    /* Update pImage with the new information. */
+    pImage->pszFilename = pszFilename;
+
+    /* Open the old image with new name. */
+    rc = rawOpenImage(pImage, pImage->uOpenFlags);
+    if (RT_FAILURE(rc))
+        goto out;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int rawClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    rc = rawFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int rawRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                   size_t cbToRead, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToRead % 512 == 0);
+
+    if (   uOffset + cbToRead > pImage->cbSize
+        || cbToRead == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* For sequential access do not allow to go back. */
+    if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
+        && uOffset < pImage->offAccess)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    rc = rawFileReadSync(pImage, uOffset, pvBuf, cbToRead, NULL);
+    pImage->offAccess = uOffset + cbToRead;
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbToRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int rawWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                    size_t cbToWrite, size_t *pcbWriteProcess,
+                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    if (   uOffset + cbToWrite > pImage->cbSize
+        || cbToWrite == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* For sequential access do not allow to go back. */
+    if (   pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL
+        && uOffset < pImage->offAccess)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    rc = rawFileWriteSync(pImage, uOffset, pvBuf, cbToWrite, NULL);
+    pImage->offAccess = uOffset + cbToWrite;
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbToWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int rawFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    rc = rawFlushImage(pImage);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned rawGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        return 1;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t rawGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+        cb = pImage->cbSize;
+
+    LogFlowFunc(("returns %llu\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t rawGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        uint64_t cbFile;
+        if (pImage->pStorage)
+        {
+            int rc = rawFileGetSize(pImage, &cbFile);
+            if (RT_SUCCESS(rc))
+                cb += cbFile;
+        }
+    }
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int rawGetPCHSGeometry(void *pBackendData,
+                              PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pImage->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int rawSetPCHSGeometry(void *pBackendData,
+                              PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int rawGetLCHSGeometry(void *pBackendData,
+                              PVDGEOMETRY pLCHSGeometry)
+{
+     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->LCHSGeometry.cCylinders)
+        {
+            *pLCHSGeometry = pImage->LCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int rawSetLCHSGeometry(void *pBackendData,
+                               PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->LCHSGeometry = *pLCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned rawGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uImageFlags = pImage->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned rawGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int rawSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    rc = rawFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = rawOpenImage(pImage, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int rawGetComment(void *pBackendData, char *pszComment,
+                          size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int rawSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int rawGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int rawSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int rawGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int rawSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int rawGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int rawSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int rawGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int rawSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void rawDump(void *pBackendData)
+{
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        rawMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
+                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
+                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
+                    pImage->cbSize / 512);
+    }
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
+static bool rawIsAsyncIOSupported(void *pBackendData)
+{
+    return true;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
+static int rawAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    int rc = VINF_SUCCESS;
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    rc = rawFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
+    if (RT_SUCCESS(rc))
+        *pcbActuallyRead = cbRead;
+
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
+static int rawAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                         PVDIOCTX pIoCtx,
+                         size_t *pcbWriteProcess, size_t *pcbPreRead,
+                         size_t *pcbPostRead, unsigned fWrite)
+{
+    int rc = VINF_SUCCESS;
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    rc = rawFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        *pcbWriteProcess = cbWrite;
+        *pcbPostRead = 0;
+        *pcbPreRead  = 0;
+    }
+
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
+static int rawAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PRAWIMAGE pImage = (PRAWIMAGE)pBackendData;
+
+    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        rc = rawFileFlushAsync(pImage, pIoCtx, NULL, NULL);
+
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_RawBackend =
+{
+    /* pszBackendName */
+    "RAW",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+    VD_CAP_CREATE_FIXED | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aRawFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    rawCheckIfValid,
+    /* pfnOpen */
+    rawOpen,
+    /* pfnCreate */
+    rawCreate,
+    /* pfnRename */
+    rawRename,
+    /* pfnClose */
+    rawClose,
+    /* pfnRead */
+    rawRead,
+    /* pfnWrite */
+    rawWrite,
+    /* pfnFlush */
+    rawFlush,
+    /* pfnGetVersion */
+    rawGetVersion,
+    /* pfnGetSize */
+    rawGetSize,
+    /* pfnGetFileSize */
+    rawGetFileSize,
+    /* pfnGetPCHSGeometry */
+    rawGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    rawSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    rawGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    rawSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    rawGetImageFlags,
+    /* pfnGetOpenFlags */
+    rawGetOpenFlags,
+    /* pfnSetOpenFlags */
+    rawSetOpenFlags,
+    /* pfnGetComment */
+    rawGetComment,
+    /* pfnSetComment */
+    rawSetComment,
+    /* pfnGetUuid */
+    rawGetUuid,
+    /* pfnSetUuid */
+    rawSetUuid,
+    /* pfnGetModificationUuid */
+    rawGetModificationUuid,
+    /* pfnSetModificationUuid */
+    rawSetModificationUuid,
+    /* pfnGetParentUuid */
+    rawGetParentUuid,
+    /* pfnSetParentUuid */
+    rawSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    rawGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    rawSetParentModificationUuid,
+    /* pfnDump */
+    rawDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    rawIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    rawAsyncRead,
+    /* pfnAsyncWrite */
+    rawAsyncWrite,
+    /* pfnAsyncFlush */
+    rawAsyncFlush,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    NULL
+};
Index: /trunk/src/VBox/Storage/VCICache.cpp
===================================================================
--- /trunk/src/VBox/Storage/VCICache.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/VCICache.cpp	(revision 33567)
@@ -0,0 +1,2104 @@
+/* $Id$ */
+/** @file
+ * VCICacheCore - VirtualBox Cache Image, Core Code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_RAW /** @todo logging group */
+#include <VBox/vd-cache-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/file.h>
+#include <iprt/asm.h>
+
+/*******************************************************************************
+* On disk data structures                                                      *
+*******************************************************************************/
+
+/** @note All structures which are written to the disk are written in camel case
+ * and packed. */
+
+/** Block size used internally, because we cache sectors the smallest unit we
+ * have to care about is 512 bytes. */
+#define VCI_BLOCK_SIZE             512
+
+/** Convert block number/size to byte offset/size. */
+#define VCI_BLOCK2BYTE(u)          ((uint64_t)(u) << 9)
+
+/** Convert byte offset/size to block number/size. */
+#define VCI_BYTE2BLOCK(u)          ((u) >> 9)
+
+/**
+ * The VCI header - at the beginning of the file.
+ *
+ * All entries a stored in little endian order.
+ */
+#pragma pack(1)
+typedef struct VciHdr
+{
+    /** The signature to identify a cache image. */
+    uint32_t    u32Signature;
+    /** Version of the layout of metadata in the cache. */
+    uint32_t    u32Version;
+    /** Maximum size of the cache file in blocks.
+     *  This includes all metadata. */
+    uint64_t    cBlocksCache;
+    /** Flag indicating whether the cache was closed cleanly. */
+    uint8_t     fUncleanShutdown;
+    /** Cache type. */
+    uint32_t    u32CacheType;
+    /** Offset of the B+-Tree root in the image in blocks. */
+    uint64_t    offTreeRoot;
+    /** Offset of the block allocation bitmap in blocks. */
+    uint64_t    offBlkMap;
+    /** Size of the block allocation bitmap in blocks. */
+    uint32_t    cBlkMap;
+    /** UUID of the image. */
+    RTUUID      uuidImage;
+    /** Modification UUID for the cache. */
+    RTUUID      uuidModification;
+    /** Reserved for future use. */
+    uint8_t     abReserved[951];
+} VciHdr, *PVciHdr;
+#pragma pack()
+AssertCompileSize(VciHdr, 2 * VCI_BLOCK_SIZE);
+
+/** VCI signature to identify a valid image. */
+#define VCI_HDR_SIGNATURE          UINT32_C(0x56434900) /* VCI\0 */
+/** Current version we support. */
+#define VCI_HDR_VERSION            UINT32_C(0x00000001)
+
+/** Value for an unclean cache shutdown. */
+#define VCI_HDR_UNCLEAN_SHUTDOWN   UINT8_C(0x01)
+/** Value for a clean cache shutdown. */
+#define VCI_HDR_CLEAN_SHUTDOWN     UINT8_C(0x00)
+
+/** Cache type: Dynamic image growing to the maximum value. */
+#define VCI_HDR_CACHE_TYPE_DYNAMIC UINT32_C(0x00000001)
+/** Cache type: Fixed image, space is preallocated. */
+#define VCI_HDR_CACHE_TYPE_FIXED   UINT32_C(0x00000002)
+
+/**
+ * On disk representation of an extent describing a range of cached data.
+ *
+ * All entries a stored in little endian order.
+ */
+#pragma pack(1)
+typedef struct VciCacheExtent
+{
+    /** Block address of the previous extent in the LRU list. */
+    uint64_t    u64ExtentPrev;
+    /** Block address of the next extent in the LRU list. */
+    uint64_t    u64ExtentNext;
+    /** Flags (for compression, encryption etc.) - currently unused and should be always 0. */
+    uint8_t     u8Flags;
+    /** Reserved */
+    uint8_t     u8Reserved;
+    /** First block of cached data the extent represents. */
+    uint64_t    u64BlockOffset;
+    /** Number of blocks the extent represents. */
+    uint32_t    u32Blocks;
+    /** First block in the image where the data is stored. */
+    uint64_t    u64BlockAddr;
+} VciCacheExtent, *PVciCacheExtent;
+#pragma pack()
+AssertCompileSize(VciCacheExtent, 38);
+
+/**
+ * On disk representation of an internal node.
+ *
+ * All entries a stored in little endian order.
+ */
+#pragma pack(1)
+typedef struct VciTreeNodeInternal
+{
+    /** First block of cached data the internal node represents. */
+    uint64_t    u64BlockOffset;
+    /** Number of blocks the internal node represents. */
+    uint32_t    u32Blocks;
+    /** Block address in the image where the next node in the tree is stored. */
+    uint64_t    u64ChildAddr;
+} VciTreeNodeInternal, *PVciTreeNodeInternal;
+#pragma pack()
+AssertCompileSize(VciTreeNodeInternal, 20);
+
+/**
+ * On-disk representation of a node in the B+-Tree.
+ *
+ * All entries a stored in little endian order.
+ */
+#pragma pack(1)
+typedef struct VciTreeNode
+{
+    /** Type of the node (root, internal, leaf). */
+    uint8_t     u8Type;
+    /** Data in the node. */
+    uint8_t     au8Data[4095];
+} VciTreeNode, *PVciTreeNode;
+#pragma pack()
+AssertCompileSize(VciTreeNode, 8 * VCI_BLOCK_SIZE);
+
+/** Node type: Internal node containing links to other nodes (VciTreeNodeInternal). */
+#define VCI_TREE_NODE_TYPE_INTERNAL UINT8_C(0x01)
+/** Node type: Leaf of the tree (VciCacheExtent). */
+#define VCI_TREE_NODE_TYPE_LEAF     UINT8_C(0x02)
+
+/** Number of cache extents described by one node. */
+#define VCI_TREE_EXTENTS_PER_NODE        ((sizeof(VciTreeNode)-1) / sizeof(VciCacheExtent))
+/** Number of internal nodes managed by one tree node. */
+#define VCI_TREE_INTERNAL_NODES_PER_NODE ((sizeof(VciTreeNode)-1) / sizeof(VciTreeNodeInternal))
+
+/**
+ * VCI block bitmap header.
+ *
+ * All entries a stored in little endian order.
+ */
+#pragma pack(1)
+typedef struct VciBlkMap
+{
+    /** Magic of the block bitmap. */
+    uint32_t     u32Magic;
+    /** Version of the block bitmap. */
+    uint32_t     u32Version;
+    /** Number of blocks this block map manages. */
+    uint64_t     cBlocks;
+    /** Number of free blocks. */
+    uint64_t     cBlocksFree;
+    /** Number of blocks allocated for metadata. */
+    uint64_t     cBlocksAllocMeta;
+    /** Number of blocks allocated for actual cached data. */
+    uint64_t     cBlocksAllocData;
+    /** Reserved for future use. */
+    uint8_t      au8Reserved[472];
+} VciBlkMap, *PVciBlkMap;
+#pragma pack()
+AssertCompileSize(VciBlkMap, VCI_BLOCK_SIZE);
+
+/** The magic which identifies a block map. */
+#define VCI_BLKMAP_MAGIC   UINT32_C(0x56424c4b) /* VBLK */
+/** Current version. */
+#define VCI_BLKMAP_VERSION UINT32_C(0x00000001)
+
+/** Block bitmap entry */
+typedef uint8_t VciBlkMapEnt;
+
+/*******************************************************************************
+*   Constants And Macros, Structures and Typedefs                              *
+*******************************************************************************/
+
+/**
+ * Block range descriptor.
+ */
+typedef struct VCIBLKRANGEDESC
+{
+    /** Previous entry in the list. */
+    struct VCIBLKRANGEDESC    *pPrev;
+    /** Next entry in the list. */
+    struct VCIBLKRANGEDESC    *pNext;
+    /** Start address of the range. */
+    uint64_t                   offAddrStart;
+    /** Number of blocks in the range. */
+    uint64_t                   cBlocks;
+    /** Flag whether the range is free or allocated. */
+    bool                       fFree;
+} VCIBLKRANGEDESC, *PVCIBLKRANGEDESC;
+
+/**
+ * Block map for the cache image - in memory structure.
+ */
+typedef struct VCIBLKMAP
+{
+    /** Number of blocks the map manages. */
+    uint64_t     cBlocks;
+    /** Number of blocks allocated for metadata. */
+    uint64_t     cBlocksAllocMeta;
+    /** Number of blocks allocated for actual cached data. */
+    uint64_t     cBlocksAllocData;
+    /** Number of free blocks. */
+    uint64_t     cBlocksFree;
+
+    /** Pointer to the head of the block range list. */
+    PVCIBLKRANGEDESC pRangesHead;
+    /** Pointer to the tail of the block range list. */
+    PVCIBLKRANGEDESC pRangesTail;
+
+} VCIBLKMAP;
+/** Pointer to a block map. */
+typedef VCIBLKMAP *PVCIBLKMAP;
+
+/**
+ * B+-Tree node header.
+ */
+typedef struct VCITREENODE
+{
+    /** Type of the node (VCI_TREE_NODE_TYPE_*). */
+    uint8_t             u8Type;
+    /** Block address where the node is stored. */
+    uint64_t            u64BlockAddr;
+    /** Pointer to the parent. */
+    struct VCITREENODE *pParent;
+} VCITREENODE, *PVCITREENODE;
+
+/**
+ * B+-Tree node pointer.
+ */
+typedef struct VCITREENODEPTR
+{
+    /** Flag whether the node is in memory or still on the disk. */
+    bool         fInMemory;
+    /** Type dependent data. */
+    union
+    {
+        /** Pointer to a in memory node. */
+        PVCITREENODE pNode;
+        /** Start block address of the node. */
+        uint64_t     offAddrBlockNode;
+    } u;
+} VCITREENODEPTR, *PVCITREENODEPTR;
+
+/**
+ * Internal node.
+ */
+typedef struct VCINODEINTERNAL
+{
+    /** First block of cached data the internal node represents. */
+    uint64_t       u64BlockOffset;
+    /** Number of blocks the internal node represents. */
+    uint32_t       u32Blocks;
+    /** Pointer to the child node. */
+    VCITREENODEPTR PtrChild;
+} VCINODEINTERNAL, *PVCINODEINTERNAL;
+
+/**
+ * A in memory internal B+-tree node.
+ */
+typedef struct VCITREENODEINT
+{
+    /** Node core. */
+    VCITREENODE     Core;
+    /** Number of used nodes. */
+    unsigned        cUsedNodes;
+    /** Array of internal nodes. */
+    VCINODEINTERNAL aIntNodes[VCI_TREE_INTERNAL_NODES_PER_NODE];
+} VCITREENODEINT, *PVCITREENODEINT;
+
+/**
+ * A in memory cache extent.
+ */
+typedef struct VCICACHEEXTENT
+{
+    /** First block of cached data the extent represents. */
+    uint64_t    u64BlockOffset;
+    /** Number of blocks the extent represents. */
+    uint32_t    u32Blocks;
+    /** First block in the image where the data is stored. */
+    uint64_t    u64BlockAddr;
+} VCICACHEEXTENT, *PVCICACHEEXTENT;
+
+/**
+ * A in memory leaf B+-tree node.
+ */
+typedef struct VCITREENODELEAF
+{
+    /** Node core. */
+    VCITREENODE    Core;
+    /** Number of used nodes. */
+    unsigned       cUsedNodes;
+    /** The extents in the node. */
+    VCICACHEEXTENT aExtents[VCI_TREE_EXTENTS_PER_NODE];
+} VCITREENODELEAF, *PVCITREENODELEAF;
+
+/**
+ * VCI image data structure.
+ */
+typedef struct VCICACHE
+{
+    /** Image name. */
+    const char       *pszFilename;
+    /** Storage handle. */
+    PVDIOSTORAGE      pStorage;
+    /** I/O interface. */
+    PVDINTERFACE      pInterfaceIO;
+    /** Async I/O interface callbacks. */
+    PVDINTERFACEIOINT pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE      pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE      pVDIfsImage;
+
+    /** Error callback. */
+    PVDINTERFACE      pInterfaceError;
+    /** Opaque data for error callback. */
+    PVDINTERFACEERROR pInterfaceErrorCallbacks;
+
+    /** Open flags passed by VBoxHD layer. */
+    unsigned          uOpenFlags;
+    /** Image flags defined during creation or determined during open. */
+    unsigned          uImageFlags;
+    /** Total size of the image. */
+    uint64_t          cbSize;
+
+    /** Offset of the B+-Tree in the image in bytes. */
+    uint64_t          offTreeRoot;
+    /** Pointer to the root node of the B+-Tree. */
+    PVCITREENODE      pRoot;
+    /** Offset to the block allocation bitmap in bytes. */
+    uint64_t          offBlksBitmap;
+    /** Block map. */
+    PVCIBLKMAP        pBlkMap;
+} VCICACHE, *PVCICACHE;
+
+/** No block free in bitmap error code. */
+#define VERR_VCI_NO_BLOCKS_FREE (-65536)
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const char *const s_apszVciFileExtensions[] =
+{
+    "vci",
+    NULL
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) vciError(PVCICACHE pCache, int rc, RT_SRC_POS_DECL,
+                         const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pCache->pInterfaceError)
+        pCache->pInterfaceErrorCallbacks->pfnError(pCache->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) vciMessage(PVCICACHE pCache, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pCache->pInterfaceError)
+        rc = pCache->pInterfaceErrorCallbacks->pfnMessage(pCache->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) vciFileOpen(PVCICACHE pCache, const char *pszFilename,
+                            uint32_t fOpen)
+{
+    return pCache->pInterfaceIOCallbacks->pfnOpen(pCache->pInterfaceIO->pvUser,
+                                                  pszFilename, fOpen,
+                                                  &pCache->pStorage);
+}
+
+DECLINLINE(int) vciFileClose(PVCICACHE pCache)
+{
+    return pCache->pInterfaceIOCallbacks->pfnClose(pCache->pInterfaceIO->pvUser,
+                                                   pCache->pStorage);
+}
+
+DECLINLINE(int) vciFileDelete(PVCICACHE pCache, const char *pszFilename)
+{
+    return pCache->pInterfaceIOCallbacks->pfnDelete(pCache->pInterfaceIO->pvUser,
+                                                    pszFilename);
+}
+
+DECLINLINE(int) vciFileMove(PVCICACHE pCache, const char *pszSrc,
+                            const char *pszDst, unsigned fMove)
+{
+    return pCache->pInterfaceIOCallbacks->pfnMove(pCache->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) vciFileGetFreeSpace(PVCICACHE pCache, const char *pszFilename,
+                                    int64_t *pcbFree)
+{
+    return pCache->pInterfaceIOCallbacks->pfnGetFreeSpace(pCache->pInterfaceIO->pvUser,
+                                                          pszFilename, pcbFree);
+}
+
+DECLINLINE(int) vciFileGetSize(PVCICACHE pCache, uint64_t *pcbSize)
+{
+    return pCache->pInterfaceIOCallbacks->pfnGetSize(pCache->pInterfaceIO->pvUser,
+                                                     pCache->pStorage, pcbSize);
+}
+
+DECLINLINE(int) vciFileSetSize(PVCICACHE pCache, uint64_t cbSize)
+{
+    return pCache->pInterfaceIOCallbacks->pfnSetSize(pCache->pInterfaceIO->pvUser,
+                                                     pCache->pStorage, cbSize);
+}
+
+DECLINLINE(int) vciFileWriteSync(PVCICACHE pCache, uint64_t uOffset,
+                                 const void *pvBuffer, size_t cbBuffer)
+{
+    return pCache->pInterfaceIOCallbacks->pfnWriteSync(pCache->pInterfaceIO->pvUser,
+                                                       pCache->pStorage, uOffset,
+                                                       pvBuffer, cbBuffer, NULL);
+}
+
+DECLINLINE(int) vciFileReadSync(PVCICACHE pCache, uint64_t uOffset,
+                                void *pvBuffer, size_t cbBuffer)
+{
+    return pCache->pInterfaceIOCallbacks->pfnReadSync(pCache->pInterfaceIO->pvUser,
+                                                      pCache->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, NULL);
+}
+
+DECLINLINE(int) vciFileFlushSync(PVCICACHE pCache)
+{
+    return pCache->pInterfaceIOCallbacks->pfnFlushSync(pCache->pInterfaceIO->pvUser,
+                                                       pCache->pStorage);
+}
+
+DECLINLINE(int) vciFileReadUserAsync(PVCICACHE pCache, uint64_t uOffset,
+                                     PVDIOCTX pIoCtx, size_t cbRead)
+{
+    return pCache->pInterfaceIOCallbacks->pfnReadUserAsync(pCache->pInterfaceIO->pvUser,
+                                                           pCache->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+DECLINLINE(int) vciFileWriteUserAsync(PVCICACHE pCache, uint64_t uOffset,
+                                      PVDIOCTX pIoCtx, size_t cbWrite,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pCache->pInterfaceIOCallbacks->pfnWriteUserAsync(pCache->pInterfaceIO->pvUser,
+                                                            pCache->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) vciFileFlushAsync(PVCICACHE pCache, PVDIOCTX pIoCtx,
+                                  PFNVDXFERCOMPLETED pfnComplete,
+                                  void *pvCompleteUser)
+{
+    return pCache->pInterfaceIOCallbacks->pfnFlushAsync(pCache->pInterfaceIO->pvUser,
+                                                        pCache->pStorage,
+                                                        pIoCtx, pfnComplete,
+                                                        pvCompleteUser);
+}
+
+/**
+ * Internal. Flush image data to disk.
+ */
+static int vciFlushImage(PVCICACHE pCache)
+{
+    int rc = VINF_SUCCESS;
+
+    if (   pCache->pStorage
+        && !(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        rc = vciFileFlushSync(pCache);
+
+    return rc;
+}
+
+/**
+ * Internal. Free all allocated space for representing an image except pCache,
+ * and optionally delete the image from disk.
+ */
+static int vciFreeImage(PVCICACHE pCache, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pCache)
+    {
+        if (pCache->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+                vciFlushImage(pCache);
+
+            vciFileClose(pCache);
+            pCache->pStorage = NULL;
+        }
+
+        if (fDelete && pCache->pszFilename)
+            vciFileDelete(pCache, pCache->pszFilename);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Creates a new block map which can manage the given number of blocks.
+ *
+ * The size of the bitmap is aligned to the VCI block size.
+ *
+ * @returns VBox status code.
+ * @param   cBlocks      The number of blocks the bitmap can manage.
+ * @param   ppBlkMap     Where to store the pointer to the block bitmap.
+ * @param   pcbBlkMap    Where to store the size of the block bitmap in blocks
+ *                       needed on the disk.
+ */
+static int vciBlkMapCreate(uint64_t cBlocks, PVCIBLKMAP *ppBlkMap, uint32_t *pcBlkMap)
+{
+    int rc = VINF_SUCCESS;
+    uint32_t cbBlkMap = RT_ALIGN_Z(cBlocks / sizeof(VciBlkMapEnt) / 8, VCI_BLOCK_SIZE);
+    PVCIBLKMAP pBlkMap = (PVCIBLKMAP)RTMemAllocZ(sizeof(VCIBLKMAP));
+    PVCIBLKRANGEDESC pFree   = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
+
+    LogFlowFunc(("cBlocks=%u ppBlkMap=%#p pcBlkMap=%#p\n", cBlocks, ppBlkMap, pcBlkMap));
+
+    if (pBlkMap && pFree)
+    {
+        pBlkMap->cBlocks          = cBlocks;
+        pBlkMap->cBlocksAllocMeta = 0;
+        pBlkMap->cBlocksAllocData = 0;
+        pBlkMap->cBlocksFree      = cBlocks;
+
+        pFree->pPrev = NULL;
+        pFree->pNext = NULL;
+        pFree->offAddrStart = 0;
+        pFree->cBlocks      = cBlocks;
+        pFree->fFree        = true;
+
+        pBlkMap->pRangesHead = pFree;
+        pBlkMap->pRangesTail = pFree;
+
+        Assert(!((cbBlkMap + sizeof(VciBlkMap) % VCI_BLOCK_SIZE)));
+        *ppBlkMap = pBlkMap;
+        *pcBlkMap = (cbBlkMap + sizeof(VciBlkMap)) / VCI_BLOCK_SIZE;
+    }
+    else
+    {
+        if (pBlkMap)
+            RTMemFree(pBlkMap);
+        if (pFree)
+            RTMemFree(pFree);
+
+        rc = VERR_NO_MEMORY;
+    }
+
+    LogFlowFunc(("returns rc=%Rrc cBlkMap=%u\n", rc, *pcBlkMap));
+    return rc;
+}
+
+/**
+ * Frees a block map.
+ *
+ * @returns nothing.
+ * @param   pBlkMap         The block bitmap to destroy.
+ */
+static void vciBlkMapDestroy(PVCIBLKMAP pBlkMap)
+{
+    LogFlowFunc(("pBlkMap=%#p\n", pBlkMap));
+
+    PVCIBLKRANGEDESC pRangeCur = pBlkMap->pRangesHead;
+
+    while (pRangeCur)
+    {
+        PVCIBLKRANGEDESC pTmp = pRangeCur;
+
+        RTMemFree(pTmp);
+
+        pRangeCur = pRangeCur->pNext;
+    }
+
+    RTMemFree(pBlkMap);
+
+    LogFlowFunc(("returns\n"));
+}
+
+/**
+ * Loads the block map from the specified medium and creates all necessary
+ * in memory structures to manage used and free blocks.
+ *
+ * @returns VBox status code.
+ * @param   pStorage        Storage handle to read the block bitmap from.
+ * @param   offBlkMap       Start of the block bitmap in blocks.
+ * @param   cBlkMap         Size of the block bitmap on the disk in blocks.
+ * @param   ppBlkMap        Where to store the block bitmap on success.
+ */
+static int vciBlkMapLoad(PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap, PVCIBLKMAP *ppBlkMap)
+{
+    int rc = VINF_SUCCESS;
+    VciBlkMap BlkMap;
+
+    LogFlowFunc(("pStorage=%#p offBlkMap=%llu cBlkMap=%u ppBlkMap=%#p\n",
+                 pStorage, offBlkMap, cBlkMap, ppBlkMap));
+
+    if (cBlkMap >= VCI_BYTE2BLOCK(sizeof(VciBlkMap)))
+    {
+        cBlkMap -= VCI_BYTE2BLOCK(sizeof(VciBlkMap));
+
+        rc = vciFileReadSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
+        if (RT_SUCCESS(rc))
+        {
+            offBlkMap += VCI_BYTE2BLOCK(sizeof(VciBlkMap));
+
+            BlkMap.u32Magic         = RT_LE2H_U32(BlkMap.u32Magic);
+            BlkMap.u32Version       = RT_LE2H_U32(BlkMap.u32Version);
+            BlkMap.cBlocks          = RT_LE2H_U32(BlkMap.cBlocks);
+            BlkMap.cBlocksFree      = RT_LE2H_U32(BlkMap.cBlocksFree);
+            BlkMap.cBlocksAllocMeta = RT_LE2H_U32(BlkMap.cBlocksAllocMeta);
+            BlkMap.cBlocksAllocData = RT_LE2H_U32(BlkMap.cBlocksAllocData);
+
+            if (   BlkMap.u32Magic == VCI_BLKMAP_MAGIC
+                && BlkMap.u32Version == VCI_BLKMAP_VERSION
+                && BlkMap.cBlocks == BlkMap.cBlocksFree + BlkMap.cBlocksAllocMeta + BlkMap.cBlocksAllocData
+                && BlkMap.cBlocks / 8 == cBlkMap)
+            {
+                PVCIBLKMAP pBlkMap = (PVCIBLKMAP)RTMemAllocZ(sizeof(VCIBLKMAP));
+                if (pBlkMap)
+                {
+                    pBlkMap->cBlocks          = BlkMap.cBlocks;
+                    pBlkMap->cBlocksFree      = BlkMap.cBlocksFree;
+                    pBlkMap->cBlocksAllocMeta = BlkMap.cBlocksAllocMeta;
+                    pBlkMap->cBlocksAllocData = BlkMap.cBlocksAllocData;
+
+                    /* Load the bitmap and construct the range list. */
+                    uint32_t cBlocksFree = 0;
+                    uint32_t cBlocksAllocated = 0;
+                    PVCIBLKRANGEDESC pRangeCur = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
+
+                    if (pRangeCur)
+                    {
+                        uint8_t abBitmapBuffer[16 * _1K];
+                        uint32_t cBlocksRead = 0;
+                        int iBit = 0;
+                        uint64_t cBlocksLeft = pBlkMap->cBlocks;
+
+                        cBlocksRead = RT_MIN(VCI_BYTE2BLOCK(sizeof(abBitmapBuffer)), cBlocksLeft);
+                        rc = vciFileReadSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), abBitmapBuffer,
+                                             VCI_BLOCK2BYTE(cBlocksRead));
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            pRangeCur->fFree        = !(abBitmapBuffer[0] & 0x01);
+                            pRangeCur->offAddrStart = 0;
+                            pRangeCur->cBlocks      = 0;
+                            pRangeCur->pNext        = NULL;
+                            pRangeCur->pPrev        = NULL;
+                            pBlkMap->pRangesHead = pRangeCur;
+                            pBlkMap->pRangesTail = pRangeCur;
+                        }
+
+                        while (   RT_SUCCESS(rc)
+                               && cBlocksLeft)
+                        {
+#if 0
+                            while (cBlocksRead)
+                            {
+                                if (pRangeCur->fFree)
+                                {
+                                    /* Check for the first set bit. */
+                                }
+                                else
+                                {
+                                    /* Check for the first free bit. */
+                                }
+
+                                if (iBit == -1)
+                                {
+                                    /* No change. */
+                                    pRangeCur->cBlocks += cBlocksRead;
+                                    cBlocksRead = 0;
+                                }
+                                else
+                                {
+                                    /* Create a new range descriptor. */
+                                }
+                            }
+#endif
+                            cBlocksLeft -= cBlocksRead;
+                            offBlkMap   += cBlocksRead;
+
+                            if (cBlocksLeft)
+                            {
+                                /* Read next chunk. */
+                            }
+                        }
+                    }
+                    else
+                        rc = VERR_NO_MEMORY;
+
+                    if (RT_SUCCESS(rc))
+                    {
+                        *ppBlkMap = pBlkMap;
+                        LogFlowFunc(("return success\n"));
+                        return VINF_SUCCESS;
+                    }
+                    else
+                        RTMemFree(pBlkMap);
+                }
+                else
+                    rc = VERR_NO_MEMORY;
+            }
+            else
+                rc = VERR_VD_GEN_INVALID_HEADER;
+        }
+        else if (RT_SUCCESS(rc))
+            rc = VERR_VD_GEN_INVALID_HEADER;
+    }
+    else
+        rc = VERR_VD_GEN_INVALID_HEADER;
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Saves the block map in the cache image. All necessary on disk structures
+ * are written.
+ *
+ * @returns VBox status code.
+ * @param   pBlkMap         The block bitmap to save.
+ * @param   pStorage        Where the block bitmap should be written to.
+ * @param   offBlkMap       Start of the block bitmap in blocks.
+ * @param   cBlkMap         Size of the block bitmap on the disk in blocks.
+ */
+static int vciBlkMapSave(PVCIBLKMAP pBlkMap, PVCICACHE pStorage, uint64_t offBlkMap, uint32_t cBlkMap)
+{
+    int rc = VINF_SUCCESS;
+    VciBlkMap BlkMap;
+
+    LogFlowFunc(("pBlkMap=%#p pStorage=%#p offBlkMap=%llu cBlkMap=%u\n",
+                 pBlkMap, pStorage, offBlkMap, cBlkMap));
+
+    /* Make sure the number of blocks allocated for us match our expectations. */
+    if ((pBlkMap->cBlocks / 8) + VCI_BYTE2BLOCK(sizeof(VciBlkMap)) == cBlkMap)
+    {
+        /* Setup the header */
+        memset(&BlkMap, 0, sizeof(VciBlkMap));
+
+        BlkMap.u32Magic         = RT_H2LE_U32(VCI_BLKMAP_MAGIC);
+        BlkMap.u32Version       = RT_H2LE_U32(VCI_BLKMAP_VERSION);
+        BlkMap.cBlocks          = RT_H2LE_U32(pBlkMap->cBlocks);
+        BlkMap.cBlocksFree      = RT_H2LE_U32(pBlkMap->cBlocksFree);
+        BlkMap.cBlocksAllocMeta = RT_H2LE_U32(pBlkMap->cBlocksAllocMeta);
+        BlkMap.cBlocksAllocData = RT_H2LE_U32(pBlkMap->cBlocksAllocData);
+
+        rc = vciFileWriteSync(pStorage, VCI_BLOCK2BYTE(offBlkMap), &BlkMap, VCI_BYTE2BLOCK(sizeof(VciBlkMap)));
+        if (RT_SUCCESS(rc))
+        {
+            uint8_t abBitmapBuffer[16*_1K];
+            unsigned iBit = 0;
+            PVCIBLKRANGEDESC pCur = pBlkMap->pRangesHead;
+
+            offBlkMap += VCI_BYTE2BLOCK(sizeof(VciBlkMap));
+
+            /* Write the descriptor ranges. */
+            while (pCur)
+            {
+                uint64_t cBlocks = pCur->cBlocks;
+
+                while (cBlocks)
+                {
+                    uint64_t cBlocksMax = RT_MIN(cBlocks, sizeof(abBitmapBuffer) * 8 - iBit);
+
+                    if (pCur->fFree)
+                        ASMBitClearRange(abBitmapBuffer, iBit, iBit + cBlocksMax);
+                    else
+                        ASMBitSetRange(abBitmapBuffer, iBit, iBit + cBlocksMax);
+
+                    iBit    += cBlocksMax;
+                    cBlocks -= cBlocksMax;
+
+                    if (iBit == sizeof(abBitmapBuffer) * 8)
+                    {
+                        /* Buffer is full, write to file and reset. */
+                        rc = vciFileWriteSync(pStorage, offBlkMap, abBitmapBuffer, sizeof(abBitmapBuffer));
+                        if (RT_FAILURE(rc))
+                            break;
+
+                        offBlkMap += VCI_BYTE2BLOCK(sizeof(abBitmapBuffer));
+                        iBit = 0;
+                    }
+                }
+
+                pCur = pCur->pNext;
+            }
+
+            Assert(iBit % 8 == 0);
+
+            if (RT_SUCCESS(rc) && iBit)
+                rc = vciFileWriteSync(pStorage, offBlkMap, abBitmapBuffer, iBit / 8);
+        }
+    }
+    else
+        rc = VERR_INTERNAL_ERROR; /* @todo Better error code. */
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Finds the range block describing the given block address.
+ *
+ * @returns Pointer to the block range descriptor or NULL if none could be found.
+ * @param   pBlkMap         The block bitmap to search on.
+ * @param   offBlockAddr    The block address to search for.
+ */
+static PVCIBLKRANGEDESC vciBlkMapFindByBlock(PVCIBLKMAP pBlkMap, uint64_t offBlockAddr)
+{
+    PVCIBLKRANGEDESC pBlk = pBlkMap->pRangesHead;
+
+    while (   pBlk
+           && pBlk->offAddrStart < offBlockAddr)
+        pBlk = pBlk->pNext;
+
+    return pBlk;
+}
+
+/**
+ * Allocates the given number of blocks in the bitmap and returns the start block address.
+ *
+ * @returns VBox status code.
+ * @param   pBlkMap          The block bitmap to allocate the blocks from.
+ * @param   cBlocks          How many blocks to allocate.
+ * @param   poffBlockAddr    Where to store the start address of the allocated region.
+ */
+static int vciBlkMapAllocate(PVCIBLKMAP pBlkMap, uint32_t cBlocks, uint64_t *poffBlockAddr)
+{
+    PVCIBLKRANGEDESC pBestFit = NULL;
+    PVCIBLKRANGEDESC pCur = NULL;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pBlkMap=%#p cBlocks=%u poffBlockAddr=%#p\n",
+                 pBlkMap, cBlocks, poffBlockAddr));
+
+    pCur = pBlkMap->pRangesHead;
+
+    while (pCur)
+    {
+        if (   pCur->fFree
+            && pCur->cBlocks >= cBlocks)
+        {
+            if (   !pBestFit
+                || pCur->cBlocks < pBestFit->cBlocks)
+            {
+                pBestFit = pCur;
+                /* Stop searching if the size is matching exactly. */
+                if (pBestFit->cBlocks == cBlocks)
+                    break;
+            }
+        }
+        pCur = pCur->pNext;
+    }
+
+    Assert(!pBestFit || pBestFit->fFree);
+
+    if (pBestFit)
+    {
+        pBestFit->fFree = false;
+
+        if (pBestFit->cBlocks > cBlocks)
+        {
+            /* Create a new free block. */
+            PVCIBLKRANGEDESC pFree = (PVCIBLKRANGEDESC)RTMemAllocZ(sizeof(VCIBLKRANGEDESC));
+
+            if (pFree)
+            {
+                pFree->fFree = true;
+                pFree->cBlocks = pBestFit->cBlocks - cBlocks;
+                pFree->offAddrStart = pBestFit->offAddrStart + cBlocks;
+
+                /* Link into the list. */
+                pFree->pNext = pBestFit->pNext;
+                pBestFit->pNext = pFree;
+                pFree->pPrev    = pBestFit;
+                if (!pFree->pNext)
+                    pBlkMap->pRangesTail = pFree;
+
+                *poffBlockAddr = pBestFit->offAddrStart;
+            }
+            else
+            {
+                rc = VERR_NO_MEMORY;
+                pBestFit->fFree = true;
+            }
+        }
+    }
+    else
+        rc = VERR_VCI_NO_BLOCKS_FREE;
+
+    LogFlowFunc(("returns rc=%Rrc offBlockAddr=%llu\n", rc, *poffBlockAddr));
+    return rc;
+}
+
+/**
+ * Try to extend the space of an already allocated block.
+ *
+ * @returns VBox status code.
+ * @param   pBlkMap          The block bitmap to allocate the blocks from.
+ * @param   cBlocksNew       How many blocks the extended block should have.
+ * @param   offBlockAddrOld  The start address of the block to reallocate.
+ * @param   poffBlockAddr    Where to store the start address of the allocated region.
+ */
+static int vciBlkMapRealloc(PVCIBLKMAP pBlkMap, uint32_t cBlocksNew, uint64_t offBlockAddrOld,
+                            uint64_t *poffBlockAddr)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pBlkMap=%#p cBlocksNew=%u offBlockAddrOld=%llu poffBlockAddr=%#p\n",
+                 pBlkMap, cBlocksNew, offBlockAddrOld, poffBlockAddr));
+
+    AssertMsgFailed(("Implement\n"));
+
+    LogFlowFunc(("returns rc=%Rrc offBlockAddr=%llu\n", rc, *poffBlockAddr));
+    return rc;
+}
+
+/**
+ * Frees a range of blocks.
+ *
+ * @returns nothing.
+ * @param   pBlkMap          The block bitmap.
+ * @param   offBlockAddr     Address of the first block to free.
+ * @param   cBlocks          How many blocks to free.
+ */
+static void vciBlkMapFree(PVCIBLKMAP pBlkMap, uint64_t offBlockAddr, uint32_t cBlocks)
+{
+    PVCIBLKRANGEDESC pBlk;
+
+    LogFlowFunc(("pBlkMap=%#p offBlockAddr=%llu cBlocks=%u\n",
+                 pBlkMap, offBlockAddr, cBlocks));
+
+    while (cBlocks)
+    {
+        pBlk = vciBlkMapFindByBlock(pBlkMap, offBlockAddr);
+        AssertPtr(pBlk);
+
+        /* Easy case, the whole block is freed. */
+        if (   pBlk->offAddrStart == offBlockAddr
+            && pBlk->cBlocks <= cBlocks)
+        {
+            pBlk->fFree = true;
+            cBlocks      -= pBlk->cBlocks;
+            offBlockAddr += pBlk->cBlocks;
+
+            /* Check if it is possible to merge free blocks. */
+            if (   pBlk->pPrev
+                && pBlk->pPrev->fFree)
+            {
+                PVCIBLKRANGEDESC pBlkPrev = pBlk->pPrev;
+
+                Assert(pBlkPrev->offAddrStart + pBlkPrev->cBlocks == pBlk->offAddrStart);
+                pBlkPrev->cBlocks += pBlk->cBlocks;
+                pBlkPrev->pNext = pBlk->pNext;
+                if (pBlk->pNext)
+                    pBlk->pNext->pPrev = pBlkPrev;
+                else
+                    pBlkMap->pRangesTail = pBlkPrev;
+
+                RTMemFree(pBlk);
+                pBlk = pBlkPrev;
+            }
+
+            /* Now the one to the right. */
+            if (   pBlk->pNext
+                && pBlk->pNext->fFree)
+            {
+                PVCIBLKRANGEDESC pBlkNext = pBlk->pNext;
+
+                Assert(pBlk->offAddrStart + pBlk->cBlocks == pBlkNext->offAddrStart);
+                pBlk->cBlocks += pBlkNext->cBlocks;
+                pBlk->pNext = pBlkNext->pNext;
+                if (pBlkNext->pNext)
+                    pBlkNext->pNext->pPrev = pBlk;
+                else
+                    pBlkMap->pRangesTail = pBlk;
+
+                RTMemFree(pBlkNext);
+            }
+        }
+        else
+        {
+            /* The block is intersecting. */
+            AssertMsgFailed(("TODO\n"));
+        }
+    }
+
+    LogFlowFunc(("returns\n"));
+}
+
+/**
+ * Converts a tree node from the image to the in memory structure.
+ *
+ * @returns Pointer to the in memory tree node.
+ * @param   offBlockAddrNode    Block address of the node.
+ * @param   pNodeImage          Pointer to the image representation of the node.
+ */
+static PVCITREENODE vciTreeNodeImage2Host(uint64_t offBlockAddrNode, PVciTreeNode pNodeImage)
+{
+    PVCITREENODE pNode = NULL;
+
+    if (pNodeImage->u8Type == VCI_TREE_NODE_TYPE_LEAF)
+    {
+        PVCITREENODELEAF pLeaf = (PVCITREENODELEAF)RTMemAllocZ(sizeof(VCITREENODELEAF));
+
+        if (pLeaf)
+        {
+            PVciCacheExtent pExtent = (PVciCacheExtent)&pNodeImage->au8Data[0];
+
+            pLeaf->Core.u8Type = VCI_TREE_NODE_TYPE_LEAF;
+
+            for (unsigned idx = 0; idx < RT_ELEMENTS(pLeaf->aExtents); idx++)
+            {
+                pLeaf->aExtents[idx].u64BlockOffset = RT_LE2H_U64(pExtent->u64BlockOffset);
+                pLeaf->aExtents[idx].u32Blocks      = RT_LE2H_U32(pExtent->u32Blocks);
+                pLeaf->aExtents[idx].u64BlockAddr   = RT_LE2H_U64(pExtent->u64BlockAddr);
+                pExtent++;
+
+                if (   pLeaf->aExtents[idx].u32Blocks
+                    && pLeaf->aExtents[idx].u64BlockAddr)
+                    pLeaf->cUsedNodes++;
+            }
+
+            pNode = &pLeaf->Core;
+        }
+    }
+    else if (pNodeImage->u8Type == VCI_TREE_NODE_TYPE_INTERNAL)
+    {
+        PVCITREENODEINT pInt = (PVCITREENODEINT)RTMemAllocZ(sizeof(VCITREENODEINT));
+
+        if (pInt)
+        {
+            PVciTreeNodeInternal pIntImage = (PVciTreeNodeInternal)&pNodeImage->au8Data[0];
+
+            pInt->Core.u8Type = VCI_TREE_NODE_TYPE_INTERNAL;
+
+            for (unsigned idx = 0; idx < RT_ELEMENTS(pInt->aIntNodes); idx++)
+            {
+                pInt->aIntNodes[idx].u64BlockOffset              = RT_LE2H_U64(pIntImage->u64BlockOffset);
+                pInt->aIntNodes[idx].u32Blocks                   = RT_LE2H_U32(pIntImage->u32Blocks);
+                pInt->aIntNodes[idx].PtrChild.fInMemory          = false;
+                pInt->aIntNodes[idx].PtrChild.u.offAddrBlockNode = RT_LE2H_U64(pIntImage->u64ChildAddr);
+                pIntImage++;
+
+                if (   pInt->aIntNodes[idx].u32Blocks
+                    && pInt->aIntNodes[idx].PtrChild.u.offAddrBlockNode)
+                    pInt->cUsedNodes++;
+            }
+
+            pNode = &pInt->Core;
+        }
+    }
+    else
+        AssertMsgFailed(("Invalid node type %d\n", pNodeImage->u8Type));
+
+    if (pNode)
+        pNode->u64BlockAddr = offBlockAddrNode;
+
+    return pNode;
+}
+
+/**
+ * Looks up the cache extent for the given virtual block address.
+ *
+ * @returns Pointer to the cache extent or NULL if none could be found.
+ * @param   pCache         The cache image instance.
+ * @param   offBlockOffset The block offset to search for.
+ */
+static PVCICACHEEXTENT vciCacheExtentLookup(PVCICACHE pCache, uint64_t offBlockOffset)
+{
+    int rc = VINF_SUCCESS;
+    PVCICACHEEXTENT pExtent = NULL;
+    PVCITREENODE pNodeCur = pCache->pRoot;
+
+    while (   RT_SUCCESS(rc)
+           && pNodeCur
+           && pNodeCur->u8Type != VCI_TREE_NODE_TYPE_LEAF)
+    {
+        PVCITREENODEINT pNodeInt = (PVCITREENODEINT)pNodeCur;
+
+        Assert(pNodeCur->u8Type == VCI_TREE_NODE_TYPE_INTERNAL);
+
+        /* Search for the correct internal node. */
+        unsigned idxMin = 0;
+        unsigned idxMax = pNodeInt->cUsedNodes;
+        unsigned idxCur = pNodeInt->cUsedNodes / 2;
+
+        while (idxMin < idxMax)
+        {
+            PVCINODEINTERNAL pInt = &pNodeInt->aIntNodes[idxCur];
+
+            /* Determine the search direction. */
+            if (offBlockOffset < pInt->u64BlockOffset)
+            {
+                /* Search left from the current extent. */
+                idxMax = idxCur;
+            }
+            else if (offBlockOffset >= pInt->u64BlockOffset + pInt->u32Blocks)
+            {
+                /* Search right from the current extent. */
+                idxMin = idxCur;
+            }
+            else
+            {
+                /* The block lies in the node, stop searching. */
+                if (pInt->PtrChild.fInMemory)
+                    pNodeCur = pInt->PtrChild.u.pNode;
+                else
+                {
+                    PVCITREENODE pNodeNew;
+                    VciTreeNode NodeTree;
+
+                    /* Read from disk and add to the tree. */
+                    rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pInt->PtrChild.u.offAddrBlockNode),
+                                         &NodeTree, sizeof(NodeTree));
+                    AssertRC(rc);
+
+                    pNodeNew = vciTreeNodeImage2Host(pInt->PtrChild.u.offAddrBlockNode, &NodeTree);
+                    if (pNodeNew)
+                    {
+                        /* Link to the parent. */
+                        pInt->PtrChild.fInMemory = true;
+                        pInt->PtrChild.u.pNode = pNodeNew;
+                        pNodeNew->pParent = pNodeCur;
+                        pNodeCur = pNodeNew;
+                    }
+                    else
+                        rc = VERR_NO_MEMORY;
+                }
+                break;
+            }
+
+            idxCur = idxMin + (idxMax - idxMin) / 2;
+        }
+    }
+
+    if (   RT_SUCCESS(rc)
+        && pNodeCur)
+    {
+        PVCITREENODELEAF pLeaf = (PVCITREENODELEAF)pNodeCur;
+        Assert(pNodeCur->u8Type == VCI_TREE_NODE_TYPE_LEAF);
+
+        /* Search the range. */
+        unsigned idxMin = 0;
+        unsigned idxMax = pLeaf->cUsedNodes;
+        unsigned idxCur = pLeaf->cUsedNodes / 2;
+
+        while (idxMin < idxMax)
+        {
+            PVCICACHEEXTENT pExtentCur = &pLeaf->aExtents[idxCur];
+
+            /* Determine the search direction. */
+            if (offBlockOffset < pExtentCur->u64BlockOffset)
+            {
+                /* Search left from the current extent. */
+                idxMax = idxCur;
+            }
+            else if (offBlockOffset >= pExtentCur->u64BlockOffset + pExtentCur->u32Blocks)
+            {
+                /* Search right from the current extent. */
+                idxMin = idxCur;
+            }
+            else
+            {
+                /* We found the extent, stop searching. */
+                pExtent = pExtentCur;
+                break;
+            }
+
+            idxCur = idxMin + (idxMax - idxMin) / 2;
+        }
+    }
+
+    return pExtent;
+}
+
+/**
+ * Internal: Open an image, constructing all necessary data structures.
+ */
+static int vciOpenImage(PVCICACHE pCache, unsigned uOpenFlags)
+{
+    VciHdr Hdr;
+    uint64_t cbFile;
+    int rc;
+
+    pCache->uOpenFlags = uOpenFlags;
+
+    pCache->pInterfaceError = VDInterfaceGet(pCache->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pCache->pInterfaceError)
+        pCache->pInterfaceErrorCallbacks = VDGetInterfaceError(pCache->pInterfaceError);
+
+    /* Get I/O interface. */
+    pCache->pInterfaceIO = VDInterfaceGet(pCache->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pCache->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pCache->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pCache->pInterfaceIO);
+    AssertPtrReturn(pCache->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the image.
+     */
+    rc = vciFileOpen(pCache, pCache->pszFilename,
+                     VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                false /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        goto out;
+    }
+
+    rc = vciFileGetSize(pCache, &cbFile);
+    if (RT_FAILURE(rc) || cbFile < sizeof(VciHdr))
+    {
+        rc = VERR_VD_GEN_INVALID_HEADER;
+        goto out;
+    }
+
+    rc = vciFileReadSync(pCache, 0, &Hdr, sizeof(Hdr));
+    if (RT_FAILURE(rc))
+    {
+        rc = VERR_VD_GEN_INVALID_HEADER;
+        goto out;
+    }
+
+    Hdr.u32Signature = RT_LE2H_U32(Hdr.u32Signature);
+    Hdr.u32Version   = RT_LE2H_U32(Hdr.u32Version);
+    Hdr.cBlocksCache = RT_LE2H_U64(Hdr.cBlocksCache);
+    Hdr.u32CacheType = RT_LE2H_U32(Hdr.u32CacheType);
+    Hdr.offTreeRoot  = RT_LE2H_U64(Hdr.offTreeRoot);
+    Hdr.offBlkMap    = RT_LE2H_U64(Hdr.offBlkMap);
+    Hdr.cBlkMap      = RT_LE2H_U64(Hdr.cBlkMap);
+
+    if (   Hdr.u32Signature == VCI_HDR_SIGNATURE
+        && Hdr.u32Version == VCI_HDR_VERSION)
+    {
+        pCache->offTreeRoot   = Hdr.offTreeRoot;
+        pCache->offBlksBitmap = Hdr.offBlkMap;
+
+        /* Load the block map. */
+        rc = vciBlkMapLoad(pCache, VCI_BLOCK2BYTE(pCache->offBlksBitmap), Hdr.cBlkMap, &pCache->pBlkMap);
+        if (RT_SUCCESS(rc))
+        {
+            /* Load the first tree node. */
+            VciTreeNode RootNode;
+
+            rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pCache->offTreeRoot), &RootNode, sizeof(VciTreeNode));
+            if (RT_SUCCESS(rc))
+            {
+                pCache->pRoot = vciTreeNodeImage2Host(pCache->offTreeRoot, &RootNode);
+                if (!pCache->pRoot)
+                    rc = VERR_NO_MEMORY;
+            }
+        }
+    }
+    else
+        rc = VERR_VD_GEN_INVALID_HEADER;
+
+out:
+    if (RT_FAILURE(rc))
+        vciFreeImage(pCache, false);
+    return rc;
+}
+
+/**
+ * Internal: Create a vci image.
+ */
+static int vciCreateImage(PVCICACHE pCache, uint64_t cbSize,
+                          unsigned uImageFlags, const char *pszComment,
+                          unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
+                          void *pvUser, unsigned uPercentStart,
+                          unsigned uPercentSpan)
+{
+    VciHdr Hdr;
+    VciTreeNode NodeRoot;
+    int rc;
+    uint64_t cBlocks = cbSize / VCI_BLOCK_SIZE; /* Size of the cache in blocks. */
+
+    if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
+    {
+        rc = vciError(pCache, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("VCI: cannot create diff image '%s'"), pCache->pszFilename);
+        return rc;
+    }
+
+    pCache->uImageFlags = uImageFlags;
+
+    pCache->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
+
+    pCache->pInterfaceError = VDInterfaceGet(pCache->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pCache->pInterfaceError)
+        pCache->pInterfaceErrorCallbacks = VDGetInterfaceError(pCache->pInterfaceError);
+
+    pCache->pInterfaceIO = VDInterfaceGet(pCache->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pCache->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pCache->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pCache->pInterfaceIO);
+    AssertPtrReturn(pCache->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    do
+    {
+        /* Create image file. */
+        rc = vciFileOpen(pCache, pCache->pszFilename,
+                         VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                                    true /* fCreate */));
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot create image '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        /* Allocate block bitmap. */
+        uint32_t cBlkMap = 0;
+        rc = vciBlkMapCreate(cBlocks, &pCache->pBlkMap, &cBlkMap);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot create block bitmap '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        /*
+         * Allocate space for the header in the block bitmap.
+         * Because the block map is empty the header has to start at block 0
+         */
+        uint64_t offHdr = 0;
+        rc = vciBlkMapAllocate(pCache->pBlkMap, VCI_BYTE2BLOCK(sizeof(VciHdr)), &offHdr);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for header in block bitmap '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        Assert(offHdr == 0);
+
+        /*
+         * Allocate space for the block map itself.
+         */
+        uint64_t offBlkMap = 0;
+        rc = vciBlkMapAllocate(pCache->pBlkMap, cBlkMap, &offBlkMap);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        /*
+         * Allocate space for the tree root node.
+         */
+        uint64_t offTreeRoot = 0;
+        rc = vciBlkMapAllocate(pCache->pBlkMap, VCI_BYTE2BLOCK(sizeof(VciTreeNode)), &offTreeRoot);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate space for block map in block map '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        /*
+         * Allocate the in memory root node.
+         */
+        pCache->pRoot = (PVCITREENODE)RTMemAllocZ(sizeof(VCITREENODELEAF));
+        if (!pCache->pRoot)
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot allocate B+-Tree root pointer '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        pCache->pRoot->u8Type = VCI_TREE_NODE_TYPE_LEAF;
+        /* Rest remains 0 as the tree is still empty. */
+
+        /*
+         * Now that we are here we have all the basic structures and know where to place them in the image.
+         * It's time to write it now.
+         */
+
+        /* Setup the header. */
+        memset(&Hdr, 0, sizeof(VciHdr));
+        Hdr.u32Signature     = RT_H2LE_U32(VCI_HDR_SIGNATURE);
+        Hdr.u32Version       = RT_H2LE_U32(VCI_HDR_VERSION);
+        Hdr.cBlocksCache     = RT_H2LE_U64(cBlocks);
+        Hdr.fUncleanShutdown = VCI_HDR_UNCLEAN_SHUTDOWN;
+        Hdr.u32CacheType     = uImageFlags & VD_IMAGE_FLAGS_FIXED
+                               ? RT_H2LE_U32(VCI_HDR_CACHE_TYPE_FIXED)
+                               : RT_H2LE_U32(VCI_HDR_CACHE_TYPE_DYNAMIC);
+        Hdr.offTreeRoot      = RT_H2LE_U64(offTreeRoot);
+        Hdr.offBlkMap        = RT_H2LE_U64(offBlkMap);
+        Hdr.cBlkMap          = RT_H2LE_U64(cBlkMap);
+
+        rc = vciFileWriteSync(pCache, offHdr, &Hdr, VCI_BYTE2BLOCK(sizeof(VciHdr)));
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write header '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        rc = vciBlkMapSave(pCache->pBlkMap, pCache, offBlkMap, cBlkMap);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write block map '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        /* Setup the root tree. */
+        memset(&NodeRoot, 0, sizeof(VciTreeNode));
+        NodeRoot.u8Type = RT_H2LE_U32(VCI_TREE_NODE_TYPE_LEAF);
+
+        rc = vciFileWriteSync(pCache, offTreeRoot, &NodeRoot, VCI_BYTE2BLOCK(sizeof(VciTreeNode)));
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot write root node '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        rc = vciFlushImage(pCache);
+        if (RT_FAILURE(rc))
+        {
+            rc = vciError(pCache, rc, RT_SRC_POS, N_("VCI: cannot flush '%s'"), pCache->pszFilename);
+            break;
+        }
+
+        pCache->cbSize = cbSize;
+
+    } while (0);
+
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan);
+
+    if (RT_FAILURE(rc))
+        vciFreeImage(pCache, rc != VERR_ALREADY_EXISTS);
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnProbe */
+static int vciProbe(const char *pszFilename, PVDINTERFACE pVDIfsCache,
+                    PVDINTERFACE pVDIfsImage)
+{
+    VciHdr Hdr;
+    PVDIOSTORAGE pStorage;
+    uint64_t cbFile;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
+
+    /* Get I/O interface. */
+    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
+    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
+                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
+                                                                   false /* fCreate */),
+                                        &pStorage);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage, &cbFile);
+    if (RT_FAILURE(rc) || cbFile < sizeof(VciHdr))
+    {
+        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+        rc = VERR_VD_GEN_INVALID_HEADER;
+        goto out;
+    }
+
+    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage, 0, &Hdr, sizeof(Hdr), NULL);
+    if (RT_FAILURE(rc))
+    {
+        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+        rc = VERR_VD_GEN_INVALID_HEADER;
+        goto out;
+    }
+
+    Hdr.u32Signature = RT_LE2H_U32(Hdr.u32Signature);
+    Hdr.u32Version   = RT_LE2H_U32(Hdr.u32Version);
+    Hdr.cBlocksCache = RT_LE2H_U64(Hdr.cBlocksCache);
+    Hdr.u32CacheType = RT_LE2H_U32(Hdr.u32CacheType);
+    Hdr.offTreeRoot  = RT_LE2H_U64(Hdr.offTreeRoot);
+    Hdr.offBlkMap    = RT_LE2H_U64(Hdr.offBlkMap);
+    Hdr.cBlkMap      = RT_LE2H_U64(Hdr.cBlkMap);
+
+    if (   Hdr.u32Signature == VCI_HDR_SIGNATURE
+        && Hdr.u32Version == VCI_HDR_VERSION)
+        rc = VINF_SUCCESS;
+    else
+        rc = VERR_VD_GEN_INVALID_HEADER;
+
+    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnOpen */
+static int vciOpen(const char *pszFilename, unsigned uOpenFlags,
+                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                   void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PVCICACHE pCache;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+
+    pCache = (PVCICACHE)RTMemAllocZ(sizeof(VCICACHE));
+    if (!pCache)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pCache->pszFilename = pszFilename;
+    pCache->pStorage = NULL;
+    pCache->pVDIfsDisk = pVDIfsDisk;
+    pCache->pVDIfsImage = pVDIfsImage;
+
+    rc = vciOpenImage(pCache, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pCache;
+    else
+        RTMemFree(pCache);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnCreate */
+static int vciCreate(const char *pszFilename, uint64_t cbSize,
+                     unsigned uImageFlags, const char *pszComment,
+                     PCRTUUID pUuid, unsigned uOpenFlags,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p",
+                 pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc;
+    PVCICACHE pCache;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pCache = (PVCICACHE)RTMemAllocZ(sizeof(VCICACHE));
+    if (!pCache)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pCache->pszFilename = pszFilename;
+    pCache->pStorage = NULL;
+    pCache->pVDIfsDisk = pVDIfsDisk;
+    pCache->pVDIfsImage = pVDIfsImage;
+
+    rc = vciCreateImage(pCache, cbSize, uImageFlags, pszComment, uOpenFlags,
+                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
+    if (RT_SUCCESS(rc))
+    {
+        /* So far the image is opened in read/write mode. Make sure the
+         * image is opened in read-only mode if the caller requested that. */
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            vciFreeImage(pCache, false);
+            rc = vciOpenImage(pCache, uOpenFlags);
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pCache);
+                goto out;
+            }
+        }
+        *ppBackendData = pCache;
+    }
+    else
+        RTMemFree(pCache);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnClose */
+static int vciClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    rc = vciFreeImage(pCache, fDelete);
+    RTMemFree(pCache);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnRead */
+static int vciRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                   size_t cbToRead, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc = VINF_SUCCESS;
+    PVCICACHEEXTENT pExtent;
+
+    AssertPtr(pCache);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToRead % 512 == 0);
+
+
+    pExtent = vciCacheExtentLookup(pCache, VCI_BYTE2BLOCK(uOffset));
+    if (pExtent)
+    {
+        uint64_t offRead = uOffset - VCI_BLOCK2BYTE(pExtent->u64BlockOffset);
+        cbToRead = RT_MIN(cbToRead, VCI_BLOCK2BYTE(pExtent->u32Blocks) - offRead);
+
+        rc = vciFileReadSync(pCache, VCI_BLOCK2BYTE(pExtent->u64BlockAddr) + offRead,
+                             pvBuf, cbToRead);
+    }
+    else
+    {
+        /** @todo Best fit to check whether we have cached data later and set
+         * pcbActuallyRead accordingly. */
+        rc = VERR_VD_BLOCK_FREE;
+    }
+
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbToRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnWrite */
+static int vciWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                    size_t cbToWrite, size_t *pcbWriteProcess)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
+                 pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pCache);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnFlush */
+static int vciFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    rc = vciFlushImage(pCache);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetVersion */
+static unsigned vciGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        return 1;
+    else
+        return 0;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetSize */
+static uint64_t vciGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pCache);
+
+    if (pCache && pCache->pStorage)
+        cb = pCache->cbSize;
+
+    LogFlowFunc(("returns %llu\n", cb));
+    return cb;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetFileSize */
+static uint64_t vciGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+    {
+        uint64_t cbFile;
+        if (pCache->pStorage)
+        {
+            int rc = vciFileGetSize(pCache, &cbFile);
+            if (RT_SUCCESS(rc))
+                cb = cbFile;
+        }
+    }
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetImageFlags */
+static unsigned vciGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        uImageFlags = pCache->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetOpenFlags */
+static unsigned vciGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        uOpenFlags = pCache->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnSetOpenFlags */
+static int vciSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. Just readonly and
+     * info flags are supported. */
+    if (!pCache || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    rc = vciFreeImage(pCache, false);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vciOpenImage(pCache, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetComment */
+static int vciGetComment(void *pBackendData, char *pszComment,
+                          size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnSetComment */
+static int vciSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+    {
+        if (pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetUuid */
+static int vciGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnSetUuid */
+static int vciSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    AssertPtr(pCache);
+
+    if (pCache)
+    {
+        if (!(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnGetModificationUuid */
+static int vciGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnSetModificationUuid */
+static int vciSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+    int rc;
+
+    AssertPtr(pCache);
+
+    if (pCache)
+    {
+        if (!(pCache->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnDump */
+static void vciDump(void *pBackendData)
+{
+    NOREF(pBackendData);
+}
+
+/** @copydoc VDCACHEBACKEND::pfnAsyncRead */
+static int vciAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    int rc = VERR_NOT_SUPPORTED;
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnAsyncWrite */
+static int vciAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                         PVDIOCTX pIoCtx, size_t *pcbWriteProcess)
+{
+    int rc = VERR_NOT_SUPPORTED;
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+
+    return rc;
+}
+
+/** @copydoc VDCACHEBACKEND::pfnAsyncFlush */
+static int vciAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    int rc = VERR_NOT_SUPPORTED;
+    PVCICACHE pCache = (PVCICACHE)pBackendData;
+
+    return rc;
+}
+
+
+VDCACHEBACKEND g_VciCacheBackend =
+{
+    /* pszBackendName */
+    "vci",
+    /* cbSize */
+    sizeof(VDCACHEBACKEND),
+    /* uBackendCaps */
+    VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC | VD_CAP_FILE | VD_CAP_VFS,
+    /* papszFileExtensions */
+    s_apszVciFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnProbe */
+    vciProbe,
+    /* pfnOpen */
+    vciOpen,
+    /* pfnCreate */
+    vciCreate,
+    /* pfnClose */
+    vciClose,
+    /* pfnRead */
+    vciRead,
+    /* pfnWrite */
+    vciWrite,
+    /* pfnFlush */
+    vciFlush,
+    /* pfnGetVersion */
+    vciGetVersion,
+    /* pfnGetSize */
+    vciGetSize,
+    /* pfnGetFileSize */
+    vciGetFileSize,
+    /* pfnGetImageFlags */
+    vciGetImageFlags,
+    /* pfnGetOpenFlags */
+    vciGetOpenFlags,
+    /* pfnSetOpenFlags */
+    vciSetOpenFlags,
+    /* pfnGetComment */
+    vciGetComment,
+    /* pfnSetComment */
+    vciSetComment,
+    /* pfnGetUuid */
+    vciGetUuid,
+    /* pfnSetUuid */
+    vciSetUuid,
+    /* pfnGetModificationUuid */
+    vciGetModificationUuid,
+    /* pfnSetModificationUuid */
+    vciSetModificationUuid,
+    /* pfnDump */
+    vciDump,
+    /* pfnAsyncRead */
+    vciAsyncRead,
+    /* pfnAsyncWrite */
+    vciAsyncWrite,
+    /* pfnAsyncFlush */
+    vciAsyncFlush,
+    /* pfnComposeLocation */
+    NULL,
+    /* pfnComposeName */
+    NULL
+};
+
Index: /trunk/src/VBox/Storage/VD.cpp
===================================================================
--- /trunk/src/VBox/Storage/VD.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/VD.cpp	(revision 33567)
@@ -0,0 +1,8130 @@
+/* $Id$ */
+/** @file
+ * VBoxHDD - VBox HDD Container implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD
+#include <VBox/vd.h>
+#include <VBox/err.h>
+#include <VBox/sup.h>
+#include <VBox/log.h>
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/ldr.h>
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/memcache.h>
+#include <iprt/sg.h>
+#include <iprt/critsect.h>
+#include <iprt/list.h>
+#include <iprt/avl.h>
+
+#include <VBox/vd-plugin.h>
+#include <VBox/vd-cache-plugin.h>
+
+#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
+
+/** Buffer size used for merging images. */
+#define VD_MERGE_BUFFER_SIZE    (16 * _1M)
+
+/** Maximum number of segments in one I/O task. */
+#define VD_IO_TASK_SEGMENTS_MAX 64
+
+/**
+ * VD async I/O interface storage descriptor.
+ */
+typedef struct VDIIOFALLBACKSTORAGE
+{
+    /** File handle. */
+    RTFILE         File;
+    /** Completion callback. */
+    PFNVDCOMPLETED pfnCompleted;
+    /** Thread for async access. */
+    RTTHREAD       ThreadAsync;
+} VDIIOFALLBACKSTORAGE, *PVDIIOFALLBACKSTORAGE;
+
+/**
+ * VBox HDD Container image descriptor.
+ */
+typedef struct VDIMAGE
+{
+    /** Link to parent image descriptor, if any. */
+    struct VDIMAGE  *pPrev;
+    /** Link to child image descriptor, if any. */
+    struct VDIMAGE  *pNext;
+    /** Container base filename. (UTF-8) */
+    char            *pszFilename;
+    /** Data managed by the backend which keeps the actual info. */
+    void            *pBackendData;
+    /** Cached sanitized image flags. */
+    unsigned        uImageFlags;
+    /** Image open flags (only those handled generically in this code and which
+     * the backends will never ever see). */
+    unsigned        uOpenFlags;
+
+    /** Function pointers for the various backend methods. */
+    PCVBOXHDDBACKEND    Backend;
+
+    /** Pointer to list of VD interfaces, per-image. */
+    PVDINTERFACE        pVDIfsImage;
+
+    /** I/O interface to the upper layer. */
+    PVDINTERFACE        pInterfaceIO;
+    /** I/O interface callback table. */
+    PVDINTERFACEIO      pInterfaceIOCallbacks;
+
+    /** Per image internal I/O interface. */
+    VDINTERFACE         VDIIOInt;
+
+    /** Fallback I/O interface, only used if the caller doesn't provide it. */
+    VDINTERFACE         VDIIO;
+
+    /** Disk this image is part of */
+    PVBOXHDD            pDisk;
+} VDIMAGE, *PVDIMAGE;
+
+/**
+ * uModified bit flags.
+ */
+#define VD_IMAGE_MODIFIED_FLAG                  RT_BIT(0)
+#define VD_IMAGE_MODIFIED_FIRST                 RT_BIT(1)
+#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE   RT_BIT(2)
+
+
+/**
+ * VBox HDD Cache image descriptor.
+ */
+typedef struct VDCACHE
+{
+    /** Cache base filename. (UTF-8) */
+    char            *pszFilename;
+    /** Data managed by the backend which keeps the actual info. */
+    void            *pBackendData;
+    /** Cached sanitized image flags. */
+    unsigned        uImageFlags;
+    /** Image open flags (only those handled generically in this code and which
+     * the backends will never ever see). */
+    unsigned        uOpenFlags;
+
+    /** Function pointers for the various backend methods. */
+    PCVDCACHEBACKEND    Backend;
+
+    /** Pointer to list of VD interfaces, per-cache. */
+    PVDINTERFACE        pVDIfsCache;
+
+    /** I/O interface to the upper layer. */
+    PVDINTERFACE        pInterfaceIO;
+    /** I/O interface callback table. */
+    PVDINTERFACEIO      pInterfaceIOCallbacks;
+
+    /** Per image internal I/O interface. */
+    VDINTERFACE         VDIIOInt;
+
+    /** Fallback I/O interface, only used if the caller doesn't provide it. */
+    VDINTERFACE         VDIIO;
+
+    /** Disk this image is part of */
+    PVBOXHDD            pDisk;
+} VDCACHE, *PVDCACHE;
+
+/**
+ * VBox HDD Container main structure, private part.
+ */
+struct VBOXHDD
+{
+    /** Structure signature (VBOXHDDDISK_SIGNATURE). */
+    uint32_t            u32Signature;
+
+    /** Image type. */
+    VDTYPE              enmType;
+
+    /** Number of opened images. */
+    unsigned            cImages;
+
+    /** Base image. */
+    PVDIMAGE            pBase;
+
+    /** Last opened image in the chain.
+     * The same as pBase if only one image is used. */
+    PVDIMAGE            pLast;
+
+    /** If a merge to one of the parents is running this may be non-NULL
+     * to indicate to what image the writes should be additionally relayed. */
+    PVDIMAGE            pImageRelay;
+
+    /** Flags representing the modification state. */
+    unsigned            uModified;
+
+    /** Cached size of this disk. */
+    uint64_t            cbSize;
+    /** Cached PCHS geometry for this disk. */
+    VDGEOMETRY          PCHSGeometry;
+    /** Cached LCHS geometry for this disk. */
+    VDGEOMETRY          LCHSGeometry;
+
+    /** Pointer to list of VD interfaces, per-disk. */
+    PVDINTERFACE        pVDIfsDisk;
+    /** Pointer to the common interface structure for error reporting. */
+    PVDINTERFACE        pInterfaceError;
+    /** Pointer to the error interface callbacks we use if available. */
+    PVDINTERFACEERROR   pInterfaceErrorCallbacks;
+
+    /** Pointer to the optional thread synchronization interface. */
+    PVDINTERFACE        pInterfaceThreadSync;
+    /** Pointer to the optional thread synchronization callbacks. */
+    PVDINTERFACETHREADSYNC pInterfaceThreadSyncCallbacks;
+
+    /** Internal I/O interface callback table for the images. */
+    VDINTERFACEIOINT    VDIIOIntCallbacks;
+
+    /** Callback table for the fallback I/O interface. */
+    VDINTERFACEIO       VDIIOCallbacks;
+
+    /** Memory cache for I/O contexts */
+    RTMEMCACHE          hMemCacheIoCtx;
+    /** Memory cache for I/O tasks. */
+    RTMEMCACHE          hMemCacheIoTask;
+    /** Critical section protecting the disk against concurrent access. */
+    RTCRITSECT          CritSect;
+    /** Flag whether the disk is currently locked by growing write or a flush
+     * request. Other flush or growing write requests need to wait until
+     * the current one completes.
+     */
+    volatile bool       fLocked;
+    /** List of waiting requests. - Protected by the critical section. */
+    RTLISTNODE          ListWriteLocked;
+
+    /** Pointer to the L2 disk cache if any. */
+    PVDCACHE            pCache;
+};
+
+# define VD_THREAD_IS_CRITSECT_OWNER(Disk) \
+    do \
+    { \
+        AssertMsg(RTCritSectIsOwner(&Disk->CritSect), \
+                  ("Thread does not own critical section\n"));\
+    } while(0)
+
+/**
+ * VBox parent read descriptor, used internally for compaction.
+ */
+typedef struct VDPARENTSTATEDESC
+{
+    /** Pointer to disk descriptor. */
+    PVBOXHDD pDisk;
+    /** Pointer to image descriptor. */
+    PVDIMAGE pImage;
+} VDPARENTSTATEDESC, *PVDPARENTSTATEDESC;
+
+/**
+ * Transfer direction.
+ */
+typedef enum VDIOCTXTXDIR
+{
+    /** Read */
+    VDIOCTXTXDIR_READ = 0,
+    /** Write */
+    VDIOCTXTXDIR_WRITE,
+    /** Flush */
+    VDIOCTXTXDIR_FLUSH,
+    /** 32bit hack */
+    VDIOCTXTXDIR_32BIT_HACK = 0x7fffffff
+} VDIOCTXTXDIR, *PVDIOCTXTXDIR;
+
+/** Transfer function */
+typedef DECLCALLBACK(int) FNVDIOCTXTRANSFER (PVDIOCTX pIoCtx);
+/** Pointer to a transfer function. */
+typedef FNVDIOCTXTRANSFER *PFNVDIOCTXTRANSFER;
+
+/**
+ * I/O context
+ */
+typedef struct VDIOCTX
+{
+    /** Disk this is request is for. */
+    PVBOXHDD                     pDisk;
+    /** Return code. */
+    int                          rcReq;
+    /** Transfer direction */
+    VDIOCTXTXDIR                 enmTxDir;
+    /** Number of bytes left until this context completes. */
+    volatile uint32_t            cbTransferLeft;
+    /** Current offset */
+    volatile uint64_t            uOffset;
+    /** Number of bytes to transfer */
+    volatile size_t              cbTransfer;
+    /** Current image in the chain. */
+    PVDIMAGE                     pImage;
+    /** S/G buffer */
+    RTSGBUF                      SgBuf;
+    /** Flag whether the I/O context is blocked because it is in the growing list. */
+    bool                         fBlocked;
+    /** Number of data transfers currently pending. */
+    volatile uint32_t            cDataTransfersPending;
+    /** How many meta data transfers are pending. */
+    volatile uint32_t            cMetaTransfersPending;
+    /** Flag whether the request finished */
+    volatile bool                fComplete;
+    /** Temporary allocated memory which is freed
+     * when the context completes. */
+    void                        *pvAllocation;
+    /** Transfer function. */
+    PFNVDIOCTXTRANSFER           pfnIoCtxTransfer;
+    /** Next transfer part after the current one completed. */
+    PFNVDIOCTXTRANSFER           pfnIoCtxTransferNext;
+    /** Parent I/O context if any. Sets the type of the context (root/child) */
+    PVDIOCTX                     pIoCtxParent;
+    /** Type dependent data (root/child) */
+    union
+    {
+        /** Root data */
+        struct
+        {
+            /** Completion callback */
+            PFNVDASYNCTRANSFERCOMPLETE   pfnComplete;
+            /** User argument 1 passed on completion. */
+            void                        *pvUser1;
+            /** User argument 1 passed on completion. */
+            void                        *pvUser2;
+        } Root;
+        /** Child data */
+        struct
+        {
+            /** Saved start offset */
+            uint64_t                     uOffsetSaved;
+            /** Saved transfer size */
+            size_t                       cbTransferLeftSaved;
+            /** Number of bytes transferred from the parent if this context completes. */
+            size_t                       cbTransferParent;
+            /** Number of bytes to pre read */
+            size_t                       cbPreRead;
+            /** Number of bytes to post read. */
+            size_t                       cbPostRead;
+            /** Number of bytes to write left in the parent. */
+            size_t                       cbWriteParent;
+            /** Write type dependent data. */
+            union
+            {
+                /** Optimized */
+                struct
+                {
+                    /** Bytes to fill to satisfy the block size. Not part of the virtual disk. */
+                    size_t               cbFill;
+                    /** Bytes to copy instead of reading from the parent */
+                    size_t               cbWriteCopy;
+                    /** Bytes to read from the image. */
+                    size_t               cbReadImage;
+                } Optimized;
+            } Write;
+        } Child;
+    } Type;
+} VDIOCTX;
+
+typedef struct VDIOCTXDEFERRED
+{
+    /** Node in the list of deferred requests.
+     * A request can be deferred if the image is growing
+     * and the request accesses the same range or if
+     * the backend needs to read or write metadata from the disk
+     * before it can continue. */
+    RTLISTNODE NodeDeferred;
+    /** I/O context this entry points to. */
+    PVDIOCTX   pIoCtx;
+} VDIOCTXDEFERRED, *PVDIOCTXDEFERRED;
+
+/**
+ * I/O task.
+ */
+typedef struct VDIOTASK
+{
+    /** Storage this task belongs to. */
+    PVDIOSTORAGE                 pIoStorage;
+    /** Optional completion callback. */
+    PFNVDXFERCOMPLETED           pfnComplete;
+    /** Opaque user data. */
+    void                        *pvUser;
+    /** Flag whether this is a meta data transfer. */
+    bool                         fMeta;
+    /** Type dependent data. */
+    union
+    {
+        /** User data transfer. */
+        struct
+        {
+            /** Number of bytes this task transferred. */
+            uint32_t             cbTransfer;
+            /** Pointer to the I/O context the task belongs. */
+            PVDIOCTX             pIoCtx;
+        } User;
+        /** Meta data transfer. */
+        struct
+        {
+            /** Meta transfer this task is for. */
+            PVDMETAXFER          pMetaXfer;
+        } Meta;
+    } Type;
+} VDIOTASK, *PVDIOTASK;
+
+/**
+ * Storage handle.
+ */
+typedef struct VDIOSTORAGE
+{
+    /** Image this storage handle belongs to. */
+    PVDIMAGE                     pImage;
+    /** AVL tree for pending async metadata transfers. */
+    PAVLRFOFFTREE                pTreeMetaXfers;
+    /** Storage handle */
+    void                        *pStorage;
+} VDIOSTORAGE;
+
+/**
+ *  Metadata transfer.
+ *
+ *  @note This entry can't be freed if either the list is not empty or
+ *  the reference counter is not 0.
+ *  The assumption is that the backends don't need to read huge amounts of
+ *  metadata to complete a transfer so the additional memory overhead should
+ *  be relatively small.
+ */
+typedef struct VDMETAXFER
+{
+    /** AVL core for fast search (the file offset is the key) */
+    AVLRFOFFNODECORE Core;
+    /** I/O storage for this transfer. */
+    PVDIOSTORAGE     pIoStorage;
+    /** Flags. */
+    uint32_t         fFlags;
+    /** List of I/O contexts waiting for this metadata transfer to complete. */
+    RTLISTNODE       ListIoCtxWaiting;
+    /** Number of references to this entry. */
+    unsigned         cRefs;
+    /** Size of the data stored with this entry. */
+    size_t           cbMeta;
+    /** Data stored - variable size. */
+    uint8_t          abData[1];
+} VDMETAXFER;
+
+/**
+ * The transfer direction for the metadata.
+ */
+#define VDMETAXFER_TXDIR_MASK  0x3
+#define VDMETAXFER_TXDIR_NONE  0x0
+#define VDMETAXFER_TXDIR_WRITE 0x1
+#define VDMETAXFER_TXDIR_READ  0x2
+#define VDMETAXFER_TXDIR_FLUSH 0x3
+#define VDMETAXFER_TXDIR_GET(flags)      ((flags) & VDMETAXFER_TXDIR_MASK)
+#define VDMETAXFER_TXDIR_SET(flags, dir) ((flags) = (flags & ~VDMETAXFER_TXDIR_MASK) | (dir))
+
+extern VBOXHDDBACKEND g_RawBackend;
+extern VBOXHDDBACKEND g_VmdkBackend;
+extern VBOXHDDBACKEND g_VDIBackend;
+extern VBOXHDDBACKEND g_VhdBackend;
+extern VBOXHDDBACKEND g_ParallelsBackend;
+extern VBOXHDDBACKEND g_DmgBackend;
+extern VBOXHDDBACKEND g_ISCSIBackend;
+
+static unsigned g_cBackends = 0;
+static PVBOXHDDBACKEND *g_apBackends = NULL;
+static PVBOXHDDBACKEND aStaticBackends[] =
+{
+    &g_VmdkBackend,
+    &g_VDIBackend,
+    &g_VhdBackend,
+    &g_ParallelsBackend,
+    &g_DmgBackend,
+    &g_RawBackend,
+    &g_ISCSIBackend
+};
+
+/**
+ * Supported backends for the disk cache.
+ */
+extern VDCACHEBACKEND g_VciCacheBackend;
+
+static unsigned g_cCacheBackends = 0;
+static PVDCACHEBACKEND *g_apCacheBackends = NULL;
+static PVDCACHEBACKEND aStaticCacheBackends[] =
+{
+    &g_VciCacheBackend
+};
+
+/**
+ * internal: add several backends.
+ */
+static int vdAddBackends(PVBOXHDDBACKEND *ppBackends, unsigned cBackends)
+{
+    PVBOXHDDBACKEND *pTmp = (PVBOXHDDBACKEND*)RTMemRealloc(g_apBackends,
+           (g_cBackends + cBackends) * sizeof(PVBOXHDDBACKEND));
+    if (RT_UNLIKELY(!pTmp))
+        return VERR_NO_MEMORY;
+    g_apBackends = pTmp;
+    memcpy(&g_apBackends[g_cBackends], ppBackends, cBackends * sizeof(PVBOXHDDBACKEND));
+    g_cBackends += cBackends;
+    return VINF_SUCCESS;
+}
+
+/**
+ * internal: add single backend.
+ */
+DECLINLINE(int) vdAddBackend(PVBOXHDDBACKEND pBackend)
+{
+    return vdAddBackends(&pBackend, 1);
+}
+
+/**
+ * internal: add several cache backends.
+ */
+static int vdAddCacheBackends(PVDCACHEBACKEND *ppBackends, unsigned cBackends)
+{
+    PVDCACHEBACKEND *pTmp = (PVDCACHEBACKEND*)RTMemRealloc(g_apCacheBackends,
+           (g_cCacheBackends + cBackends) * sizeof(PVDCACHEBACKEND));
+    if (RT_UNLIKELY(!pTmp))
+        return VERR_NO_MEMORY;
+    g_apCacheBackends = pTmp;
+    memcpy(&g_apCacheBackends[g_cCacheBackends], ppBackends, cBackends * sizeof(PVDCACHEBACKEND));
+    g_cCacheBackends += cBackends;
+    return VINF_SUCCESS;
+}
+
+/**
+ * internal: add single cache backend.
+ */
+DECLINLINE(int) vdAddCacheBackend(PVDCACHEBACKEND pBackend)
+{
+    return vdAddCacheBackends(&pBackend, 1);
+}
+
+/**
+ * internal: issue error message.
+ */
+static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
+                   const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pDisk->pInterfaceErrorCallbacks)
+        pDisk->pInterfaceErrorCallbacks->pfnError(pDisk->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * internal: thread synchronization, start read.
+ */
+DECLINLINE(int) vdThreadStartRead(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
+        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartRead(pDisk->pInterfaceThreadSync->pvUser);
+    return rc;
+}
+
+/**
+ * internal: thread synchronization, finish read.
+ */
+DECLINLINE(int) vdThreadFinishRead(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
+        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishRead(pDisk->pInterfaceThreadSync->pvUser);
+    return rc;
+}
+
+/**
+ * internal: thread synchronization, start write.
+ */
+DECLINLINE(int) vdThreadStartWrite(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
+        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnStartWrite(pDisk->pInterfaceThreadSync->pvUser);
+    return rc;
+}
+
+/**
+ * internal: thread synchronization, finish write.
+ */
+DECLINLINE(int) vdThreadFinishWrite(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    if (RT_UNLIKELY(pDisk->pInterfaceThreadSyncCallbacks))
+        rc = pDisk->pInterfaceThreadSyncCallbacks->pfnFinishWrite(pDisk->pInterfaceThreadSync->pvUser);
+    return rc;
+}
+
+/**
+ * internal: find image format backend.
+ */
+static int vdFindBackend(const char *pszBackend, PCVBOXHDDBACKEND *ppBackend)
+{
+    int rc = VINF_SUCCESS;
+    PCVBOXHDDBACKEND pBackend = NULL;
+
+    if (!g_apBackends)
+        VDInit();
+
+    for (unsigned i = 0; i < g_cBackends; i++)
+    {
+        if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
+        {
+            pBackend = g_apBackends[i];
+            break;
+        }
+    }
+    *ppBackend = pBackend;
+    return rc;
+}
+
+/**
+ * internal: find cache format backend.
+ */
+static int vdFindCacheBackend(const char *pszBackend, PCVDCACHEBACKEND *ppBackend)
+{
+    int rc = VINF_SUCCESS;
+    PCVDCACHEBACKEND pBackend = NULL;
+
+    if (!g_apCacheBackends)
+        VDInit();
+
+    for (unsigned i = 0; i < g_cCacheBackends; i++)
+    {
+        if (!RTStrICmp(pszBackend, g_apCacheBackends[i]->pszBackendName))
+        {
+            pBackend = g_apCacheBackends[i];
+            break;
+        }
+    }
+    *ppBackend = pBackend;
+    return rc;
+}
+
+/**
+ * internal: add image structure to the end of images list.
+ */
+static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE 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 vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE 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--;
+}
+
+/**
+ * internal: find image by index into the images list.
+ */
+static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
+{
+    PVDIMAGE pImage = pDisk->pBase;
+    if (nImage == VD_LAST_IMAGE)
+        return pDisk->pLast;
+    while (pImage && nImage)
+    {
+        pImage = pImage->pNext;
+        nImage--;
+    }
+    return pImage;
+}
+
+/**
+ * Internal: Tries to read the desired range from the given cache.
+ *
+ * @returns VBox status code.
+ * @retval  VERR_VD_BLOCK_FREE if the block is not in the cache.
+ *          pcbRead will be set to the number of bytes not in the cache.
+ *          Everything thereafter might be in the cache.
+ * @param   pCache   The cache to read from.
+ * @param   uOffset  Offset of the virtual disk to read.
+ * @param   pvBuf    Where to store the read data.
+ * @param   cbRead   How much to read.
+ * @param   pcbRead  Where to store the number of bytes actually read.
+ *                   On success this indicates the number of bytes read from the cache.
+ *                   If VERR_VD_BLOCK_FREE is returned this gives the number of bytes
+ *                   which are not in the cache.
+ *                   In both cases everything beyond this value
+ *                   might or might not be in the cache.
+ */
+static int vdCacheReadHelper(PVDCACHE pCache, uint64_t uOffset,
+                             void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbRead=%zu pcbRead=%#p\n",
+                 pCache, uOffset, pvBuf, cbRead, pcbRead));
+
+    AssertPtr(pCache);
+    AssertPtr(pcbRead);
+
+    rc = pCache->Backend->pfnRead(pCache->pBackendData, uOffset, pvBuf,
+                                  cbRead, pcbRead);
+
+    LogFlowFunc(("returns rc=%Rrc pcbRead=%zu\n", rc, *pcbRead));
+    return rc;
+}
+
+/**
+ * Internal: Writes data for the given block into the cache.
+ *
+ * @returns VBox status code.
+ * @param   pCache     The cache to write to.
+ * @param   uOffset    Offset of the virtual disk to write to teh cache.
+ * @param   pcvBuf     The data to write.
+ * @param   cbWrite    How much to write.
+ * @param   pcbWritten How much data could be written, optional.
+ */
+static int vdCacheWriteHelper(PVDCACHE pCache, uint64_t uOffset, const void *pcvBuf,
+                              size_t cbWrite, size_t *pcbWritten)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pCache=%#p uOffset=%llu pvBuf=%#p cbWrite=%zu pcbWritten=%#p\n",
+                 pCache, uOffset, pcvBuf, cbWrite, pcbWritten));
+
+    AssertPtr(pCache);
+    AssertPtr(pcvBuf);
+    Assert(cbWrite > 0);
+
+    if (pcbWritten)
+        rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
+                                       cbWrite, pcbWritten);
+    else
+    {
+        size_t cbWritten = 0;
+
+        do
+        {
+            rc = pCache->Backend->pfnWrite(pCache->pBackendData, uOffset, pcvBuf,
+                                           cbWrite, &cbWritten);
+            uOffset += cbWritten;
+            pcvBuf   = (char *)pcvBuf + cbWritten;
+            cbWrite -= cbWritten;
+        } while (   cbWrite
+                 && RT_SUCCESS(rc));
+    }
+
+    LogFlowFunc(("returns rc=%Rrc pcbWritten=%zu\n",
+                 rc, pcbWritten ? *pcbWritten : cbWrite));
+    return rc;
+}
+
+/**
+ * Internal: Reads a given amount of data from the image chain of the disk.
+ **/
+static int vdDiskReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
+                            uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbThisRead)
+{
+    int rc = VINF_SUCCESS;
+    size_t cbThisRead = cbRead;
+
+    AssertPtr(pcbThisRead);
+
+    *pcbThisRead = 0;
+
+    /*
+     * Try to read from the given image.
+     * If the block is not allocated read from override chain if present.
+     */
+    rc = pImage->Backend->pfnRead(pImage->pBackendData,
+                                  uOffset, pvBuf, cbThisRead,
+                                  &cbThisRead);
+
+    if (rc == VERR_VD_BLOCK_FREE)
+    {
+        for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
+             pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
+             pCurrImage = pCurrImage->pPrev)
+        {
+            rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+                                              uOffset, pvBuf, cbThisRead,
+                                              &cbThisRead);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+        *pcbThisRead = cbThisRead;
+
+    return rc;
+}
+
+/**
+ * internal: read the specified amount of data in whatever blocks the backend
+ * will give us.
+ */
+static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, PVDIMAGE pImageParentOverride,
+                        uint64_t uOffset, void *pvBuf, size_t cbRead,
+                        bool fZeroFreeBlocks, bool fUpdateCache)
+{
+    int rc = VINF_SUCCESS;
+    size_t cbThisRead;
+    bool fAllFree = true;
+    size_t cbBufClear = 0;
+
+    /* Loop until all read. */
+    do
+    {
+        /* Search for image with allocated block. Do not attempt to read more
+         * than the previous reads marked as valid. Otherwise this would return
+         * stale data when different block sizes are used for the images. */
+        cbThisRead = cbRead;
+
+        if (   pDisk->pCache
+            && !pImageParentOverride)
+        {
+            rc = vdCacheReadHelper(pDisk->pCache, uOffset, pvBuf,
+                                   cbThisRead, &cbThisRead);
+
+            if (rc == VERR_VD_BLOCK_FREE)
+            {
+                rc = vdDiskReadHelper(pDisk, pImage, pImageParentOverride,
+                                      uOffset, pvBuf, cbThisRead, &cbThisRead);
+
+                /* If the read was successful, write the data back into the cache. */
+                if (   RT_SUCCESS(rc)
+                    && fUpdateCache)
+                {
+                    rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf,
+                                            cbThisRead, NULL);
+                }
+            }
+        }
+        else
+        {
+            /** @todo can be be replaced by vdDiskReadHelper if it proves to be reliable,
+             * don't want to be responsible for data corruption...
+             */
+            /*
+             * Try to read from the given image.
+             * If the block is not allocated read from override chain if present.
+             */
+            rc = pImage->Backend->pfnRead(pImage->pBackendData,
+                                          uOffset, pvBuf, cbThisRead,
+                                          &cbThisRead);
+
+            if (rc == VERR_VD_BLOCK_FREE)
+            {
+                for (PVDIMAGE pCurrImage = pImageParentOverride ? pImageParentOverride : pImage->pPrev;
+                     pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
+                     pCurrImage = pCurrImage->pPrev)
+                {
+                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+                                                      uOffset, pvBuf, cbThisRead,
+                                                      &cbThisRead);
+                }
+            }
+        }
+
+        /* No image in the chain contains the data for the block. */
+        if (rc == VERR_VD_BLOCK_FREE)
+        {
+            /* Fill the free space with 0 if we are told to do so
+             * or a previous read returned valid data. */
+            if (fZeroFreeBlocks || !fAllFree)
+                memset(pvBuf, '\0', cbThisRead);
+            else
+                cbBufClear += cbThisRead;
+
+            rc = VINF_SUCCESS;
+        }
+        else if (RT_SUCCESS(rc))
+        {
+            /* First not free block, fill the space before with 0. */
+            if (!fZeroFreeBlocks)
+            {
+                memset((char *)pvBuf - cbBufClear, '\0', cbBufClear);
+                cbBufClear = 0;
+                fAllFree = false;
+            }
+        }
+
+        cbRead -= cbThisRead;
+        uOffset += cbThisRead;
+        pvBuf = (char *)pvBuf + cbThisRead;
+    } while (cbRead != 0 && RT_SUCCESS(rc));
+
+    return (!fZeroFreeBlocks && fAllFree) ? VERR_VD_BLOCK_FREE : rc;
+}
+
+DECLINLINE(PVDIOCTX) vdIoCtxAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
+                                  uint64_t uOffset, size_t cbTransfer,
+                                  PCRTSGSEG pcaSeg, unsigned cSeg,
+                                  void *pvAllocation,
+                                  PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+{
+    PVDIOCTX pIoCtx = NULL;
+
+    pIoCtx = (PVDIOCTX)RTMemCacheAlloc(pDisk->hMemCacheIoCtx);
+    if (RT_LIKELY(pIoCtx))
+    {
+        pIoCtx->pDisk                 = pDisk;
+        pIoCtx->enmTxDir              = enmTxDir;
+        pIoCtx->cbTransferLeft        = cbTransfer;
+        pIoCtx->uOffset               = uOffset;
+        pIoCtx->cbTransfer            = cbTransfer;
+        pIoCtx->cDataTransfersPending = 0;
+        pIoCtx->cMetaTransfersPending = 0;
+        pIoCtx->fComplete             = false;
+        pIoCtx->fBlocked              = false;
+        pIoCtx->pvAllocation          = pvAllocation;
+        pIoCtx->pfnIoCtxTransfer      = pfnIoCtxTransfer;
+        pIoCtx->pfnIoCtxTransferNext  = NULL;
+        pIoCtx->rcReq                 = VINF_SUCCESS;
+
+        /* There is no S/G list for a flush request. */
+        if (enmTxDir != VDIOCTXTXDIR_FLUSH)
+            RTSgBufInit(&pIoCtx->SgBuf, pcaSeg, cSeg);
+        else
+            memset(&pIoCtx->SgBuf, 0, sizeof(RTSGBUF));
+    }
+
+    return pIoCtx;
+}
+
+DECLINLINE(PVDIOCTX) vdIoCtxRootAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
+                                      uint64_t uOffset, size_t cbTransfer,
+                                      PCRTSGSEG paSeg, unsigned cSeg,
+                                      PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                                      void *pvUser1, void *pvUser2,
+                                      void *pvAllocation,
+                                      PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+{
+    PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
+                                   paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
+
+    if (RT_LIKELY(pIoCtx))
+    {
+        pIoCtx->pIoCtxParent          = NULL;
+        pIoCtx->Type.Root.pfnComplete = pfnComplete;
+        pIoCtx->Type.Root.pvUser1     = pvUser1;
+        pIoCtx->Type.Root.pvUser2     = pvUser2;
+    }
+
+    LogFlow(("Allocated root I/O context %#p\n", pIoCtx));
+    return pIoCtx;
+}
+
+DECLINLINE(PVDIOCTX) vdIoCtxChildAlloc(PVBOXHDD pDisk, VDIOCTXTXDIR enmTxDir,
+                                       uint64_t uOffset, size_t cbTransfer,
+                                       PCRTSGSEG paSeg, unsigned cSeg,
+                                       PVDIOCTX pIoCtxParent, size_t cbTransferParent,
+                                       size_t cbWriteParent, void *pvAllocation,
+                                       PFNVDIOCTXTRANSFER pfnIoCtxTransfer)
+{
+    PVDIOCTX pIoCtx = vdIoCtxAlloc(pDisk, enmTxDir, uOffset, cbTransfer,
+                                   paSeg, cSeg, pvAllocation, pfnIoCtxTransfer);
+
+    AssertPtr(pIoCtxParent);
+    Assert(!pIoCtxParent->pIoCtxParent);
+
+    if (RT_LIKELY(pIoCtx))
+    {
+        pIoCtx->pIoCtxParent                   = pIoCtxParent;
+        pIoCtx->Type.Child.uOffsetSaved        = uOffset;
+        pIoCtx->Type.Child.cbTransferLeftSaved = cbTransfer;
+        pIoCtx->Type.Child.cbTransferParent    = cbTransferParent;
+        pIoCtx->Type.Child.cbWriteParent       = cbWriteParent;
+    }
+
+    LogFlow(("Allocated child I/O context %#p\n", pIoCtx));
+    return pIoCtx;
+}
+
+DECLINLINE(PVDIOTASK) vdIoTaskUserAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDIOCTX pIoCtx, uint32_t cbTransfer)
+{
+    PVDIOTASK pIoTask = NULL;
+
+    pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
+    if (pIoTask)
+    {
+        pIoTask->pIoStorage           = pIoStorage;
+        pIoTask->pfnComplete          = pfnComplete;
+        pIoTask->pvUser               = pvUser;
+        pIoTask->fMeta                = false;
+        pIoTask->Type.User.cbTransfer = cbTransfer;
+        pIoTask->Type.User.pIoCtx     = pIoCtx;
+    }
+
+    return pIoTask;
+}
+
+DECLINLINE(PVDIOTASK) vdIoTaskMetaAlloc(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser, PVDMETAXFER pMetaXfer)
+{
+    PVDIOTASK pIoTask = NULL;
+
+    pIoTask = (PVDIOTASK)RTMemCacheAlloc(pIoStorage->pImage->pDisk->hMemCacheIoTask);
+    if (pIoTask)
+    {
+        pIoTask->pIoStorage          = pIoStorage;
+        pIoTask->pfnComplete         = pfnComplete;
+        pIoTask->pvUser              = pvUser;
+        pIoTask->fMeta               = true;
+        pIoTask->Type.Meta.pMetaXfer = pMetaXfer;
+    }
+
+    return pIoTask;
+}
+
+DECLINLINE(void) vdIoCtxFree(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+{
+    LogFlow(("Freeing I/O context %#p\n", pIoCtx));
+    if (pIoCtx->pvAllocation)
+        RTMemFree(pIoCtx->pvAllocation);
+#ifdef DEBUG
+    memset(pIoCtx, 0xff, sizeof(VDIOCTX));
+#endif
+    RTMemCacheFree(pDisk->hMemCacheIoCtx, pIoCtx);
+}
+
+DECLINLINE(void) vdIoTaskFree(PVBOXHDD pDisk, PVDIOTASK pIoTask)
+{
+    RTMemCacheFree(pDisk->hMemCacheIoTask, pIoTask);
+}
+
+DECLINLINE(void) vdIoCtxChildReset(PVDIOCTX pIoCtx)
+{
+    AssertPtr(pIoCtx->pIoCtxParent);
+
+    RTSgBufReset(&pIoCtx->SgBuf);
+    pIoCtx->uOffset        = pIoCtx->Type.Child.uOffsetSaved;
+    pIoCtx->cbTransferLeft = pIoCtx->Type.Child.cbTransferLeftSaved;
+}
+
+DECLINLINE(PVDMETAXFER) vdMetaXferAlloc(PVDIMAGE pImage, PVDIOSTORAGE pIoStorage, uint64_t uOffset, size_t cb)
+{
+    PVDMETAXFER pMetaXfer = (PVDMETAXFER)RTMemAlloc(RT_OFFSETOF(VDMETAXFER, abData[cb]));
+
+    if (RT_LIKELY(pMetaXfer))
+    {
+        pMetaXfer->Core.Key     = uOffset;
+        pMetaXfer->Core.KeyLast = uOffset + cb - 1;
+        pMetaXfer->fFlags       = VDMETAXFER_TXDIR_NONE;
+        pMetaXfer->cbMeta       = cb;
+        pMetaXfer->pIoStorage   = pIoStorage;
+        pMetaXfer->cRefs        = 0;
+        RTListInit(&pMetaXfer->ListIoCtxWaiting);
+    }
+    return pMetaXfer;
+}
+
+DECLINLINE(int) vdIoCtxDefer(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+{
+    PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+
+    if (!pDeferred)
+        return VERR_NO_MEMORY;
+
+    LogFlowFunc(("Deferring write pIoCtx=%#p\n", pIoCtx));
+
+    Assert(!pIoCtx->pIoCtxParent && !pIoCtx->fBlocked);
+
+    RTListInit(&pDeferred->NodeDeferred);
+    pDeferred->pIoCtx = pIoCtx;
+    RTListAppend(&pDisk->ListWriteLocked, &pDeferred->NodeDeferred);
+    pIoCtx->fBlocked = true;
+    return VINF_SUCCESS;
+}
+
+static size_t vdIoCtxCopy(PVDIOCTX pIoCtxDst, PVDIOCTX pIoCtxSrc, size_t cbData)
+{
+    return RTSgBufCopy(&pIoCtxDst->SgBuf, &pIoCtxSrc->SgBuf, cbData);
+}
+
+static int vdIoCtxCmp(PVDIOCTX pIoCtx1, PVDIOCTX pIoCtx2, size_t cbData)
+{
+    return RTSgBufCmp(&pIoCtx1->SgBuf, &pIoCtx2->SgBuf, cbData);
+}
+
+static size_t vdIoCtxCopyTo(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
+{
+    return RTSgBufCopyToBuf(&pIoCtx->SgBuf, pbData, cbData);
+}
+
+
+static size_t vdIoCtxCopyFrom(PVDIOCTX pIoCtx, uint8_t *pbData, size_t cbData)
+{
+    return RTSgBufCopyFromBuf(&pIoCtx->SgBuf, pbData, cbData);
+}
+
+static size_t vdIoCtxSet(PVDIOCTX pIoCtx, uint8_t ch, size_t cbData)
+{
+    return RTSgBufSet(&pIoCtx->SgBuf, ch, cbData);
+}
+
+static int vdIoCtxProcess(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    RTCritSectEnter(&pDisk->CritSect);
+
+    if (   !pIoCtx->cbTransferLeft
+        && !pIoCtx->cMetaTransfersPending
+        && !pIoCtx->cDataTransfersPending
+        && !pIoCtx->pfnIoCtxTransfer)
+    {
+        rc = VINF_VD_ASYNC_IO_FINISHED;
+        goto out;
+    }
+
+    /*
+     * We complete the I/O context in case of an error
+     * if there is no I/O task pending.
+     */
+    if (   RT_FAILURE(pIoCtx->rcReq)
+        && !pIoCtx->cMetaTransfersPending
+        && !pIoCtx->cDataTransfersPending)
+    {
+        rc = VINF_VD_ASYNC_IO_FINISHED;
+        goto out;
+    }
+
+    /* Don't change anything if there is a metadata transfer pending or we are blocked. */
+    if (   pIoCtx->cMetaTransfersPending
+        || pIoCtx->fBlocked)
+    {
+        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+        goto out;
+    }
+
+    if (pIoCtx->pfnIoCtxTransfer)
+    {
+        /* Call the transfer function advancing to the next while there is no error. */
+        while (   pIoCtx->pfnIoCtxTransfer
+               && RT_SUCCESS(rc))
+        {
+            LogFlowFunc(("calling transfer function %#p\n", pIoCtx->pfnIoCtxTransfer));
+            rc = pIoCtx->pfnIoCtxTransfer(pIoCtx);
+
+            /* Advance to the next part of the transfer if the current one succeeded. */
+            if (RT_SUCCESS(rc))
+            {
+                pIoCtx->pfnIoCtxTransfer = pIoCtx->pfnIoCtxTransferNext;
+                pIoCtx->pfnIoCtxTransferNext = NULL;
+            }
+        }
+    }
+
+    if (   RT_SUCCESS(rc)
+        && !pIoCtx->cbTransferLeft
+        && !pIoCtx->cMetaTransfersPending
+        && !pIoCtx->cDataTransfersPending)
+        rc = VINF_VD_ASYNC_IO_FINISHED;
+    else if (   RT_SUCCESS(rc)
+             || rc == VERR_VD_NOT_ENOUGH_METADATA
+             || rc == VERR_VD_IOCTX_HALT)
+        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+    else if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+    {
+        ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rc, VINF_SUCCESS);
+        /*
+         * The I/O context completed if we have an error and there is no data
+         * or meta data transfer pending.
+         */
+        if (   !pIoCtx->cMetaTransfersPending
+            && !pIoCtx->cDataTransfersPending)
+            rc = VINF_VD_ASYNC_IO_FINISHED;
+        else
+            rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+    }
+
+out:
+    RTCritSectLeave(&pDisk->CritSect);
+
+    LogFlowFunc(("pIoCtx=%#p rc=%Rrc cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
+                 pIoCtx, rc, pIoCtx->cbTransferLeft, pIoCtx->cMetaTransfersPending,
+                 pIoCtx->fComplete));
+
+    return rc;
+}
+
+static int vdIoCtxLockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+
+    if (!ASMAtomicCmpXchgBool(&pDisk->fLocked, true, false))
+    {
+        rc = vdIoCtxDefer(pDisk, pIoCtx);
+        if (RT_SUCCESS(rc))
+            rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+    }
+
+    return rc;
+}
+
+static void vdIoCtxUnlockDisk(PVBOXHDD pDisk, PVDIOCTX pIoCtx)
+{
+    Assert(pDisk->fLocked);
+    ASMAtomicXchgBool(&pDisk->fLocked, false);
+
+    /* Process any pending writes if the current request didn't caused another growing. */
+    RTCritSectEnter(&pDisk->CritSect);
+
+    if (!RTListIsEmpty(&pDisk->ListWriteLocked))
+    {
+        RTLISTNODE ListTmp;
+
+        RTListMove(&ListTmp, &pDisk->ListWriteLocked);
+        RTCritSectLeave(&pDisk->CritSect);
+
+        /* Process the list. */
+        do
+        {
+            int rc;
+            PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
+            PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
+
+            AssertPtr(pIoCtxWait);
+
+            RTListNodeRemove(&pDeferred->NodeDeferred);
+            RTMemFree(pDeferred);
+
+            Assert(!pIoCtxWait->pIoCtxParent);
+
+            pIoCtxWait->fBlocked = false;
+            LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
+
+            rc = vdIoCtxProcess(pIoCtxWait);
+            if (   rc == VINF_VD_ASYNC_IO_FINISHED
+                && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
+            {
+                LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
+                vdThreadFinishWrite(pDisk);
+                pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
+                                                  pIoCtxWait->Type.Root.pvUser2,
+                                                  pIoCtxWait->rcReq);
+                vdIoCtxFree(pDisk, pIoCtxWait);
+            }
+        } while (!RTListIsEmpty(&ListTmp));
+    }
+    else
+        RTCritSectLeave(&pDisk->CritSect);
+}
+
+/**
+ * internal: read the specified amount of data in whatever blocks the backend
+ * will give us - async version.
+ */
+static int vdReadHelperAsync(PVDIOCTX pIoCtx)
+{
+    int rc;
+    size_t cbToRead     = pIoCtx->cbTransfer;
+    uint64_t uOffset    = pIoCtx->uOffset;
+    PVDIMAGE pCurrImage = NULL;
+    size_t cbThisRead;
+
+    /* Loop until all reads started or we have a backend which needs to read metadata. */
+    do
+    {
+        pCurrImage = pIoCtx->pImage;
+
+        /* Search for image with allocated block. Do not attempt to read more
+         * than the previous reads marked as valid. Otherwise this would return
+         * stale data when different block sizes are used for the images. */
+        cbThisRead = cbToRead;
+
+        /*
+         * Try to read from the given image.
+         * If the block is not allocated read from override chain if present.
+         */
+        rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
+                                               uOffset, cbThisRead,
+                                               pIoCtx, &cbThisRead);
+
+        if (rc == VERR_VD_BLOCK_FREE)
+        {
+            for (pCurrImage =  pCurrImage->pPrev;
+                 pCurrImage != NULL && rc == VERR_VD_BLOCK_FREE;
+                 pCurrImage = pCurrImage->pPrev)
+            {
+                rc = pCurrImage->Backend->pfnAsyncRead(pCurrImage->pBackendData,
+                                                       uOffset, cbThisRead,
+                                                       pIoCtx, &cbThisRead);
+            }
+        }
+
+        /* The task state will be updated on success already, don't do it here!. */
+        if (rc == VERR_VD_BLOCK_FREE)
+        {
+            /* No image in the chain contains the data for the block. */
+            vdIoCtxSet(pIoCtx, '\0', cbThisRead);
+            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisRead);
+            rc = VINF_SUCCESS;
+        }
+        else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+            rc = VINF_SUCCESS;
+        else if (rc == VERR_VD_IOCTX_HALT)
+        {
+            uOffset  += cbThisRead;
+            cbToRead -= cbThisRead;
+            pIoCtx->fBlocked = true;
+        }
+
+        if (RT_FAILURE(rc))
+            break;
+
+        cbToRead -= cbThisRead;
+        uOffset  += cbThisRead;
+    } while (cbToRead != 0 && RT_SUCCESS(rc));
+
+    if (   rc == VERR_VD_NOT_ENOUGH_METADATA
+        || rc == VERR_VD_IOCTX_HALT)
+    {
+        /* Save the current state. */
+        pIoCtx->uOffset    = uOffset;
+        pIoCtx->cbTransfer = cbToRead;
+        pIoCtx->pImage     = pCurrImage;
+    }
+
+    return rc;
+}
+
+/**
+ * internal: parent image read wrapper for compacting.
+ */
+static int vdParentRead(void *pvUser, uint64_t uOffset, void *pvBuf,
+                        size_t cbRead)
+{
+    PVDPARENTSTATEDESC pParentState = (PVDPARENTSTATEDESC)pvUser;
+    return vdReadHelper(pParentState->pDisk, pParentState->pImage, NULL, uOffset,
+                        pvBuf, cbRead, true /* fZeroFreeBlocks */,
+                        false /* fUpdateCache */);
+}
+
+/**
+ * internal: mark the disk as not modified.
+ */
+static void vdResetModifiedFlag(PVBOXHDD pDisk)
+{
+    if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
+    {
+        /* generate new last-modified uuid */
+        if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
+        {
+            RTUUID Uuid;
+
+            RTUuidCreate(&Uuid);
+            pDisk->pLast->Backend->pfnSetModificationUuid(pDisk->pLast->pBackendData,
+                                                          &Uuid);
+
+            if (pDisk->pCache)
+                pDisk->pCache->Backend->pfnSetModificationUuid(pDisk->pCache->pBackendData,
+                                                               &Uuid);
+        }
+
+        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
+    }
+}
+
+/**
+ * internal: mark the disk as modified.
+ */
+static void vdSetModifiedFlag(PVBOXHDD pDisk)
+{
+    pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
+    if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
+    {
+        pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
+
+        /* First modify, so create a UUID and ensure it's written to disk. */
+        vdResetModifiedFlag(pDisk);
+
+        if (!(pDisk->uModified & VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
+            pDisk->pLast->Backend->pfnFlush(pDisk->pLast->pBackendData);
+    }
+}
+
+/**
+ * internal: write a complete block (only used for diff images), taking the
+ * remaining data from parent images. This implementation does not optimize
+ * anything (except that it tries to read only that portions from parent
+ * images that are really needed).
+ */
+static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
+                                 PVDIMAGE pImageParentOverride,
+                                 uint64_t uOffset, size_t cbWrite,
+                                 size_t cbThisWrite, size_t cbPreRead,
+                                 size_t cbPostRead, const void *pvBuf,
+                                 void *pvTmp)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Read the data that goes before the write to fill the block. */
+    if (cbPreRead)
+    {
+        /*
+         * Updating the cache doesn't make sense here because
+         * this will be done after the complete block was written.
+         */
+        rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
+                          uOffset - cbPreRead, pvTmp, cbPreRead,
+                          true /* fZeroFreeBlocks*/,
+                          false /* fUpdateCache */);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /* Copy the data to the right place in the buffer. */
+    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
+
+    /* Read the data that goes after the write to fill the block. */
+    if (cbPostRead)
+    {
+        /* If we have data to be written, use that instead of reading
+         * data from the image. */
+        size_t cbWriteCopy;
+        if (cbWrite > cbThisWrite)
+            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
+        else
+            cbWriteCopy = 0;
+        /* Figure out how much we cannot read from the image, because
+         * the last block to write might exceed the nominal size of the
+         * image for technical reasons. */
+        size_t cbFill;
+        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
+            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
+        else
+            cbFill = 0;
+        /* The rest must be read from the image. */
+        size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
+
+        /* Now assemble the remaining data. */
+        if (cbWriteCopy)
+            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
+                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
+        if (cbReadImage)
+            rc = vdReadHelper(pDisk, pImage, pImageParentOverride,
+                              uOffset + cbThisWrite + cbWriteCopy,
+                              (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
+                              cbReadImage, true /* fZeroFreeBlocks */,
+                              false /* fUpdateCache */);
+        if (RT_FAILURE(rc))
+            return rc;
+        /* Zero out the remainder of this block. Will never be visible, as this
+         * is beyond the limit of the image. */
+        if (cbFill)
+            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
+                   '\0', cbFill);
+    }
+
+    /* Write the full block to the virtual disk. */
+    rc = pImage->Backend->pfnWrite(pImage->pBackendData,
+                                   uOffset - cbPreRead, pvTmp,
+                                   cbPreRead + cbThisWrite + cbPostRead,
+                                   NULL, &cbPreRead, &cbPostRead, 0);
+    Assert(rc != VERR_VD_BLOCK_FREE);
+    Assert(cbPreRead == 0);
+    Assert(cbPostRead == 0);
+
+    return rc;
+}
+
+/**
+ * internal: write a complete block (only used for diff images), taking the
+ * remaining data from parent images. This implementation optimizes out writes
+ * that do not change the data relative to the state as of the parent images.
+ * All backends which support differential/growing images support this.
+ */
+static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
+                                  PVDIMAGE pImageParentOverride,
+                                  uint64_t uOffset, size_t cbWrite,
+                                  size_t cbThisWrite, size_t cbPreRead,
+                                  size_t cbPostRead, const void *pvBuf,
+                                  void *pvTmp)
+{
+    size_t cbFill = 0;
+    size_t cbWriteCopy = 0;
+    size_t cbReadImage = 0;
+    int rc;
+
+    if (cbPostRead)
+    {
+        /* Figure out how much we cannot read from the image, because
+         * the last block to write might exceed the nominal size of the
+         * image for technical reasons. */
+        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
+            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
+
+        /* If we have data to be written, use that instead of reading
+         * data from the image. */
+        if (cbWrite > cbThisWrite)
+            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
+
+        /* The rest must be read from the image. */
+        cbReadImage = cbPostRead - cbWriteCopy - cbFill;
+    }
+
+    /* Read the entire data of the block so that we can compare whether it will
+     * be modified by the write or not. */
+    rc = vdReadHelper(pDisk, pImage, pImageParentOverride, uOffset - cbPreRead, pvTmp,
+                      cbPreRead + cbThisWrite + cbPostRead - cbFill,
+                      true /* fZeroFreeBlocks */,
+                      false /* fUpdateCache */);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* Check if the write would modify anything in this block. */
+    if (   !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
+        && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
+                                    (char *)pvBuf + cbThisWrite, cbWriteCopy)))
+    {
+        /* Block is completely unchanged, so no need to write anything. */
+        return VINF_SUCCESS;
+    }
+
+    /* Copy the data to the right place in the buffer. */
+    memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
+
+    /* Handle the data that goes after the write to fill the block. */
+    if (cbPostRead)
+    {
+        /* Now assemble the remaining data. */
+        if (cbWriteCopy)
+            memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
+                   (char *)pvBuf + cbThisWrite, cbWriteCopy);
+        /* Zero out the remainder of this block. Will never be visible, as this
+         * is beyond the limit of the image. */
+        if (cbFill)
+            memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
+                   '\0', cbFill);
+    }
+
+    /* Write the full block to the virtual disk. */
+    rc = pImage->Backend->pfnWrite(pImage->pBackendData,
+                                   uOffset - cbPreRead, pvTmp,
+                                   cbPreRead + cbThisWrite + cbPostRead,
+                                   NULL, &cbPreRead, &cbPostRead, 0);
+    Assert(rc != VERR_VD_BLOCK_FREE);
+    Assert(cbPreRead == 0);
+    Assert(cbPostRead == 0);
+
+    return rc;
+}
+
+/**
+ * internal: write buffer to the image, taking care of block boundaries and
+ * write optimizations.
+ */
+static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage,
+                         PVDIMAGE pImageParentOverride, uint64_t uOffset,
+                         const void *pvBuf, size_t cbWrite,
+                         bool fUpdateCache)
+{
+    int rc;
+    unsigned fWrite;
+    size_t cbThisWrite;
+    size_t cbPreRead, cbPostRead;
+    uint64_t uOffsetCur = uOffset;
+    size_t cbWriteCur = cbWrite;
+    const void *pcvBufCur = pvBuf;
+
+    /* Loop until all written. */
+    do
+    {
+        /* Try to write the possibly partial block to the last opened image.
+         * This works when the block is already allocated in this image or
+         * if it is a full-block write (and allocation isn't suppressed below).
+         * For image formats which don't support zero blocks, it's beneficial
+         * to avoid unnecessarily allocating unchanged blocks. This prevents
+         * unwanted expanding of images. VMDK is an example. */
+        cbThisWrite = cbWriteCur;
+        fWrite =   (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
+                 ? 0 : VD_WRITE_NO_ALLOC;
+        rc = pImage->Backend->pfnWrite(pImage->pBackendData, uOffsetCur, pcvBufCur,
+                                       cbThisWrite, &cbThisWrite, &cbPreRead,
+                                       &cbPostRead, fWrite);
+        if (rc == VERR_VD_BLOCK_FREE)
+        {
+            void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
+            AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
+
+            if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
+            {
+                /* Optimized write, suppress writing to a so far unallocated
+                 * block if the data is in fact not changed. */
+                rc = vdWriteHelperOptimized(pDisk, pImage, pImageParentOverride,
+                                            uOffsetCur, cbWriteCur,
+                                            cbThisWrite, cbPreRead, cbPostRead,
+                                            pcvBufCur, pvTmp);
+            }
+            else
+            {
+                /* Normal write, not optimized in any way. The block will
+                 * be written no matter what. This will usually (unless the
+                 * backend has some further optimization enabled) cause the
+                 * block to be allocated. */
+                rc = vdWriteHelperStandard(pDisk, pImage, pImageParentOverride,
+                                           uOffsetCur, cbWriteCur,
+                                           cbThisWrite, cbPreRead, cbPostRead,
+                                           pcvBufCur, pvTmp);
+            }
+            RTMemTmpFree(pvTmp);
+            if (RT_FAILURE(rc))
+                break;
+        }
+
+        cbWriteCur -= cbThisWrite;
+        uOffsetCur += cbThisWrite;
+        pcvBufCur = (char *)pcvBufCur + cbThisWrite;
+    } while (cbWriteCur != 0 && RT_SUCCESS(rc));
+
+    /* Update the cache on success */
+    if (   RT_SUCCESS(rc)
+        && pDisk->pCache
+        && fUpdateCache)
+        rc = vdCacheWriteHelper(pDisk->pCache, uOffset, pvBuf, cbWrite, NULL);
+
+    return rc;
+}
+
+/**
+ * internal: write a complete block (only used for diff images), taking the
+ * remaining data from parent images. This implementation does not optimize
+ * anything (except that it tries to read only that portions from parent
+ * images that are really needed) - async version.
+ */
+static int vdWriteHelperStandardAsync(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+
+#if 0
+
+    /* Read the data that goes before the write to fill the block. */
+    if (cbPreRead)
+    {
+        rc = vdReadHelperAsync(pIoCtxDst);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /* Copy the data to the right place in the buffer. */
+    vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbThisWrite);
+
+    /* Read the data that goes after the write to fill the block. */
+    if (cbPostRead)
+    {
+        /* If we have data to be written, use that instead of reading
+         * data from the image. */
+        size_t cbWriteCopy;
+        if (cbWrite > cbThisWrite)
+            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
+        else
+            cbWriteCopy = 0;
+        /* Figure out how much we cannot read from the image, because
+         * the last block to write might exceed the nominal size of the
+         * image for technical reasons. */
+        size_t cbFill;
+        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
+            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
+        else
+            cbFill = 0;
+        /* The rest must be read from the image. */
+        size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
+
+        /* Now assemble the remaining data. */
+        if (cbWriteCopy)
+        {
+            vdIoCtxCopy(pIoCtxDst, pIoCtxSrc, cbWriteCopy);
+            ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbWriteCopy);
+        }
+
+        if (cbReadImage)
+            rc = vdReadHelperAsync(pDisk, pImage, pImageParentOverride, pIoCtxDst,
+                                   uOffset + cbThisWrite + cbWriteCopy,
+                                   cbReadImage);
+        if (RT_FAILURE(rc))
+            return rc;
+        /* Zero out the remainder of this block. Will never be visible, as this
+         * is beyond the limit of the image. */
+        if (cbFill)
+        {
+            vdIoCtxSet(pIoCtxDst, '\0', cbFill);
+            ASMAtomicSubU32(&pIoCtxDst->cbTransferLeft, cbFill);
+        }
+    }
+
+    if (   !pIoCtxDst->cbTransferLeft
+        && !pIoCtxDst->cMetaTransfersPending
+        && ASMAtomicCmpXchgBool(&pIoCtxDst->fComplete, true, false))
+    {
+        /* Write the full block to the virtual disk. */
+        vdIoCtxChildReset(pIoCtxDst);
+        rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
+                                            uOffset - cbPreRead,
+                                            cbPreRead + cbThisWrite + cbPostRead,
+                                            pIoCtxDst,
+                                            NULL, &cbPreRead, &cbPostRead, 0);
+        Assert(rc != VERR_VD_BLOCK_FREE);
+        Assert(cbPreRead == 0);
+        Assert(cbPostRead == 0);
+    }
+    else
+    {
+        LogFlow(("cbTransferLeft=%u cMetaTransfersPending=%u fComplete=%RTbool\n",
+                 pIoCtxDst->cbTransferLeft, pIoCtxDst->cMetaTransfersPending,
+                 pIoCtxDst->fComplete));
+        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+    }
+
+    return rc;
+#endif
+    return VERR_NOT_IMPLEMENTED;
+}
+
+static int vdWriteHelperOptimizedCmpAndWriteAsync(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PVDIMAGE pImage = pIoCtx->pImage;
+    size_t cbThisWrite    = 0;
+    size_t cbPreRead      = pIoCtx->Type.Child.cbPreRead;
+    size_t cbPostRead     = pIoCtx->Type.Child.cbPostRead;
+    size_t cbWriteCopy    = pIoCtx->Type.Child.Write.Optimized.cbWriteCopy;
+    size_t cbFill         = pIoCtx->Type.Child.Write.Optimized.cbFill;
+    size_t cbReadImage    = pIoCtx->Type.Child.Write.Optimized.cbReadImage;
+    PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    AssertPtr(pIoCtxParent);
+    Assert(!pIoCtxParent->pIoCtxParent);
+    Assert(!pIoCtx->cbTransferLeft && !pIoCtx->cMetaTransfersPending);
+
+    vdIoCtxChildReset(pIoCtx);
+    cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
+    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
+
+    /* Check if the write would modify anything in this block. */
+    if (!RTSgBufCmp(&pIoCtx->SgBuf, &pIoCtxParent->SgBuf, cbThisWrite))
+    {
+        RTSGBUF SgBufSrcTmp;
+
+        RTSgBufClone(&SgBufSrcTmp, &pIoCtxParent->SgBuf);
+        RTSgBufAdvance(&SgBufSrcTmp, cbThisWrite);
+        RTSgBufAdvance(&pIoCtx->SgBuf, cbThisWrite);
+
+        if (!cbWriteCopy || !RTSgBufCmp(&pIoCtx->SgBuf, &SgBufSrcTmp, cbWriteCopy))
+        {
+            /* Block is completely unchanged, so no need to write anything. */
+            LogFlowFunc(("Block didn't changed\n"));
+            ASMAtomicWriteU32(&pIoCtx->cbTransferLeft, 0);
+            RTSgBufAdvance(&pIoCtxParent->SgBuf, cbThisWrite);
+            return VINF_VD_ASYNC_IO_FINISHED;
+        }
+    }
+
+    /* Copy the data to the right place in the buffer. */
+    RTSgBufReset(&pIoCtx->SgBuf);
+    RTSgBufAdvance(&pIoCtx->SgBuf, cbPreRead);
+    vdIoCtxCopy(pIoCtx, pIoCtxParent, cbThisWrite);
+
+    /* Handle the data that goes after the write to fill the block. */
+    if (cbPostRead)
+    {
+        /* Now assemble the remaining data. */
+        if (cbWriteCopy)
+        {
+            /*
+             * The S/G buffer of the parent needs to be cloned because
+             * it is not allowed to modify the state.
+             */
+            RTSGBUF SgBufParentTmp;
+
+            RTSgBufClone(&SgBufParentTmp, &pIoCtxParent->SgBuf);
+            RTSgBufCopy(&pIoCtx->SgBuf, &SgBufParentTmp, cbWriteCopy);
+        }
+
+        /* Zero out the remainder of this block. Will never be visible, as this
+         * is beyond the limit of the image. */
+        if (cbFill)
+        {
+            RTSgBufAdvance(&pIoCtx->SgBuf, cbReadImage);
+            vdIoCtxSet(pIoCtx, '\0', cbFill);
+        }
+    }
+
+    /* Write the full block to the virtual disk. */
+    RTSgBufReset(&pIoCtx->SgBuf);
+    rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData,
+                                        pIoCtx->uOffset - cbPreRead,
+                                        cbPreRead + cbThisWrite + cbPostRead,
+                                        pIoCtx, NULL, &cbPreRead, &cbPostRead, 0);
+    Assert(rc != VERR_VD_BLOCK_FREE);
+    Assert(cbPreRead == 0);
+    Assert(cbPostRead == 0);
+    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        rc = VINF_SUCCESS;
+    else if (rc == VERR_VD_IOCTX_HALT)
+    {
+        pIoCtx->fBlocked = true;
+        rc = VINF_SUCCESS;
+    }
+
+    return rc;
+}
+
+static int vdWriteHelperOptimizedPreReadAsync(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    if (pIoCtx->cbTransferLeft)
+        rc = vdReadHelperAsync(pIoCtx);
+
+    if (   RT_SUCCESS(rc)
+        && (   pIoCtx->cbTransferLeft
+            || pIoCtx->cMetaTransfersPending))
+        rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+     else
+        pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedCmpAndWriteAsync;
+
+    return rc;
+}
+
+/**
+ * internal: write a complete block (only used for diff images), taking the
+ * remaining data from parent images. This implementation optimizes out writes
+ * that do not change the data relative to the state as of the parent images.
+ * All backends which support differential/growing images support this - async version.
+ */
+static int vdWriteHelperOptimizedAsync(PVDIOCTX pIoCtx)
+{
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+    uint64_t uOffset   = pIoCtx->Type.Child.uOffsetSaved;
+    size_t cbThisWrite = pIoCtx->Type.Child.cbTransferParent;
+    size_t cbPreRead   = pIoCtx->Type.Child.cbPreRead;
+    size_t cbPostRead  = pIoCtx->Type.Child.cbPostRead;
+    size_t cbWrite     = pIoCtx->Type.Child.cbWriteParent;
+    size_t cbFill = 0;
+    size_t cbWriteCopy = 0;
+    size_t cbReadImage = 0;
+
+    LogFlowFunc(("pIoCtx=%#p\n", pIoCtx));
+
+    AssertPtr(pIoCtx->pIoCtxParent);
+    Assert(!pIoCtx->pIoCtxParent->pIoCtxParent);
+
+    if (cbPostRead)
+    {
+        /* Figure out how much we cannot read from the image, because
+         * the last block to write might exceed the nominal size of the
+         * image for technical reasons. */
+        if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
+            cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
+
+        /* If we have data to be written, use that instead of reading
+         * data from the image. */
+        if (cbWrite > cbThisWrite)
+            cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
+
+        /* The rest must be read from the image. */
+        cbReadImage = cbPostRead - cbWriteCopy - cbFill;
+    }
+
+    pIoCtx->Type.Child.Write.Optimized.cbFill      = cbFill;
+    pIoCtx->Type.Child.Write.Optimized.cbWriteCopy = cbWriteCopy;
+    pIoCtx->Type.Child.Write.Optimized.cbReadImage = cbReadImage;
+
+    /* Read the entire data of the block so that we can compare whether it will
+     * be modified by the write or not. */
+    pIoCtx->cbTransferLeft = cbPreRead + cbThisWrite + cbPostRead - cbFill;
+    pIoCtx->cbTransfer     = pIoCtx->cbTransferLeft;
+    pIoCtx->uOffset       -= cbPreRead;
+
+    /* Next step */
+    pIoCtx->pfnIoCtxTransferNext = vdWriteHelperOptimizedPreReadAsync;
+    return VINF_SUCCESS;
+}
+
+/**
+ * internal: write buffer to the image, taking care of block boundaries and
+ * write optimizations - async version.
+ */
+static int vdWriteHelperAsync(PVDIOCTX pIoCtx)
+{
+    int rc;
+    size_t cbWrite   = pIoCtx->cbTransfer;
+    uint64_t uOffset = pIoCtx->uOffset;
+    PVDIMAGE pImage  = pIoCtx->pImage;
+    PVBOXHDD pDisk   = pIoCtx->pDisk;
+    unsigned fWrite;
+    size_t cbThisWrite;
+    size_t cbPreRead, cbPostRead;
+
+    /* Loop until all written. */
+    do
+    {
+        /* Try to write the possibly partial block to the last opened image.
+         * This works when the block is already allocated in this image or
+         * if it is a full-block write (and allocation isn't suppressed below).
+         * For image formats which don't support zero blocks, it's beneficial
+         * to avoid unnecessarily allocating unchanged blocks. This prevents
+         * unwanted expanding of images. VMDK is an example. */
+        cbThisWrite = cbWrite;
+        fWrite =   (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
+                 ? 0 : VD_WRITE_NO_ALLOC;
+        rc = pImage->Backend->pfnAsyncWrite(pImage->pBackendData, uOffset,
+                                            cbThisWrite, pIoCtx,
+                                            &cbThisWrite, &cbPreRead,
+                                            &cbPostRead, fWrite);
+        if (rc == VERR_VD_BLOCK_FREE)
+        {
+            /* Lock the disk .*/
+            rc = vdIoCtxLockDisk(pDisk, pIoCtx);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Allocate segment and buffer in one go.
+                 * A bit hackish but avoids the need to allocate memory twice.
+                 */
+                PRTSGSEG pTmp = (PRTSGSEG)RTMemAlloc(cbPreRead + cbThisWrite + cbPostRead + sizeof(RTSGSEG));
+                AssertBreakStmt(VALID_PTR(pTmp), rc = VERR_NO_MEMORY);
+
+                pTmp->pvSeg = pTmp + 1;
+                pTmp->cbSeg = cbPreRead + cbThisWrite + cbPostRead;
+
+                PVDIOCTX pIoCtxWrite = vdIoCtxChildAlloc(pDisk, VDIOCTXTXDIR_WRITE,
+                                                         uOffset, pTmp->cbSeg,
+                                                         pTmp, 1,
+                                                         pIoCtx, cbThisWrite,
+                                                         cbWrite,
+                                                         pTmp,
+                                                           (pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME)
+                                                         ? vdWriteHelperStandardAsync
+                                                         : vdWriteHelperOptimizedAsync);
+                if (!VALID_PTR(pIoCtxWrite))
+                {
+                    RTMemTmpFree(pTmp);
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                LogFlowFunc(("Disk is growing because of pIoCtx=%#p pIoCtxWrite=%#p\n",
+                             pIoCtx, pIoCtxWrite));
+
+                pIoCtxWrite->pImage                = pImage;
+                pIoCtxWrite->Type.Child.cbPreRead  = cbPreRead;
+                pIoCtxWrite->Type.Child.cbPostRead = cbPostRead;
+
+                /* Process the write request */
+                rc = vdIoCtxProcess(pIoCtxWrite);
+
+                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+                {
+                    vdIoCtxFree(pDisk, pIoCtxWrite);
+                    break;
+                }
+                else if (   rc == VINF_VD_ASYNC_IO_FINISHED
+                         && ASMAtomicCmpXchgBool(&pIoCtxWrite->fComplete, true, false))
+                {
+                    LogFlow(("Child write request completed\n"));
+                    Assert(pIoCtx->cbTransferLeft >= cbThisWrite);
+                    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbThisWrite);
+                    ASMAtomicWriteBool(&pDisk->fLocked, false);
+                    vdIoCtxFree(pDisk, pIoCtxWrite);
+
+                    rc = VINF_SUCCESS;
+                }
+                else
+                {
+                    LogFlow(("Child write pending\n"));
+                    pIoCtx->fBlocked = true;
+                    rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+                    cbWrite -= cbThisWrite;
+                    uOffset += cbThisWrite;
+                    break;
+                }
+            }
+            else
+            {
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+                break;
+            }
+        }
+
+        if (rc == VERR_VD_IOCTX_HALT)
+        {
+            cbWrite -= cbThisWrite;
+            uOffset += cbThisWrite;
+            pIoCtx->fBlocked = true;
+            break;
+        }
+        else if (rc == VERR_VD_NOT_ENOUGH_METADATA)
+            break;
+
+        cbWrite -= cbThisWrite;
+        uOffset += cbThisWrite;
+    } while (cbWrite != 0 && (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS));
+
+    if (   rc == VERR_VD_ASYNC_IO_IN_PROGRESS
+        || rc == VERR_VD_NOT_ENOUGH_METADATA
+        || rc == VERR_VD_IOCTX_HALT)
+    {
+        /*
+         * Tell the caller that we don't need to go back here because all
+         * writes are initiated.
+         */
+        if (!cbWrite)
+            rc = VINF_SUCCESS;
+
+        pIoCtx->uOffset    = uOffset;
+        pIoCtx->cbTransfer = cbWrite;
+    }
+
+    return rc;
+}
+
+/**
+ * Flush helper async version.
+ */
+static int vdFlushHelperAsync(PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+    PVDIMAGE pImage = pIoCtx->pImage;
+
+    rc = vdIoCtxLockDisk(pDisk, pIoCtx);
+    if (RT_SUCCESS(rc))
+    {
+        vdResetModifiedFlag(pDisk);
+        rc = pImage->Backend->pfnAsyncFlush(pImage->pBackendData, pIoCtx);
+        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+            rc = VINF_SUCCESS;
+    }
+
+    return rc;
+}
+
+/**
+ * internal: scans plugin directory and loads the backends have been found.
+ */
+static int vdLoadDynamicBackends()
+{
+#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
+    int rc = VINF_SUCCESS;
+    PRTDIR pPluginDir = NULL;
+
+    /* Enumerate plugin backends. */
+    char szPath[RTPATH_MAX];
+    rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* To get all entries with VBoxHDD as prefix. */
+    char *pszPluginFilter = RTPathJoinA(szPath, VBOX_HDDFORMAT_PLUGIN_PREFIX "*");
+    if (!pszPluginFilter)
+        return VERR_NO_STR_MEMORY;
+
+    PRTDIRENTRYEX pPluginDirEntry = NULL;
+    size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
+    /* The plugins are in the same directory as the other shared libs. */
+    rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
+    if (RT_FAILURE(rc))
+    {
+        /* On Windows the above immediately signals that there are no
+         * files matching, while on other platforms enumerating the
+         * files below fails. Either way: no plugins. */
+        goto out;
+    }
+
+    pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
+    if (!pPluginDirEntry)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
+    {
+        RTLDRMOD hPlugin = NIL_RTLDRMOD;
+        PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
+        PVBOXHDDBACKEND pBackend = NULL;
+        char *pszPluginPath = NULL;
+
+        if (rc == VERR_BUFFER_OVERFLOW)
+        {
+            /* allocate new buffer. */
+            RTMemFree(pPluginDirEntry);
+            pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
+            if (!pPluginDirEntry)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+            /* Retry. */
+            rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+            if (RT_FAILURE(rc))
+                break;
+        }
+        else if (RT_FAILURE(rc))
+            break;
+
+        /* We got the new entry. */
+        if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
+            continue;
+
+        /* Prepend the path to the libraries. */
+        pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
+        if (!pszPluginPath)
+        {
+            rc = VERR_NO_STR_MEMORY;
+            break;
+        }
+
+        rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
+            if (RT_FAILURE(rc) || !pfnHDDFormatLoad)
+            {
+                LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
+                if (RT_SUCCESS(rc))
+                    rc = VERR_SYMBOL_NOT_FOUND;
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Get the function table. */
+                rc = pfnHDDFormatLoad(&pBackend);
+                if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
+                {
+                    pBackend->hPlugin = hPlugin;
+                    vdAddBackend(pBackend);
+                }
+                else
+                    LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
+            }
+            else
+                LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
+
+            if (RT_FAILURE(rc))
+                RTLdrClose(hPlugin);
+        }
+        RTStrFree(pszPluginPath);
+    }
+out:
+    if (rc == VERR_NO_MORE_FILES)
+        rc = VINF_SUCCESS;
+    RTStrFree(pszPluginFilter);
+    if (pPluginDirEntry)
+        RTMemFree(pPluginDirEntry);
+    if (pPluginDir)
+        RTDirClose(pPluginDir);
+    return rc;
+#else
+    return VINF_SUCCESS;
+#endif
+}
+
+/**
+ * internal: scans plugin directory and loads the cache backends have been found.
+ */
+static int vdLoadDynamicCacheBackends()
+{
+#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
+    int rc = VINF_SUCCESS;
+    PRTDIR pPluginDir = NULL;
+
+    /* Enumerate plugin backends. */
+    char szPath[RTPATH_MAX];
+    rc = RTPathAppPrivateArch(szPath, sizeof(szPath));
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* To get all entries with VBoxHDD as prefix. */
+    char *pszPluginFilter = RTPathJoinA(szPath, VD_CACHEFORMAT_PLUGIN_PREFIX "*");
+    if (!pszPluginFilter)
+    {
+        rc = VERR_NO_STR_MEMORY;
+        return rc;
+    }
+
+    PRTDIRENTRYEX pPluginDirEntry = NULL;
+    size_t cbPluginDirEntry = sizeof(RTDIRENTRYEX);
+    /* The plugins are in the same directory as the other shared libs. */
+    rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
+    if (RT_FAILURE(rc))
+    {
+        /* On Windows the above immediately signals that there are no
+         * files matching, while on other platforms enumerating the
+         * files below fails. Either way: no plugins. */
+        goto out;
+    }
+
+    pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(sizeof(RTDIRENTRYEX));
+    if (!pPluginDirEntry)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    while ((rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK)) != VERR_NO_MORE_FILES)
+    {
+        RTLDRMOD hPlugin = NIL_RTLDRMOD;
+        PFNVDCACHEFORMATLOAD pfnVDCacheLoad = NULL;
+        PVDCACHEBACKEND pBackend = NULL;
+        char *pszPluginPath = NULL;
+
+        if (rc == VERR_BUFFER_OVERFLOW)
+        {
+            /* allocate new buffer. */
+            RTMemFree(pPluginDirEntry);
+            pPluginDirEntry = (PRTDIRENTRYEX)RTMemAllocZ(cbPluginDirEntry);
+            if (!pPluginDirEntry)
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
+            /* Retry. */
+            rc = RTDirReadEx(pPluginDir, pPluginDirEntry, &cbPluginDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+            if (RT_FAILURE(rc))
+                break;
+        }
+        else if (RT_FAILURE(rc))
+            break;
+
+        /* We got the new entry. */
+        if (!RTFS_IS_FILE(pPluginDirEntry->Info.Attr.fMode))
+            continue;
+
+        /* Prepend the path to the libraries. */
+        pszPluginPath = RTPathJoinA(szPath, pPluginDirEntry->szName);
+        if (!pszPluginPath)
+        {
+            rc = VERR_NO_STR_MEMORY;
+            break;
+        }
+
+        rc = SUPR3HardenedLdrLoad(pszPluginPath, &hPlugin);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTLdrGetSymbol(hPlugin, VD_CACHEFORMAT_LOAD_NAME, (void**)&pfnVDCacheLoad);
+            if (RT_FAILURE(rc) || !pfnVDCacheLoad)
+            {
+                LogFunc(("error resolving the entry point %s in plugin %s, rc=%Rrc, pfnVDCacheLoad=%#p\n",
+                         VD_CACHEFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnVDCacheLoad));
+                if (RT_SUCCESS(rc))
+                    rc = VERR_SYMBOL_NOT_FOUND;
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Get the function table. */
+                rc = pfnVDCacheLoad(&pBackend);
+                if (RT_SUCCESS(rc) && pBackend->cbSize == sizeof(VDCACHEBACKEND))
+                {
+                    pBackend->hPlugin = hPlugin;
+                    vdAddCacheBackend(pBackend);
+                }
+                else
+                    LogFunc(("ignored plugin '%s': pBackend->cbSize=%d rc=%Rrc\n", pszPluginPath, pBackend->cbSize, rc));
+            }
+            else
+                LogFunc(("ignored plugin '%s': rc=%Rrc\n", pszPluginPath, rc));
+
+            if (RT_FAILURE(rc))
+                RTLdrClose(hPlugin);
+        }
+        RTStrFree(pszPluginPath);
+    }
+out:
+    if (rc == VERR_NO_MORE_FILES)
+        rc = VINF_SUCCESS;
+    RTStrFree(pszPluginFilter);
+    if (pPluginDirEntry)
+        RTMemFree(pPluginDirEntry);
+    if (pPluginDir)
+        RTDirClose(pPluginDir);
+    return rc;
+#else
+    return VINF_SUCCESS;
+#endif
+}
+
+/**
+ * VD async I/O interface open callback.
+ */
+static int vdIOOpenFallback(void *pvUser, const char *pszLocation,
+                            uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
+                            void **ppStorage)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)RTMemAllocZ(sizeof(VDIIOFALLBACKSTORAGE));
+
+    if (!pStorage)
+        return VERR_NO_MEMORY;
+
+    pStorage->pfnCompleted = pfnCompleted;
+
+    /* Open the file. */
+    int rc = RTFileOpen(&pStorage->File, pszLocation, fOpen);
+    if (RT_SUCCESS(rc))
+    {
+        *ppStorage = pStorage;
+        return VINF_SUCCESS;
+    }
+
+    RTMemFree(pStorage);
+    return rc;
+}
+
+/**
+ * VD async I/O interface close callback.
+ */
+static int vdIOCloseFallback(void *pvUser, void *pvStorage)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    RTFileClose(pStorage->File);
+    RTMemFree(pStorage);
+    return VINF_SUCCESS;
+}
+
+static int vdIODeleteFallback(void *pvUser, const char *pcszFilename)
+{
+    return RTFileDelete(pcszFilename);
+}
+
+static int vdIOMoveFallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
+{
+    return RTFileMove(pcszSrc, pcszDst, fMove);
+}
+
+static int vdIOGetFreeSpaceFallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
+{
+    return RTFsQuerySizes(pcszFilename, NULL, pcbFreeSpace, NULL, NULL);
+}
+
+static int vdIOGetModificationTimeFallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
+{
+    RTFSOBJINFO info;
+    int rc = RTPathQueryInfo(pcszFilename, &info, RTFSOBJATTRADD_NOTHING);
+    if (RT_SUCCESS(rc))
+        *pModificationTime = info.ModificationTime;
+    return rc;
+}
+
+/**
+ * VD async I/O interface callback for retrieving the file size.
+ */
+static int vdIOGetSizeFallback(void *pvUser, void *pvStorage, uint64_t *pcbSize)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    return RTFileGetSize(pStorage->File, pcbSize);
+}
+
+/**
+ * VD async I/O interface callback for setting the file size.
+ */
+static int vdIOSetSizeFallback(void *pvUser, void *pvStorage, uint64_t cbSize)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    return RTFileSetSize(pStorage->File, cbSize);
+}
+
+/**
+ * VD async I/O interface callback for a synchronous write to the file.
+ */
+static int vdIOWriteSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                              const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    return RTFileWriteAt(pStorage->File, uOffset, pvBuf, cbWrite, pcbWritten);
+}
+
+/**
+ * VD async I/O interface callback for a synchronous read from the file.
+ */
+static int vdIOReadSyncFallback(void *pvUser, void *pvStorage, uint64_t uOffset,
+                             void *pvBuf, size_t cbRead, size_t *pcbRead)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    return RTFileReadAt(pStorage->File, uOffset, pvBuf, cbRead, pcbRead);
+}
+
+/**
+ * VD async I/O interface callback for a synchronous flush of the file data.
+ */
+static int vdIOFlushSyncFallback(void *pvUser, void *pvStorage)
+{
+    PVDIIOFALLBACKSTORAGE pStorage = (PVDIIOFALLBACKSTORAGE)pvStorage;
+
+    return RTFileFlush(pStorage->File);
+}
+
+/**
+ * VD async I/O interface callback for a asynchronous read from the file.
+ */
+static int vdIOReadAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
+                                 PCRTSGSEG paSegments, size_t cSegments,
+                                 size_t cbRead, void *pvCompletion,
+                                 void **ppTask)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * VD async I/O interface callback for a asynchronous write to the file.
+ */
+static int vdIOWriteAsyncFallback(void *pvUser, void *pStorage, uint64_t uOffset,
+                                  PCRTSGSEG paSegments, size_t cSegments,
+                                  size_t cbWrite, void *pvCompletion,
+                                  void **ppTask)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * VD async I/O interface callback for a asynchronous flush of the file data.
+ */
+static int vdIOFlushAsyncFallback(void *pvUser, void *pStorage,
+                                  void *pvCompletion, void **ppTask)
+{
+    return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Internal - Continues an I/O context after
+ * it was halted because of an active transfer.
+ */
+static int vdIoCtxContinue(PVDIOCTX pIoCtx, int rcReq)
+{
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+    int rc = VINF_SUCCESS;
+
+    if (RT_FAILURE(rcReq))
+        ASMAtomicCmpXchgS32(&pIoCtx->rcReq, rcReq, VINF_SUCCESS);
+
+    if (!pIoCtx->fBlocked)
+    {
+        /* Continue the transfer */
+        rc = vdIoCtxProcess(pIoCtx);
+
+        if (   rc == VINF_VD_ASYNC_IO_FINISHED
+            && ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
+        {
+            LogFlowFunc(("I/O context completed pIoCtx=%#p\n", pIoCtx));
+            if (pIoCtx->pIoCtxParent)
+            {
+                PVDIOCTX pIoCtxParent = pIoCtx->pIoCtxParent;
+
+                LogFlowFunc(("I/O context transfered %u bytes for the parent pIoCtxParent=%p\n",
+                             pIoCtx->Type.Child.cbTransferParent, pIoCtxParent));
+
+                /* Update the parent state. */
+                Assert(!pIoCtxParent->pIoCtxParent);
+                Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE);
+                Assert(pIoCtxParent->cbTransferLeft >= pIoCtx->Type.Child.cbTransferParent);
+                ASMAtomicSubU32(&pIoCtxParent->cbTransferLeft, pIoCtx->Type.Child.cbTransferParent);
+
+                if (RT_FAILURE(pIoCtx->rcReq))
+                    ASMAtomicCmpXchgS32(&pIoCtxParent->rcReq, pIoCtx->rcReq, VINF_SUCCESS);
+
+                /*
+                 * A completed child write means that we finished growing the image.
+                 * We have to process any pending writes now.
+                 */
+                Assert(pDisk->fLocked);
+                ASMAtomicWriteBool(&pDisk->fLocked, false);
+
+                /* Unblock the parent */
+                pIoCtxParent->fBlocked = false;
+
+                rc = vdIoCtxProcess(pIoCtxParent);
+
+                if (   rc == VINF_VD_ASYNC_IO_FINISHED
+                    && ASMAtomicCmpXchgBool(&pIoCtxParent->fComplete, true, false))
+                {
+                    LogFlowFunc(("Parent I/O context completed pIoCtxParent=%#p rcReq=%Rrc\n", pIoCtxParent, pIoCtxParent->rcReq));
+                    pIoCtxParent->Type.Root.pfnComplete(pIoCtxParent->Type.Root.pvUser1,
+                                                        pIoCtxParent->Type.Root.pvUser2,
+                                                        pIoCtxParent->rcReq);
+                    vdThreadFinishWrite(pDisk);
+                    vdIoCtxFree(pDisk, pIoCtxParent);
+                }
+
+                /* Process any pending writes if the current request didn't caused another growing. */
+                RTCritSectEnter(&pDisk->CritSect);
+
+                if (!RTListIsEmpty(&pDisk->ListWriteLocked) && !pDisk->fLocked)
+                {
+                    RTLISTNODE ListTmp;
+
+                    LogFlowFunc(("Before: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
+                                 pDisk->ListWriteLocked.pPrev));
+
+                    RTListMove(&ListTmp, &pDisk->ListWriteLocked);
+
+                    LogFlowFunc(("After: pNext=%#p pPrev=%#p\n", pDisk->ListWriteLocked.pNext,
+                                 pDisk->ListWriteLocked.pPrev));
+
+                    RTCritSectLeave(&pDisk->CritSect);
+
+                    /* Process the list. */
+                    do
+                    {
+                        PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListTmp, VDIOCTXDEFERRED, NodeDeferred);
+                        PVDIOCTX pIoCtxWait = pDeferred->pIoCtx;
+
+                        AssertPtr(pIoCtxWait);
+
+                        RTListNodeRemove(&pDeferred->NodeDeferred);
+                        RTMemFree(pDeferred);
+
+                        Assert(!pIoCtxWait->pIoCtxParent);
+
+                        pIoCtxWait->fBlocked = false;
+                        LogFlowFunc(("Processing waiting I/O context pIoCtxWait=%#p\n", pIoCtxWait));
+
+                        rc = vdIoCtxProcess(pIoCtxWait);
+                        if (   rc == VINF_VD_ASYNC_IO_FINISHED
+                            && ASMAtomicCmpXchgBool(&pIoCtxWait->fComplete, true, false))
+                        {
+                            LogFlowFunc(("Waiting I/O context completed pIoCtxWait=%#p\n", pIoCtxWait));
+                            vdThreadFinishWrite(pDisk);
+                            pIoCtxWait->Type.Root.pfnComplete(pIoCtxWait->Type.Root.pvUser1,
+                                                              pIoCtxWait->Type.Root.pvUser2,
+                                                              pIoCtxWait->rcReq);
+                            vdIoCtxFree(pDisk, pIoCtxWait);
+                        }
+                    } while (!RTListIsEmpty(&ListTmp));
+                }
+                else
+                    RTCritSectLeave(&pDisk->CritSect);
+            }
+            else
+            {
+                if (pIoCtx->enmTxDir == VDIOCTXTXDIR_FLUSH)
+                {
+                    vdIoCtxUnlockDisk(pDisk, pIoCtx);
+                    vdThreadFinishWrite(pDisk);
+                }
+                else if (pIoCtx->enmTxDir == VDIOCTXTXDIR_WRITE)
+                    vdThreadFinishWrite(pDisk);
+                else
+                {
+                    Assert(pIoCtx->enmTxDir == VDIOCTXTXDIR_READ);
+                    vdThreadFinishRead(pDisk);
+                }
+
+                LogFlowFunc(("I/O context completed pIoCtx=%#p rcReq=%Rrc\n", pIoCtx, pIoCtx->rcReq));
+                pIoCtx->Type.Root.pfnComplete(pIoCtx->Type.Root.pvUser1,
+                                              pIoCtx->Type.Root.pvUser2,
+                                              pIoCtx->rcReq);
+            }
+
+            vdIoCtxFree(pDisk, pIoCtx);
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal - Called when user transfer completed.
+ */
+static int vdUserXferCompleted(PVDIOSTORAGE pIoStorage, PVDIOCTX pIoCtx,
+                               PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
+                               size_t cbTransfer, int rcReq)
+{
+    int rc = VINF_SUCCESS;
+    bool fIoCtxContinue = true;
+    PVBOXHDD pDisk = pIoCtx->pDisk;
+
+    LogFlowFunc(("pIoStorage=%#p pIoCtx=%#p pfnComplete=%#p pvUser=%#p cbTransfer=%zu rcReq=%Rrc\n",
+                 pIoStorage, pIoCtx, pfnComplete, pvUser, cbTransfer, rcReq));
+
+    Assert(pIoCtx->cbTransferLeft >= cbTransfer);
+    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTransfer);
+    ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+
+    if (pfnComplete)
+    {
+        RTCritSectEnter(&pDisk->CritSect);
+        rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
+        RTCritSectLeave(&pDisk->CritSect);
+    }
+
+    if (RT_SUCCESS(rc))
+        rc = vdIoCtxContinue(pIoCtx, rcReq);
+    else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        rc = VINF_SUCCESS;
+
+    return rc;
+}
+
+/**
+ * Internal - Called when a meta transfer completed.
+ */
+static int vdMetaXferCompleted(PVDIOSTORAGE pIoStorage, PFNVDXFERCOMPLETED pfnComplete, void *pvUser,
+                               PVDMETAXFER pMetaXfer, int rcReq)
+{
+    PVBOXHDD pDisk = pIoStorage->pImage->pDisk;
+    RTLISTNODE ListIoCtxWaiting;
+    bool fFlush;
+
+    LogFlowFunc(("pIoStorage=%#p pfnComplete=%#p pvUser=%#p pMetaXfer=%#p rcReq=%Rrc\n",
+                 pIoStorage, pfnComplete, pvUser, pMetaXfer, rcReq));
+
+    RTCritSectEnter(&pDisk->CritSect);
+    fFlush = VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_FLUSH;
+    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+
+    if (!fFlush)
+    {
+        RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
+
+        if (RT_FAILURE(rcReq))
+        {
+            /* Remove from the AVL tree. */
+            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
+            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
+            Assert(fRemoved);
+            RTMemFree(pMetaXfer);
+        }
+        else
+        {
+            /* Increase the reference counter to make sure it doesn't go away before the last context is processed. */
+            pMetaXfer->cRefs++;
+        }
+    }
+    else
+        RTListMove(&ListIoCtxWaiting, &pMetaXfer->ListIoCtxWaiting);
+    RTCritSectLeave(&pDisk->CritSect);
+
+    /* Go through the waiting list and continue the I/O contexts. */
+    while (!RTListIsEmpty(&ListIoCtxWaiting))
+    {
+        int rc = VINF_SUCCESS;
+        bool fContinue = true;
+        PVDIOCTXDEFERRED pDeferred = RTListNodeGetFirst(&ListIoCtxWaiting, VDIOCTXDEFERRED, NodeDeferred);
+        PVDIOCTX pIoCtx = pDeferred->pIoCtx;
+        RTListNodeRemove(&pDeferred->NodeDeferred);
+
+        RTMemFree(pDeferred);
+        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
+
+        if (pfnComplete)
+        {
+            RTCritSectEnter(&pDisk->CritSect);
+            rc = pfnComplete(pIoStorage->pImage->pBackendData, pIoCtx, pvUser, rcReq);
+            RTCritSectLeave(&pDisk->CritSect);
+        }
+
+        LogFlow(("Completion callback for I/O context %#p returned %Rrc\n", pIoCtx, rc));
+
+        if (RT_SUCCESS(rc))
+        {
+            rc = vdIoCtxContinue(pIoCtx, rcReq);
+            AssertRC(rc);
+        }
+        else
+            Assert(rc == VERR_VD_ASYNC_IO_IN_PROGRESS);
+    }
+
+    /* Remove if not used anymore. */
+    if (RT_SUCCESS(rcReq) && !fFlush)
+    {
+        RTCritSectEnter(&pDisk->CritSect);
+        pMetaXfer->cRefs--;
+        if (!pMetaXfer->cRefs && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting))
+        {
+            /* Remove from the AVL tree. */
+            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
+            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
+            Assert(fRemoved);
+            RTMemFree(pMetaXfer);
+        }
+        RTCritSectLeave(&pDisk->CritSect);
+    }
+    else if (fFlush)
+        RTMemFree(pMetaXfer);
+
+    return VINF_SUCCESS;
+}
+
+static int vdIOIntReqCompleted(void *pvUser, int rcReq)
+{
+    int rc = VINF_SUCCESS;
+    PVDIOTASK pIoTask = (PVDIOTASK)pvUser;
+    PVDIOSTORAGE pIoStorage = pIoTask->pIoStorage;
+
+    LogFlowFunc(("Task completed pIoTask=%#p\n", pIoTask));
+
+    if (!pIoTask->fMeta)
+        rc = vdUserXferCompleted(pIoStorage, pIoTask->Type.User.pIoCtx,
+                                 pIoTask->pfnComplete, pIoTask->pvUser,
+                                 pIoTask->Type.User.cbTransfer, rcReq);
+    else
+        rc = vdMetaXferCompleted(pIoStorage, pIoTask->pfnComplete, pIoTask->pvUser,
+                                 pIoTask->Type.Meta.pMetaXfer, rcReq);
+
+    vdIoTaskFree(pIoStorage->pImage->pDisk, pIoTask);
+
+    return rc;
+}
+
+/**
+ * VD I/O interface callback for opening a file.
+ */
+static int vdIOIntOpen(void *pvUser, const char *pszLocation,
+                       unsigned uOpenFlags, PPVDIOSTORAGE ppIoStorage)
+{
+    int rc = VINF_SUCCESS;
+    PVDIMAGE pImage         = (PVDIMAGE)pvUser;
+    PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
+
+    if (!pIoStorage)
+        return VERR_NO_MEMORY;
+
+    pIoStorage->pImage = pImage;
+
+    /* Create the AVl tree. */
+    pIoStorage->pTreeMetaXfers = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
+    if (pIoStorage->pTreeMetaXfers)
+    {
+        rc = pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                    pszLocation, uOpenFlags,
+                                                    vdIOIntReqCompleted,
+                                                    &pIoStorage->pStorage);
+        if (RT_SUCCESS(rc))
+        {
+            *ppIoStorage = pIoStorage;
+            return VINF_SUCCESS;
+        }
+
+        RTMemFree(pIoStorage->pTreeMetaXfers);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    RTMemFree(pIoStorage);
+    return rc;
+}
+
+static int vdIOIntTreeMetaXferDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
+{
+    AssertMsgFailed(("Tree should be empty at this point!\n"));
+    return VINF_SUCCESS;
+}
+
+static int vdIOIntClose(void *pvUser, PVDIOSTORAGE pIoStorage)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    int rc = pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                     pIoStorage->pStorage);
+    AssertRC(rc);
+
+    RTAvlrFileOffsetDestroy(pIoStorage->pTreeMetaXfers, vdIOIntTreeMetaXferDestroy, NULL);
+    RTMemFree(pIoStorage->pTreeMetaXfers);
+    RTMemFree(pIoStorage);
+    return VINF_SUCCESS;
+}
+
+static int vdIOIntDelete(void *pvUser, const char *pcszFilename)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                    pcszFilename);
+}
+
+static int vdIOIntMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
+                       unsigned fMove)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pcszSrc, pcszDst, fMove);
+}
+
+static int vdIOIntGetFreeSpace(void *pvUser, const char *pcszFilename,
+                               int64_t *pcbFreeSpace)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
+                                                          pcszFilename,
+                                                          pcbFreeSpace);
+}
+
+static int vdIOIntGetModificationTime(void *pvUser, const char *pcszFilename,
+                                      PRTTIMESPEC pModificationTime)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
+                                                                 pcszFilename,
+                                                                 pModificationTime);
+}
+
+static int vdIOIntGetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
+                          uint64_t *pcbSize)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                    pIoStorage->pStorage,
+                                                    pcbSize);
+}
+
+static int vdIOIntSetSize(void *pvUser, PVDIOSTORAGE pIoStorage,
+                          uint64_t cbSize)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pIoStorage->pStorage,
+                                                     cbSize);
+}
+
+static int vdIOIntWriteSync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                            uint64_t uOffset, const void *pvBuf,
+                            size_t cbWrite, size_t *pcbWritten)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pIoStorage->pStorage,
+                                                       uOffset, pvBuf, cbWrite,
+                                                       pcbWritten);
+}
+
+static int vdIOIntReadSync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                           uint64_t uOffset, void *pvBuf, size_t cbRead,
+                           size_t *pcbRead)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pIoStorage->pStorage,
+                                                      uOffset, pvBuf, cbRead,
+                                                      pcbRead);
+}
+
+static int vdIOIntFlushSync(void *pvUser, PVDIOSTORAGE pIoStorage)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pIoStorage->pStorage);
+}
+
+static int vdIOIntReadUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                uint64_t uOffset, PVDIOCTX pIoCtx,
+                                size_t cbRead)
+{
+    int rc = VINF_SUCCESS;
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+
+    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbRead=%u\n",
+                 pvUser, pIoStorage, uOffset, pIoCtx, cbRead));
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    /* Build the S/G array and spawn a new I/O task */
+    while (cbRead)
+    {
+        RTSGSEG  aSeg[VD_IO_TASK_SEGMENTS_MAX];
+        unsigned cSegments  = VD_IO_TASK_SEGMENTS_MAX;
+        size_t   cbTaskRead = 0;
+
+        cbTaskRead = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbRead);
+
+        AssertMsg(cbTaskRead <= cbRead, ("Invalid number of bytes to read\n"));
+
+        LogFlow(("Reading %u bytes into %u segments\n", cbTaskRead, cSegments));
+
+#ifdef RT_STRICT
+        for (unsigned i = 0; i < cSegments; i++)
+                AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
+                          ("Segment %u is invalid\n", i));
+#endif
+
+        PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, NULL, NULL, pIoCtx, cbTaskRead);
+
+        if (!pIoTask)
+            return VERR_NO_MEMORY;
+
+        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
+
+        void *pvTask;
+        rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
+                                                         pIoStorage->pStorage,
+                                                         uOffset, aSeg, cSegments,
+                                                         cbTaskRead, pIoTask,
+                                                         &pvTask);
+        if (RT_SUCCESS(rc))
+        {
+            AssertMsg(cbTaskRead <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
+            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskRead);
+            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+            vdIoTaskFree(pDisk, pIoTask);
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+        {
+            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+            break;
+        }
+
+        uOffset += cbTaskRead;
+        cbRead  -= cbTaskRead;
+    }
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+static int vdIOIntWriteUserAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                 uint64_t uOffset, PVDIOCTX pIoCtx,
+                                 size_t cbWrite,
+                                 PFNVDXFERCOMPLETED pfnComplete,
+                                 void *pvCompleteUser)
+{
+    int rc = VINF_SUCCESS;
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+
+    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pIoCtx=%#p cbWrite=%u\n",
+                 pvUser, pIoStorage, uOffset, pIoCtx, cbWrite));
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    /* Build the S/G array and spawn a new I/O task */
+    while (cbWrite)
+    {
+        RTSGSEG  aSeg[VD_IO_TASK_SEGMENTS_MAX];
+        unsigned cSegments   = VD_IO_TASK_SEGMENTS_MAX;
+        size_t   cbTaskWrite = 0;
+
+        cbTaskWrite = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, aSeg, &cSegments, cbWrite);
+
+        AssertMsg(cbTaskWrite <= cbWrite, ("Invalid number of bytes to write\n"));
+
+        LogFlow(("Writing %u bytes from %u segments\n", cbTaskWrite, cSegments));
+
+#ifdef DEBUG
+        for (unsigned i = 0; i < cSegments; i++)
+                AssertMsg(aSeg[i].pvSeg && !(aSeg[i].cbSeg % 512),
+                          ("Segment %u is invalid\n", i));
+#endif
+
+        PVDIOTASK pIoTask = vdIoTaskUserAlloc(pIoStorage, pfnComplete, pvCompleteUser, pIoCtx, cbTaskWrite);
+
+        if (!pIoTask)
+            return VERR_NO_MEMORY;
+
+        ASMAtomicIncU32(&pIoCtx->cDataTransfersPending);
+
+        void *pvTask;
+        rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
+                                                          pIoStorage->pStorage,
+                                                          uOffset, aSeg, cSegments,
+                                                          cbTaskWrite, pIoTask,
+                                                          &pvTask);
+        if (RT_SUCCESS(rc))
+        {
+            AssertMsg(cbTaskWrite <= pIoCtx->cbTransferLeft, ("Impossible!\n"));
+            ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbTaskWrite);
+            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+            vdIoTaskFree(pDisk, pIoTask);
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+        {
+            ASMAtomicDecU32(&pIoCtx->cDataTransfersPending);
+            break;
+        }
+
+        uOffset += cbTaskWrite;
+        cbWrite -= cbTaskWrite;
+    }
+
+    return rc;
+}
+
+static int vdIOIntReadMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                uint64_t uOffset, void *pvBuf,
+                                size_t cbRead, PVDIOCTX pIoCtx,
+                                PPVDMETAXFER ppMetaXfer,
+                                PFNVDXFERCOMPLETED pfnComplete,
+                                void *pvCompleteUser)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    int rc = VINF_SUCCESS;
+    RTSGSEG Seg;
+    PVDIOTASK pIoTask;
+    PVDMETAXFER pMetaXfer = NULL;
+    void *pvTask = NULL;
+
+    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbRead=%u\n",
+                 pvUser, pIoStorage, uOffset, pvBuf, cbRead));
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
+    if (!pMetaXfer)
+    {
+#ifdef RT_STRICT
+        pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGetBestFit(pIoStorage->pTreeMetaXfers, uOffset, false /* fAbove */);
+        AssertMsg(!pMetaXfer || (pMetaXfer->Core.Key + (RTFOFF)pMetaXfer->cbMeta <= (RTFOFF)uOffset),
+                  ("Overlapping meta transfers!\n"));
+#endif
+
+        /* Allocate a new meta transfer. */
+        pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbRead);
+        if (!pMetaXfer)
+            return VERR_NO_MEMORY;
+
+        pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
+        if (!pIoTask)
+        {
+            RTMemFree(pMetaXfer);
+            return VERR_NO_MEMORY;
+        }
+
+        Seg.cbSeg = cbRead;
+        Seg.pvSeg = pMetaXfer->abData;
+
+        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_READ);
+        rc = pImage->pInterfaceIOCallbacks->pfnReadAsync(pImage->pInterfaceIO->pvUser,
+                                                         pIoStorage->pStorage,
+                                                         uOffset, &Seg, 1,
+                                                         cbRead, pIoTask,
+                                                         &pvTask);
+
+        if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        {
+            bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
+            Assert(fInserted);
+        }
+        else
+            RTMemFree(pMetaXfer);
+
+        if (RT_SUCCESS(rc))
+        {
+            VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+            vdIoTaskFree(pDisk, pIoTask);
+        }
+        else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS && !pfnComplete)
+            rc = VERR_VD_NOT_ENOUGH_METADATA;
+    }
+
+    Assert(VALID_PTR(pMetaXfer) || RT_FAILURE(rc));
+
+    if (RT_SUCCESS(rc) || rc == VERR_VD_NOT_ENOUGH_METADATA || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+    {
+        /* If it is pending add the request to the list. */
+        if (VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_READ)
+        {
+            PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+            AssertPtr(pDeferred);
+
+            RTListInit(&pDeferred->NodeDeferred);
+            pDeferred->pIoCtx = pIoCtx;
+
+            ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+            RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+            rc = VERR_VD_NOT_ENOUGH_METADATA;
+        }
+        else
+        {
+            /* Transfer the data. */
+            pMetaXfer->cRefs++;
+            Assert(pMetaXfer->cbMeta >= cbRead);
+            Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
+            memcpy(pvBuf, pMetaXfer->abData, cbRead);
+            *ppMetaXfer = pMetaXfer;
+        }
+    }
+
+    return rc;
+}
+
+static int vdIOIntWriteMetaAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                 uint64_t uOffset, void *pvBuf,
+                                 size_t cbWrite, PVDIOCTX pIoCtx,
+                                 PFNVDXFERCOMPLETED pfnComplete,
+                                 void *pvCompleteUser)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    int rc = VINF_SUCCESS;
+    RTSGSEG Seg;
+    PVDIOTASK pIoTask;
+    PVDMETAXFER pMetaXfer = NULL;
+    bool fInTree = false;
+    void *pvTask = NULL;
+
+    LogFlowFunc(("pvUser=%#p pIoStorage=%#p uOffset=%llu pvBuf=%#p cbWrite=%u\n",
+                 pvUser, pIoStorage, uOffset, pvBuf, cbWrite));
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    pMetaXfer = (PVDMETAXFER)RTAvlrFileOffsetGet(pIoStorage->pTreeMetaXfers, uOffset);
+    if (!pMetaXfer)
+    {
+        /* Allocate a new meta transfer. */
+        pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, uOffset, cbWrite);
+        if (!pMetaXfer)
+            return VERR_NO_MEMORY;
+    }
+    else
+    {
+        Assert(pMetaXfer->cbMeta >= cbWrite);
+        Assert(pMetaXfer->Core.Key == (RTFOFF)uOffset);
+        fInTree = true;
+    }
+
+    Assert(VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE);
+
+    pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvCompleteUser, pMetaXfer);
+    if (!pIoTask)
+    {
+        RTMemFree(pMetaXfer);
+        return VERR_NO_MEMORY;
+    }
+
+    memcpy(pMetaXfer->abData, pvBuf, cbWrite);
+    Seg.cbSeg = cbWrite;
+    Seg.pvSeg = pMetaXfer->abData;
+
+    ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+
+    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_WRITE);
+    rc = pImage->pInterfaceIOCallbacks->pfnWriteAsync(pImage->pInterfaceIO->pvUser,
+                                                      pIoStorage->pStorage,
+                                                      uOffset, &Seg, 1,
+                                                      cbWrite, pIoTask,
+                                                      &pvTask);
+    if (RT_SUCCESS(rc))
+    {
+        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
+        vdIoTaskFree(pDisk, pIoTask);
+        if (fInTree && !pMetaXfer->cRefs)
+        {
+            LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
+            bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
+            AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
+            RTMemFree(pMetaXfer);
+            pMetaXfer = NULL;
+        }
+    }
+    else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+    {
+        PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+        AssertPtr(pDeferred);
+
+        RTListInit(&pDeferred->NodeDeferred);
+        pDeferred->pIoCtx = pIoCtx;
+
+        if (!fInTree)
+        {
+            bool fInserted = RTAvlrFileOffsetInsert(pIoStorage->pTreeMetaXfers, &pMetaXfer->Core);
+            Assert(fInserted);
+        }
+
+        RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+    }
+    else
+    {
+        RTMemFree(pMetaXfer);
+        pMetaXfer = NULL;
+    }
+
+    return rc;
+}
+
+static void vdIOIntMetaXferRelease(void *pvUser, PVDMETAXFER pMetaXfer)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    PVDIOSTORAGE pIoStorage = pMetaXfer->pIoStorage;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    Assert(   VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE
+           || VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_WRITE);
+    Assert(pMetaXfer->cRefs > 0);
+
+    pMetaXfer->cRefs--;
+    if (   !pMetaXfer->cRefs
+        && RTListIsEmpty(&pMetaXfer->ListIoCtxWaiting)
+        && VDMETAXFER_TXDIR_GET(pMetaXfer->fFlags) == VDMETAXFER_TXDIR_NONE)
+    {
+        /* Free the meta data entry. */
+        LogFlow(("Removing meta xfer=%#p\n", pMetaXfer));
+        bool fRemoved = RTAvlrFileOffsetRemove(pIoStorage->pTreeMetaXfers, pMetaXfer->Core.Key) != NULL;
+        AssertMsg(fRemoved, ("Metadata transfer wasn't removed\n"));
+
+        RTMemFree(pMetaXfer);
+    }
+}
+
+static int vdIOIntFlushAsync(void *pvUser, PVDIOSTORAGE pIoStorage,
+                             PVDIOCTX pIoCtx, PFNVDXFERCOMPLETED pfnComplete,
+                             void *pvCompleteUser)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    int rc = VINF_SUCCESS;
+    PVDIOTASK pIoTask;
+    PVDMETAXFER pMetaXfer = NULL;
+    void *pvTask = NULL;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    LogFlowFunc(("pvUser=%#p pIoStorage=%#p pIoCtx=%#p\n",
+                 pvUser, pIoStorage, pIoCtx));
+
+    /* Allocate a new meta transfer. */
+    pMetaXfer = vdMetaXferAlloc(pImage, pIoStorage, 0, 0);
+    if (!pMetaXfer)
+        return VERR_NO_MEMORY;
+
+    pIoTask = vdIoTaskMetaAlloc(pIoStorage, pfnComplete, pvUser, pMetaXfer);
+    if (!pIoTask)
+    {
+        RTMemFree(pMetaXfer);
+        return VERR_NO_MEMORY;
+    }
+
+    ASMAtomicIncU32(&pIoCtx->cMetaTransfersPending);
+
+    PVDIOCTXDEFERRED pDeferred = (PVDIOCTXDEFERRED)RTMemAllocZ(sizeof(VDIOCTXDEFERRED));
+    AssertPtr(pDeferred);
+
+    RTListInit(&pDeferred->NodeDeferred);
+    pDeferred->pIoCtx = pIoCtx;
+
+    RTListAppend(&pMetaXfer->ListIoCtxWaiting, &pDeferred->NodeDeferred);
+    VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_FLUSH);
+    rc = pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                      pIoStorage->pStorage,
+                                                      pIoTask, &pvTask);
+    if (RT_SUCCESS(rc))
+    {
+        VDMETAXFER_TXDIR_SET(pMetaXfer->fFlags, VDMETAXFER_TXDIR_NONE);
+        ASMAtomicDecU32(&pIoCtx->cMetaTransfersPending);
+        vdIoTaskFree(pDisk, pIoTask);
+        RTMemFree(pDeferred);
+        RTMemFree(pMetaXfer);
+    }
+    else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+        RTMemFree(pMetaXfer);
+
+    return rc;
+}
+
+static size_t vdIOIntIoCtxCopyTo(void *pvUser, PVDIOCTX pIoCtx,
+                                 void *pvBuf, size_t cbBuf)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    size_t cbCopied = 0;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    cbCopied = vdIoCtxCopyTo(pIoCtx, (uint8_t *)pvBuf, cbBuf);
+    Assert(cbCopied == cbBuf);
+
+    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
+
+    return cbCopied;
+}
+
+static size_t vdIOIntIoCtxCopyFrom(void *pvUser, PVDIOCTX pIoCtx,
+                                   void *pvBuf, size_t cbBuf)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    size_t cbCopied = 0;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    cbCopied = vdIoCtxCopyFrom(pIoCtx, (uint8_t *)pvBuf, cbBuf);
+    Assert(cbCopied == cbBuf);
+
+    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCopied);
+
+    return cbCopied;
+}
+
+static size_t vdIOIntIoCtxSet(void *pvUser, PVDIOCTX pIoCtx, int ch, size_t cb)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    size_t cbSet = 0;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    cbSet = vdIoCtxSet(pIoCtx, ch, cb);
+    Assert(cbSet == cb);
+
+    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbSet);
+
+    return cbSet;
+}
+
+static size_t vdIOIntIoCtxSegArrayCreate(void *pvUser, PVDIOCTX pIoCtx,
+                                         PRTSGSEG paSeg, unsigned *pcSeg,
+                                         size_t cbData)
+{
+    PVDIMAGE pImage = (PVDIMAGE)pvUser;
+    PVBOXHDD pDisk  = pImage->pDisk;
+    size_t cbCreated = 0;
+
+    VD_THREAD_IS_CRITSECT_OWNER(pDisk);
+
+    cbCreated = RTSgBufSegArrayCreate(&pIoCtx->SgBuf, paSeg, pcSeg, cbData);
+    Assert(!paSeg || cbData == cbCreated);
+
+    return cbCreated;
+}
+
+static void vdIOIntIoCtxCompleted(void *pvUser, PVDIOCTX pIoCtx, int rcReq,
+                                  size_t cbCompleted)
+{
+    /* Continue */
+    pIoCtx->fBlocked = false;
+    ASMAtomicSubU32(&pIoCtx->cbTransferLeft, cbCompleted);
+
+    /* Clear the pointer to next transfer function in case we have nothing to transfer anymore.
+     * @todo: Find a better way to prevent vdIoCtxContinue from calling the read/write helper again. */
+    if (!pIoCtx->cbTransferLeft)
+        pIoCtx->pfnIoCtxTransfer = NULL;
+
+    vdIoCtxContinue(pIoCtx, rcReq);
+}
+
+/**
+ * VD I/O interface callback for opening a file (limited version for VDGetFormat).
+ */
+static int vdIOIntOpenLimited(void *pvUser, const char *pszLocation,
+                              uint32_t fOpen, PPVDIOSTORAGE ppIoStorage)
+{
+    int rc = VINF_SUCCESS;
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE));
+
+    if (!pIoStorage)
+        return VERR_NO_MEMORY;
+
+    rc = pInterfaceIOCallbacks->pfnOpen(NULL, pszLocation, fOpen,
+                                        NULL, &pIoStorage->pStorage);
+    if (RT_SUCCESS(rc))
+        *ppIoStorage = pIoStorage;
+    else
+        RTMemFree(pIoStorage);
+
+    return rc;
+}
+
+static int vdIOIntCloseLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    int rc = pInterfaceIOCallbacks->pfnClose(NULL, pIoStorage->pStorage);
+    AssertRC(rc);
+
+    RTMemFree(pIoStorage);
+    return VINF_SUCCESS;
+}
+
+static int vdIOIntDeleteLimited(void *pvUser, const char *pcszFilename)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnDelete(NULL, pcszFilename);
+}
+
+static int vdIOIntMoveLimited(void *pvUser, const char *pcszSrc,
+                              const char *pcszDst, unsigned fMove)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnMove(NULL, pcszSrc, pcszDst, fMove);
+}
+
+static int vdIOIntGetFreeSpaceLimited(void *pvUser, const char *pcszFilename,
+                                      int64_t *pcbFreeSpace)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnGetFreeSpace(NULL, pcszFilename, pcbFreeSpace);
+}
+
+static int vdIOIntGetModificationTimeLimited(void *pvUser,
+                                             const char *pcszFilename,
+                                             PRTTIMESPEC pModificationTime)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnGetModificationTime(NULL, pcszFilename, pModificationTime);
+}
+
+static int vdIOIntGetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                 uint64_t *pcbSize)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnGetSize(NULL, pIoStorage->pStorage, pcbSize);
+}
+
+static int vdIOIntSetSizeLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                 uint64_t cbSize)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnSetSize(NULL, pIoStorage->pStorage, cbSize);
+}
+
+static int vdIOIntWriteSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                   uint64_t uOffset, const void *pvBuf,
+                                   size_t cbWrite, size_t *pcbWritten)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnWriteSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbWrite, pcbWritten);
+}
+
+static int vdIOIntReadSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage,
+                                  uint64_t uOffset, void *pvBuf, size_t cbRead,
+                                  size_t *pcbRead)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnReadSync(NULL, pIoStorage->pStorage, uOffset, pvBuf, cbRead, pcbRead);
+}
+
+static int vdIOIntFlushSyncLimited(void *pvUser, PVDIOSTORAGE pIoStorage)
+{
+    PVDINTERFACEIO pInterfaceIOCallbacks = (PVDINTERFACEIO)pvUser;
+    return pInterfaceIOCallbacks->pfnFlushSync(NULL, pIoStorage->pStorage);
+}
+
+/**
+ * internal: send output to the log (unconditionally).
+ */
+int vdLogMessage(void *pvUser, const char *pszFormat, va_list args)
+{
+    NOREF(pvUser);
+    RTLogPrintfV(pszFormat, args);
+    return VINF_SUCCESS;
+}
+
+DECLINLINE(int) vdMessageWrapper(PVBOXHDD pDisk, const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    int rc = pDisk->pInterfaceErrorCallbacks->pfnMessage(pDisk->pInterfaceError->pvUser,
+                                                         pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+/**
+ * Initializes HDD backends.
+ *
+ * @returns VBox status code.
+ */
+VBOXDDU_DECL(int) VDInit(void)
+{
+    int rc = vdAddBackends(aStaticBackends, RT_ELEMENTS(aStaticBackends));
+    if (RT_SUCCESS(rc))
+    {
+        rc = vdAddCacheBackends(aStaticCacheBackends, RT_ELEMENTS(aStaticCacheBackends));
+        if (RT_SUCCESS(rc))
+        {
+            rc = vdLoadDynamicBackends();
+            if (RT_SUCCESS(rc))
+                rc = vdLoadDynamicCacheBackends();
+        }
+    }
+    LogRel(("VDInit finished\n"));
+    return rc;
+}
+
+/**
+ * Destroys loaded HDD backends.
+ *
+ * @returns VBox status code.
+ */
+VBOXDDU_DECL(int) VDShutdown(void)
+{
+    PVBOXHDDBACKEND *pBackends = g_apBackends;
+    PVDCACHEBACKEND *pCacheBackends = g_apCacheBackends;
+    unsigned cBackends = g_cBackends;
+
+    if (!pBackends)
+        return VERR_INTERNAL_ERROR;
+
+    g_cBackends = 0;
+    g_apBackends = NULL;
+
+#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
+    for (unsigned i = 0; i < cBackends; i++)
+        if (pBackends[i]->hPlugin != NIL_RTLDRMOD)
+            RTLdrClose(pBackends[i]->hPlugin);
+#endif
+
+    /* Clear the supported cache backends. */
+    cBackends = g_cCacheBackends;
+    g_cCacheBackends = 0;
+    g_apCacheBackends = NULL;
+
+#ifndef VBOX_HDD_NO_DYNAMIC_BACKENDS
+    for (unsigned i = 0; i < cBackends; i++)
+        if (pCacheBackends[i]->hPlugin != NIL_RTLDRMOD)
+            RTLdrClose(pCacheBackends[i]->hPlugin);
+#endif
+
+    if (pCacheBackends)
+        RTMemFree(pCacheBackends);
+    RTMemFree(pBackends);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Lists all HDD backends and their capabilities in a caller-provided buffer.
+ *
+ * @returns VBox status code.
+ *          VERR_BUFFER_OVERFLOW if not enough space is passed.
+ * @param   cEntriesAlloc   Number of list entries available.
+ * @param   pEntries        Pointer to array for the entries.
+ * @param   pcEntriesUsed   Number of entries returned.
+ */
+VBOXDDU_DECL(int) VDBackendInfo(unsigned cEntriesAlloc, PVDBACKENDINFO pEntries,
+                                unsigned *pcEntriesUsed)
+{
+    int rc = VINF_SUCCESS;
+    PRTDIR pPluginDir = NULL;
+    unsigned cEntries = 0;
+
+    LogFlowFunc(("cEntriesAlloc=%u pEntries=%#p pcEntriesUsed=%#p\n", cEntriesAlloc, pEntries, pcEntriesUsed));
+    /* Check arguments. */
+    AssertMsgReturn(cEntriesAlloc,
+                    ("cEntriesAlloc=%u\n", cEntriesAlloc),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(pEntries),
+                    ("pEntries=%#p\n", pEntries),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(pcEntriesUsed),
+                    ("pcEntriesUsed=%#p\n", pcEntriesUsed),
+                    VERR_INVALID_PARAMETER);
+    if (!g_apBackends)
+        VDInit();
+
+    if (cEntriesAlloc < g_cBackends)
+    {
+        *pcEntriesUsed = g_cBackends;
+        return VERR_BUFFER_OVERFLOW;
+    }
+
+    for (unsigned i = 0; i < g_cBackends; i++)
+    {
+        pEntries[i].pszBackend = g_apBackends[i]->pszBackendName;
+        pEntries[i].uBackendCaps = g_apBackends[i]->uBackendCaps;
+        pEntries[i].paFileExtensions = g_apBackends[i]->paFileExtensions;
+        pEntries[i].paConfigInfo = g_apBackends[i]->paConfigInfo;
+        pEntries[i].pfnComposeLocation = g_apBackends[i]->pfnComposeLocation;
+        pEntries[i].pfnComposeName = g_apBackends[i]->pfnComposeName;
+    }
+
+    LogFlowFunc(("returns %Rrc *pcEntriesUsed=%u\n", rc, cEntries));
+    *pcEntriesUsed = g_cBackends;
+    return rc;
+}
+
+/**
+ * Lists the capabilities of a backend identified by its name.
+ *
+ * @returns VBox status code.
+ * @param   pszBackend      The backend name.
+ * @param   pEntries        Pointer to an entry.
+ */
+VBOXDDU_DECL(int) VDBackendInfoOne(const char *pszBackend, PVDBACKENDINFO pEntry)
+{
+    LogFlowFunc(("pszBackend=%#p pEntry=%#p\n", pszBackend, pEntry));
+    /* Check arguments. */
+    AssertMsgReturn(VALID_PTR(pszBackend),
+                    ("pszBackend=%#p\n", pszBackend),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(pEntry),
+                    ("pEntry=%#p\n", pEntry),
+                    VERR_INVALID_PARAMETER);
+    if (!g_apBackends)
+        VDInit();
+
+    /* Go through loaded backends. */
+    for (unsigned i = 0; i < g_cBackends; i++)
+    {
+        if (!RTStrICmp(pszBackend, g_apBackends[i]->pszBackendName))
+        {
+            pEntry->pszBackend = g_apBackends[i]->pszBackendName;
+            pEntry->uBackendCaps = g_apBackends[i]->uBackendCaps;
+            pEntry->paFileExtensions = g_apBackends[i]->paFileExtensions;
+            pEntry->paConfigInfo = g_apBackends[i]->paConfigInfo;
+            return VINF_SUCCESS;
+        }
+    }
+
+    return VERR_NOT_FOUND;
+}
+
+/**
+ * Allocates and initializes an empty HDD container.
+ * No image files are opened.
+ *
+ * @returns VBox status code.
+ * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+ * @param   enmType         Type of the image container.
+ * @param   ppDisk          Where to store the reference to HDD container.
+ */
+VBOXDDU_DECL(int) VDCreate(PVDINTERFACE pVDIfsDisk, VDTYPE enmType, PVBOXHDD *ppDisk)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = NULL;
+
+    LogFlowFunc(("pVDIfsDisk=%#p\n", pVDIfsDisk));
+    do
+    {
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(ppDisk),
+                           ("ppDisk=%#p\n", ppDisk),
+                           rc = VERR_INVALID_PARAMETER);
+
+        pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
+        if (pDisk)
+        {
+            pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
+            pDisk->enmType      = enmType;
+            pDisk->cImages      = 0;
+            pDisk->pBase        = NULL;
+            pDisk->pLast        = NULL;
+            pDisk->cbSize       = 0;
+            pDisk->PCHSGeometry.cCylinders = 0;
+            pDisk->PCHSGeometry.cHeads     = 0;
+            pDisk->PCHSGeometry.cSectors   = 0;
+            pDisk->LCHSGeometry.cCylinders = 0;
+            pDisk->LCHSGeometry.cHeads     = 0;
+            pDisk->LCHSGeometry.cSectors   = 0;
+            pDisk->pVDIfsDisk  = pVDIfsDisk;
+            pDisk->pInterfaceError = NULL;
+            pDisk->pInterfaceErrorCallbacks = NULL;
+            pDisk->pInterfaceThreadSync = NULL;
+            pDisk->pInterfaceThreadSyncCallbacks = NULL;
+            pDisk->fLocked = false;
+            RTListInit(&pDisk->ListWriteLocked);
+
+            /* Create the I/O ctx cache */
+            rc = RTMemCacheCreate(&pDisk->hMemCacheIoCtx, sizeof(VDIOCTX), 0, UINT32_MAX,
+                                  NULL, NULL, NULL, 0);
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pDisk);
+                break;
+            }
+
+            /* Create the I/O task cache */
+            rc = RTMemCacheCreate(&pDisk->hMemCacheIoTask, sizeof(VDIOTASK), 0, UINT32_MAX,
+                                  NULL, NULL, NULL, 0);
+            if (RT_FAILURE(rc))
+            {
+                RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
+                RTMemFree(pDisk);
+                break;
+            }
+
+            /* Create critical section. */
+            rc = RTCritSectInit(&pDisk->CritSect);
+            if (RT_FAILURE(rc))
+            {
+                RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
+                RTMemCacheDestroy(pDisk->hMemCacheIoTask);
+                RTMemFree(pDisk);
+                break;
+            }
+
+            pDisk->pInterfaceError = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_ERROR);
+            if (pDisk->pInterfaceError)
+                pDisk->pInterfaceErrorCallbacks = VDGetInterfaceError(pDisk->pInterfaceError);
+
+            pDisk->pInterfaceThreadSync = VDInterfaceGet(pVDIfsDisk, VDINTERFACETYPE_THREADSYNC);
+            if (pDisk->pInterfaceThreadSync)
+                pDisk->pInterfaceThreadSyncCallbacks = VDGetInterfaceThreadSync(pDisk->pInterfaceThreadSync);
+
+            /* Create fallback I/O callback table */
+            pDisk->VDIIOCallbacks.cbSize                 = sizeof(VDINTERFACEIO);
+            pDisk->VDIIOCallbacks.enmInterface           = VDINTERFACETYPE_IO;
+            pDisk->VDIIOCallbacks.pfnOpen                = vdIOOpenFallback;
+            pDisk->VDIIOCallbacks.pfnClose               = vdIOCloseFallback;
+            pDisk->VDIIOCallbacks.pfnDelete              = vdIODeleteFallback;
+            pDisk->VDIIOCallbacks.pfnMove                = vdIOMoveFallback;
+            pDisk->VDIIOCallbacks.pfnGetFreeSpace        = vdIOGetFreeSpaceFallback;
+            pDisk->VDIIOCallbacks.pfnGetModificationTime = vdIOGetModificationTimeFallback;
+            pDisk->VDIIOCallbacks.pfnGetSize             = vdIOGetSizeFallback;
+            pDisk->VDIIOCallbacks.pfnSetSize             = vdIOSetSizeFallback;
+            pDisk->VDIIOCallbacks.pfnReadSync            = vdIOReadSyncFallback;
+            pDisk->VDIIOCallbacks.pfnWriteSync           = vdIOWriteSyncFallback;
+            pDisk->VDIIOCallbacks.pfnFlushSync           = vdIOFlushSyncFallback;
+            pDisk->VDIIOCallbacks.pfnReadAsync           = vdIOReadAsyncFallback;
+            pDisk->VDIIOCallbacks.pfnWriteAsync          = vdIOWriteAsyncFallback;
+            pDisk->VDIIOCallbacks.pfnFlushAsync          = vdIOFlushAsyncFallback;
+
+            /*
+             * Create the internal I/O callback table.
+             * The interface is per-image but no need to duplicate the
+             * callback table every time.
+             */
+            pDisk->VDIIOIntCallbacks.cbSize                 = sizeof(VDINTERFACEIOINT);
+            pDisk->VDIIOIntCallbacks.enmInterface           = VDINTERFACETYPE_IOINT;
+            pDisk->VDIIOIntCallbacks.pfnOpen                = vdIOIntOpen;
+            pDisk->VDIIOIntCallbacks.pfnClose               = vdIOIntClose;
+            pDisk->VDIIOIntCallbacks.pfnDelete              = vdIOIntDelete;
+            pDisk->VDIIOIntCallbacks.pfnMove                = vdIOIntMove;
+            pDisk->VDIIOIntCallbacks.pfnGetFreeSpace        = vdIOIntGetFreeSpace;
+            pDisk->VDIIOIntCallbacks.pfnGetModificationTime = vdIOIntGetModificationTime;
+            pDisk->VDIIOIntCallbacks.pfnGetSize             = vdIOIntGetSize;
+            pDisk->VDIIOIntCallbacks.pfnSetSize             = vdIOIntSetSize;
+            pDisk->VDIIOIntCallbacks.pfnReadSync            = vdIOIntReadSync;
+            pDisk->VDIIOIntCallbacks.pfnWriteSync           = vdIOIntWriteSync;
+            pDisk->VDIIOIntCallbacks.pfnFlushSync           = vdIOIntFlushSync;
+            pDisk->VDIIOIntCallbacks.pfnReadUserAsync       = vdIOIntReadUserAsync;
+            pDisk->VDIIOIntCallbacks.pfnWriteUserAsync      = vdIOIntWriteUserAsync;
+            pDisk->VDIIOIntCallbacks.pfnReadMetaAsync       = vdIOIntReadMetaAsync;
+            pDisk->VDIIOIntCallbacks.pfnWriteMetaAsync      = vdIOIntWriteMetaAsync;
+            pDisk->VDIIOIntCallbacks.pfnMetaXferRelease     = vdIOIntMetaXferRelease;
+            pDisk->VDIIOIntCallbacks.pfnFlushAsync          = vdIOIntFlushAsync;
+            pDisk->VDIIOIntCallbacks.pfnIoCtxCopyFrom       = vdIOIntIoCtxCopyFrom;
+            pDisk->VDIIOIntCallbacks.pfnIoCtxCopyTo         = vdIOIntIoCtxCopyTo;
+            pDisk->VDIIOIntCallbacks.pfnIoCtxSet            = vdIOIntIoCtxSet;
+            pDisk->VDIIOIntCallbacks.pfnIoCtxSegArrayCreate = vdIOIntIoCtxSegArrayCreate;
+            pDisk->VDIIOIntCallbacks.pfnIoCtxCompleted      = vdIOIntIoCtxCompleted;
+
+            *ppDisk = pDisk;
+        }
+        else
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+    } while (0);
+
+    LogFlowFunc(("returns %Rrc (pDisk=%#p)\n", rc, pDisk));
+    return rc;
+}
+
+/**
+ * Destroys HDD container.
+ * If container has opened image files they will be closed.
+ *
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
+{
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreak(pDisk);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+        VDCloseAll(pDisk);
+        RTCritSectDelete(&pDisk->CritSect);
+        RTMemCacheDestroy(pDisk->hMemCacheIoCtx);
+        RTMemCacheDestroy(pDisk->hMemCacheIoTask);
+        RTMemFree(pDisk);
+    } while (0);
+    LogFlowFunc(("returns\n"));
+}
+
+/**
+ * Try to get the backend name which can use this image.
+ *
+ * @returns VBox status code.
+ *          VINF_SUCCESS if a plugin was found.
+ *                       ppszFormat contains the string which can be used as backend name.
+ *          VERR_NOT_SUPPORTED if no backend was found.
+ * @param   pVDIfsDisk      Pointer to the per-disk VD interface list.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pszFilename     Name of the image file for which the backend is queried.
+ * @param   ppszFormat      Receives pointer of the UTF-8 string which contains the format name.
+ *                          The returned pointer must be freed using RTStrFree().
+ */
+VBOXDDU_DECL(int) VDGetFormat(PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                              const char *pszFilename, char **ppszFormat, VDTYPE *penmType)
+{
+    int rc = VERR_NOT_SUPPORTED;
+    VDINTERFACEIOINT VDIIOIntCallbacks;
+    VDINTERFACE      VDIIOInt;
+    VDINTERFACEIO    VDIIOCallbacksFallback;
+    PVDINTERFACE     pInterfaceIO;
+    PVDINTERFACEIO   pInterfaceIOCallbacks;
+
+    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
+    /* Check arguments. */
+    AssertMsgReturn(VALID_PTR(pszFilename) && *pszFilename,
+                    ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(ppszFormat),
+                    ("ppszFormat=%#p\n", ppszFormat),
+                    VERR_INVALID_PARAMETER);
+    AssertMsgReturn(VALID_PTR(ppszFormat),
+                    ("penmType=%#p\n", penmType),
+                    VERR_INVALID_PARAMETER);
+
+    if (!g_apBackends)
+        VDInit();
+
+    pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
+    if (!pInterfaceIO)
+    {
+        /*
+         * Caller doesn't provide an I/O interface, create our own using the
+         * native file API.
+         */
+        VDIIOCallbacksFallback.cbSize                    = sizeof(VDINTERFACEIO);
+        VDIIOCallbacksFallback.enmInterface              = VDINTERFACETYPE_IO;
+        VDIIOCallbacksFallback.pfnOpen                   = vdIOOpenFallback;
+        VDIIOCallbacksFallback.pfnClose                  = vdIOCloseFallback;
+        VDIIOCallbacksFallback.pfnDelete                 = vdIODeleteFallback;
+        VDIIOCallbacksFallback.pfnMove                   = vdIOMoveFallback;
+        VDIIOCallbacksFallback.pfnGetFreeSpace           = vdIOGetFreeSpaceFallback;
+        VDIIOCallbacksFallback.pfnGetModificationTime    = vdIOGetModificationTimeFallback;
+        VDIIOCallbacksFallback.pfnGetSize                = vdIOGetSizeFallback;
+        VDIIOCallbacksFallback.pfnSetSize                = vdIOSetSizeFallback;
+        VDIIOCallbacksFallback.pfnReadSync               = vdIOReadSyncFallback;
+        VDIIOCallbacksFallback.pfnWriteSync              = vdIOWriteSyncFallback;
+        VDIIOCallbacksFallback.pfnFlushSync              = vdIOFlushSyncFallback;
+        pInterfaceIOCallbacks = &VDIIOCallbacksFallback;
+    }
+    else
+        pInterfaceIOCallbacks = VDGetInterfaceIO(pInterfaceIO);
+
+    /* Set up the internal I/O interface. */
+    AssertReturn(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
+                 VERR_INVALID_PARAMETER);
+    VDIIOIntCallbacks.cbSize                    = sizeof(VDINTERFACEIOINT);
+    VDIIOIntCallbacks.enmInterface              = VDINTERFACETYPE_IOINT;
+    VDIIOIntCallbacks.pfnOpen                   = vdIOIntOpenLimited;
+    VDIIOIntCallbacks.pfnClose                  = vdIOIntCloseLimited;
+    VDIIOIntCallbacks.pfnDelete                 = vdIOIntDeleteLimited;
+    VDIIOIntCallbacks.pfnMove                   = vdIOIntMoveLimited;
+    VDIIOIntCallbacks.pfnGetFreeSpace           = vdIOIntGetFreeSpaceLimited;
+    VDIIOIntCallbacks.pfnGetModificationTime    = vdIOIntGetModificationTimeLimited;
+    VDIIOIntCallbacks.pfnGetSize                = vdIOIntGetSizeLimited;
+    VDIIOIntCallbacks.pfnSetSize                = vdIOIntSetSizeLimited;
+    VDIIOIntCallbacks.pfnReadSync               = vdIOIntReadSyncLimited;
+    VDIIOIntCallbacks.pfnWriteSync              = vdIOIntWriteSyncLimited;
+    VDIIOIntCallbacks.pfnFlushSync              = vdIOIntFlushSyncLimited;
+    VDIIOIntCallbacks.pfnReadUserAsync          = NULL;
+    VDIIOIntCallbacks.pfnWriteUserAsync         = NULL;
+    VDIIOIntCallbacks.pfnReadMetaAsync          = NULL;
+    VDIIOIntCallbacks.pfnWriteMetaAsync         = NULL;
+    VDIIOIntCallbacks.pfnFlushAsync             = NULL;
+    rc = VDInterfaceAdd(&VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                        &VDIIOIntCallbacks, pInterfaceIOCallbacks, &pVDIfsImage);
+    AssertRC(rc);
+
+    /* Find the backend supporting this file format. */
+    for (unsigned i = 0; i < g_cBackends; i++)
+    {
+        if (g_apBackends[i]->pfnCheckIfValid)
+        {
+            rc = g_apBackends[i]->pfnCheckIfValid(pszFilename, pVDIfsDisk,
+                                                  pVDIfsImage, penmType);
+            if (    RT_SUCCESS(rc)
+                /* The correct backend has been found, but there is a small
+                 * incompatibility so that the file cannot be used. Stop here
+                 * and signal success - the actual open will of course fail,
+                 * but that will create a really sensible error message. */
+                ||  (   rc != VERR_VD_GEN_INVALID_HEADER
+                     && rc != VERR_VD_VDI_INVALID_HEADER
+                     && rc != VERR_VD_VMDK_INVALID_HEADER
+                     && rc != VERR_VD_ISCSI_INVALID_HEADER
+                     && rc != VERR_VD_VHD_INVALID_HEADER
+                     && rc != VERR_VD_RAW_INVALID_HEADER
+                     && rc != VERR_VD_PARALLELS_INVALID_HEADER
+                     && rc != VERR_VD_DMG_INVALID_HEADER))
+            {
+                /* Copy the name into the new string. */
+                char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
+                if (!pszFormat)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+                *ppszFormat = pszFormat;
+                rc = VINF_SUCCESS;
+                break;
+            }
+            rc = VERR_NOT_SUPPORTED;
+        }
+    }
+
+    /* Try the cache backends. */
+    if (rc == VERR_NOT_SUPPORTED)
+    {
+        for (unsigned i = 0; i < g_cCacheBackends; i++)
+        {
+            if (g_apCacheBackends[i]->pfnProbe)
+            {
+                rc = g_apCacheBackends[i]->pfnProbe(pszFilename, pVDIfsDisk,
+                                                    pVDIfsImage);
+                if (    RT_SUCCESS(rc)
+                    ||  (rc != VERR_VD_GEN_INVALID_HEADER))
+                {
+                    /* Copy the name into the new string. */
+                    char *pszFormat = RTStrDup(g_apBackends[i]->pszBackendName);
+                    if (!pszFormat)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
+                    *ppszFormat = pszFormat;
+                    rc = VINF_SUCCESS;
+                    break;
+                }
+                rc = VERR_NOT_SUPPORTED;
+            }
+        }
+    }
+
+    LogFlowFunc(("returns %Rrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
+    return rc;
+}
+
+/**
+ * Opens an image file.
+ *
+ * The first opened image file in 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 another 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 is opened in read-only mode if a read/write open is not possible.
+ * Use VDIsReadOnly to check open mode.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use.
+ * @param   pszFilename     Name of the image file to open.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ */
+VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszBackend,
+                         const char *pszFilename, unsigned uOpenFlags,
+                         PVDINTERFACE pVDIfsImage)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+    PVDIMAGE pImage = NULL;
+
+    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsImage=%#p\n",
+                 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsImage));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
+                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Set up image descriptor. */
+        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
+        if (!pImage)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pImage->pszFilename = RTStrDup(pszFilename);
+        if (!pImage->pszFilename)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pImage->pDisk       = pDisk;
+        pImage->pVDIfsImage = pVDIfsImage;
+
+        rc = vdFindBackend(pszBackend, &pImage->Backend);
+        if (RT_FAILURE(rc))
+            break;
+        if (!pImage->Backend)
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: unknown backend name '%s'"), pszBackend);
+            break;
+        }
+
+        /* Set up the I/O interface. */
+        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
+        if (pImage->pInterfaceIO)
+            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
+        else
+        {
+            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
+                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
+            pImage->pInterfaceIO = &pImage->VDIIO;
+            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
+        }
+
+        /* Set up the internal I/O interface. */
+        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
+                        rc = VERR_INVALID_PARAMETER);
+        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
+        AssertRC(rc);
+
+        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
+        rc = pImage->Backend->pfnOpen(pImage->pszFilename,
+                                      uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
+                                      pDisk->pVDIfsDisk,
+                                      pImage->pVDIfsImage,
+                                      pDisk->enmType,
+                                      &pImage->pBackendData);
+        /* If the open in read-write mode failed, retry in read-only mode. */
+        if (RT_FAILURE(rc))
+        {
+            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                &&  (   rc == VERR_ACCESS_DENIED
+                     || rc == VERR_PERMISSION_DENIED
+                     || rc == VERR_WRITE_PROTECT
+                     || rc == VERR_SHARING_VIOLATION
+                     || rc == VERR_FILE_LOCK_FAILED))
+                rc = pImage->Backend->pfnOpen(pImage->pszFilename,
+                                                (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
+                                               | VD_OPEN_FLAGS_READONLY,
+                                               pDisk->pVDIfsDisk,
+                                               pImage->pVDIfsImage,
+                                               pDisk->enmType,
+                                               &pImage->pBackendData);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdError(pDisk, rc, RT_SRC_POS,
+                             N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
+                break;
+            }
+        }
+
+        /* Lock disk for writing, as we modify pDisk information below. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        /* Check image type. As the image itself has only partial knowledge
+         * whether it's a base image or not, this info is derived here. The
+         * base image can be fixed or normal, all others must be normal or
+         * diff images. Some image formats don't distinguish between normal
+         * and diff images, so this must be corrected here. */
+        unsigned uImageFlags;
+        uImageFlags = pImage->Backend->pfnGetImageFlags(pImage->pBackendData);
+        if (RT_FAILURE(rc))
+            uImageFlags = VD_IMAGE_FLAGS_NONE;
+        if (    RT_SUCCESS(rc)
+            &&  !(uOpenFlags & VD_OPEN_FLAGS_INFO))
+        {
+            if (    pDisk->cImages == 0
+                &&  (uImageFlags & VD_IMAGE_FLAGS_DIFF))
+            {
+                rc = VERR_VD_INVALID_TYPE;
+                break;
+            }
+            else if (pDisk->cImages != 0)
+            {
+                if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+                {
+                    rc = VERR_VD_INVALID_TYPE;
+                    break;
+                }
+                else
+                    uImageFlags |= VD_IMAGE_FLAGS_DIFF;
+            }
+        }
+
+        /* Ensure we always get correct diff information, even if the backend
+         * doesn't actually have a stored flag for this. It must not return
+         * bogus information for the parent UUID if it is not a diff image. */
+        RTUUID parentUuid;
+        RTUuidClear(&parentUuid);
+        rc2 = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, &parentUuid);
+        if (RT_SUCCESS(rc2) && !RTUuidIsNull(&parentUuid))
+            uImageFlags |= VD_IMAGE_FLAGS_DIFF;
+
+        pImage->uImageFlags = uImageFlags;
+
+        /* Force sane optimization settings. It's not worth avoiding writes
+         * to fixed size images. The overhead would have almost no payback. */
+        if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+            pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
+
+        /** @todo optionally check UUIDs */
+
+        /* Cache disk information. */
+        pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
+
+        /* Cache PCHS geometry. */
+        rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                  &pDisk->PCHSGeometry);
+        if (RT_FAILURE(rc2))
+        {
+            pDisk->PCHSGeometry.cCylinders = 0;
+            pDisk->PCHSGeometry.cHeads = 0;
+            pDisk->PCHSGeometry.cSectors = 0;
+        }
+        else
+        {
+            /* Make sure the PCHS geometry is properly clipped. */
+            pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
+            pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
+            pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
+        }
+
+        /* Cache LCHS geometry. */
+        rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                  &pDisk->LCHSGeometry);
+        if (RT_FAILURE(rc2))
+        {
+            pDisk->LCHSGeometry.cCylinders = 0;
+            pDisk->LCHSGeometry.cHeads = 0;
+            pDisk->LCHSGeometry.cSectors = 0;
+        }
+        else
+        {
+            /* Make sure the LCHS geometry is properly clipped. */
+            pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
+            pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
+        }
+
+        if (pDisk->cImages != 0)
+        {
+            /* Switch previous image to read-only mode. */
+            unsigned uOpenFlagsPrevImg;
+            uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
+            if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
+            {
+                uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
+                rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
+            }
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Image successfully opened, make it the last image. */
+            vdAddImageToList(pDisk, pImage);
+            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
+        }
+        else
+        {
+            /* Error detected, but image opened. Close image. */
+            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
+            AssertRC(rc2);
+            pImage->pBackendData = NULL;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (pImage)
+        {
+            if (pImage->pszFilename)
+                RTStrFree(pImage->pszFilename);
+            RTMemFree(pImage);
+        }
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Opens a cache image.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Pointer to the HDD container which should use the cache image.
+ * @param   pszBackend      Name of the cache file backend to use (case insensitive).
+ * @param   pszFilename     Name of the cache image to open.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
+ */
+VBOXDDU_DECL(int) VDCacheOpen(PVBOXHDD pDisk, const char *pszBackend,
+                              const char *pszFilename, unsigned uOpenFlags,
+                              PVDINTERFACE pVDIfsCache)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+    PVDCACHE pCache = NULL;
+
+    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uOpenFlags=%#x, pVDIfsCache=%#p\n",
+                 pDisk, pszBackend, pszFilename, uOpenFlags, pVDIfsCache));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
+                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Set up image descriptor. */
+        pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
+        if (!pCache)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pCache->pszFilename = RTStrDup(pszFilename);
+        if (!pCache->pszFilename)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pCache->pDisk       = pDisk;
+        pCache->pVDIfsCache = pVDIfsCache;
+
+        rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
+        if (RT_FAILURE(rc))
+            break;
+        if (!pCache->Backend)
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: unknown backend name '%s'"), pszBackend);
+            break;
+        }
+
+        /* Set up the I/O interface. */
+        pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
+        if (pCache->pInterfaceIO)
+            pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
+        else
+        {
+            rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
+                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
+            pCache->pInterfaceIO = &pCache->VDIIO;
+            pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
+        }
+
+        /* Set up the internal I/O interface. */
+        AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
+                        rc = VERR_INVALID_PARAMETER);
+        rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                            &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
+        AssertRC(rc);
+
+        pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
+        rc = pCache->Backend->pfnOpen(pCache->pszFilename,
+                                      uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
+                                      pDisk->pVDIfsDisk,
+                                      pCache->pVDIfsCache,
+                                      &pCache->pBackendData);
+        /* If the open in read-write mode failed, retry in read-only mode. */
+        if (RT_FAILURE(rc))
+        {
+            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                &&  (   rc == VERR_ACCESS_DENIED
+                     || rc == VERR_PERMISSION_DENIED
+                     || rc == VERR_WRITE_PROTECT
+                     || rc == VERR_SHARING_VIOLATION
+                     || rc == VERR_FILE_LOCK_FAILED))
+                rc = pCache->Backend->pfnOpen(pCache->pszFilename,
+                                                (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
+                                               | VD_OPEN_FLAGS_READONLY,
+                                               pDisk->pVDIfsDisk,
+                                               pCache->pVDIfsCache,
+                                               &pCache->pBackendData);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdError(pDisk, rc, RT_SRC_POS,
+                             N_("VD: error %Rrc opening image file '%s'"), rc, pszFilename);
+                break;
+            }
+        }
+
+        /* Lock disk for writing, as we modify pDisk information below. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        /*
+         * Check that the modification UUID of the cache and last image
+         * match. If not the image was modified in-between without the cache.
+         * The cache might contain stale data.
+         */
+        RTUUID UuidImage, UuidCache;
+
+        rc = pCache->Backend->pfnGetModificationUuid(pCache->pBackendData,
+                                                     &UuidCache);
+        if (RT_SUCCESS(rc))
+        {
+            rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
+                                                               &UuidImage);
+            if (RT_SUCCESS(rc))
+            {
+                if (RTUuidCompare(&UuidImage, &UuidCache))
+                    rc = VERR_VD_CACHE_NOT_UP_TO_DATE;
+            }
+        }
+
+        /*
+         * We assume that the user knows what he is doing if one of the images
+         * doesn't support the modification uuid.
+         */
+        if (rc == VERR_NOT_SUPPORTED)
+            rc = VINF_SUCCESS;
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Cache successfully opened, make it the current one. */
+            if (!pDisk->pCache)
+                pDisk->pCache = pCache;
+            else
+                rc = VERR_VD_CACHE_ALREADY_EXISTS;
+        }
+
+        if (RT_FAILURE(rc))
+        {
+            /* Error detected, but image opened. Close image. */
+            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
+            AssertRC(rc2);
+            pCache->pBackendData = NULL;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (pCache)
+        {
+            if (pCache->pszFilename)
+                RTStrFree(pCache->pszFilename);
+            RTMemFree(pCache);
+        }
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Creates and opens a new base image file.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use.
+ * @param   pszFilename     Name of the image file to create.
+ * @param   cbSize          Image size in bytes.
+ * @param   uImageFlags     Flags specifying special image features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pPCHSGeometry   Pointer to physical disk geometry <= (16383,16,63). Not NULL.
+ * @param   pLCHSGeometry   Pointer to logical disk geometry <= (x,255,63). Not NULL.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszBackend,
+                               const char *pszFilename, uint64_t cbSize,
+                               unsigned uImageFlags, const char *pszComment,
+                               PCVDGEOMETRY pPCHSGeometry,
+                               PCVDGEOMETRY pLCHSGeometry,
+                               PCRTUUID pUuid, unsigned uOpenFlags,
+                               PVDINTERFACE pVDIfsImage,
+                               PVDINTERFACE pVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false, fLockRead = false;
+    PVDIMAGE pImage = NULL;
+    RTUUID uuid;
+
+    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
+                 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment,
+                 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
+                 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
+                 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, pUuid,
+                 uOpenFlags, pVDIfsImage, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
+                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbSize,
+                           ("cbSize=%llu\n", cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(   ((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0)
+                           || ((uImageFlags & (VD_IMAGE_FLAGS_FIXED | VD_IMAGE_FLAGS_DIFF)) != VD_IMAGE_FLAGS_FIXED),
+                           ("uImageFlags=%#x\n", uImageFlags),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The PCHS geometry fields may be 0 to leave it for later. */
+        AssertMsgBreakStmt(   VALID_PTR(pPCHSGeometry)
+                           && pPCHSGeometry->cHeads <= 16
+                           && pPCHSGeometry->cSectors <= 63,
+                           ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
+                            pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
+                            pPCHSGeometry->cSectors),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The LCHS geometry fields may be 0 to leave it to later autodetection. */
+        AssertMsgBreakStmt(   VALID_PTR(pLCHSGeometry)
+                           && pLCHSGeometry->cHeads <= 255
+                           && pLCHSGeometry->cSectors <= 63,
+                           ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
+                            pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
+                            pLCHSGeometry->cSectors),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The UUID may be NULL. */
+        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
+                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Check state. Needs a temporary read lock. Holding the write lock
+         * all the time would be blocking other activities for too long. */
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+        AssertMsgBreakStmt(pDisk->cImages == 0,
+                           ("Create base image cannot be done with other images open\n"),
+                           rc = VERR_VD_INVALID_STATE);
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = false;
+
+        /* Set up image descriptor. */
+        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
+        if (!pImage)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pImage->pszFilename = RTStrDup(pszFilename);
+        if (!pImage->pszFilename)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pImage->pDisk       = pDisk;
+        pImage->pVDIfsImage = pVDIfsImage;
+
+        /* Set up the I/O interface. */
+        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
+        if (pImage->pInterfaceIO)
+            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
+        else
+        {
+            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
+                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
+            pImage->pInterfaceIO = &pImage->VDIIO;
+            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
+        }
+
+        /* Set up the internal I/O interface. */
+        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
+                        rc = VERR_INVALID_PARAMETER);
+        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
+        AssertRC(rc);
+
+        rc = vdFindBackend(pszBackend, &pImage->Backend);
+        if (RT_FAILURE(rc))
+            break;
+        if (!pImage->Backend)
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: unknown backend name '%s'"), pszBackend);
+            break;
+        }
+        if (!(pImage->Backend->uBackendCaps & (  VD_CAP_CREATE_FIXED
+                                               | VD_CAP_CREATE_DYNAMIC)))
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: backend '%s' cannot create base images"), pszBackend);
+            break;
+        }
+
+        /* Create UUID if the caller didn't specify one. */
+        if (!pUuid)
+        {
+            rc = RTUuidCreate(&uuid);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdError(pDisk, rc, RT_SRC_POS,
+                             N_("VD: cannot generate UUID for image '%s'"),
+                             pszFilename);
+                break;
+            }
+            pUuid = &uuid;
+        }
+
+        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
+        uImageFlags &= ~VD_IMAGE_FLAGS_DIFF;
+        rc = pImage->Backend->pfnCreate(pImage->pszFilename, cbSize,
+                                        uImageFlags, pszComment, pPCHSGeometry,
+                                        pLCHSGeometry, pUuid,
+                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
+                                        0, 99,
+                                        pDisk->pVDIfsDisk,
+                                        pImage->pVDIfsImage,
+                                        pVDIfsOperation,
+                                        &pImage->pBackendData);
+
+        if (RT_SUCCESS(rc))
+        {
+            pImage->uImageFlags = uImageFlags;
+
+            /* Force sane optimization settings. It's not worth avoiding writes
+             * to fixed size images. The overhead would have almost no payback. */
+            if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+                pImage->uOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
+
+            /* Lock disk for writing, as we modify pDisk information below. */
+            rc2 = vdThreadStartWrite(pDisk);
+            AssertRC(rc2);
+            fLockWrite = true;
+
+            /** @todo optionally check UUIDs */
+
+            /* Re-check state, as the lock wasn't held and another image
+             * creation call could have been done by another thread. */
+            AssertMsgStmt(pDisk->cImages == 0,
+                          ("Create base image cannot be done with other images open\n"),
+                          rc = VERR_VD_INVALID_STATE);
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Cache disk information. */
+            pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
+
+            /* Cache PCHS geometry. */
+            rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                      &pDisk->PCHSGeometry);
+            if (RT_FAILURE(rc2))
+            {
+                pDisk->PCHSGeometry.cCylinders = 0;
+                pDisk->PCHSGeometry.cHeads = 0;
+                pDisk->PCHSGeometry.cSectors = 0;
+            }
+            else
+            {
+                /* Make sure the CHS geometry is properly clipped. */
+                pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
+                pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
+                pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
+            }
+
+            /* Cache LCHS geometry. */
+            rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                      &pDisk->LCHSGeometry);
+            if (RT_FAILURE(rc2))
+            {
+                pDisk->LCHSGeometry.cCylinders = 0;
+                pDisk->LCHSGeometry.cHeads = 0;
+                pDisk->LCHSGeometry.cSectors = 0;
+            }
+            else
+            {
+                /* Make sure the CHS geometry is properly clipped. */
+                pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
+                pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
+            }
+
+            /* Image successfully opened, make it the last image. */
+            vdAddImageToList(pDisk, pImage);
+            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
+        }
+        else
+        {
+            /* Error detected, image may or may not be opened. Close and delete
+             * image if it was opened. */
+            if (pImage->pBackendData)
+            {
+                rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
+                AssertRC(rc2);
+                pImage->pBackendData = NULL;
+            }
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (pImage)
+        {
+            if (pImage->pszFilename)
+                RTStrFree(pImage->pszFilename);
+            RTMemFree(pImage);
+        }
+    }
+
+    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
+        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Creates and opens a new differencing image file in HDD container.
+ * See comments for VDOpen function about differencing images.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   pszBackend      Name of the image file backend to use.
+ * @param   pszFilename     Name of the differencing image file to create.
+ * @param   uImageFlags     Flags specifying special image features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   pParentUuid     New parent UUID of the image. If NULL, the UUID is queried automatically.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsImage     Pointer to the per-image VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszBackend,
+                               const char *pszFilename, unsigned uImageFlags,
+                               const char *pszComment, PCRTUUID pUuid,
+                               PCRTUUID pParentUuid, unsigned uOpenFlags,
+                               PVDINTERFACE pVDIfsImage,
+                               PVDINTERFACE pVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false, fLockRead = false;
+    PVDIMAGE pImage = NULL;
+    RTUUID uuid;
+
+    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
+                 pDisk, pszBackend, pszFilename, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsImage, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
+                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
+                           ("uImageFlags=%#x\n", uImageFlags),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The UUID may be NULL. */
+        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
+                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The parent UUID may be NULL. */
+        AssertMsgBreakStmt(pParentUuid == NULL || VALID_PTR(pParentUuid),
+                           ("pParentUuid=%#p ParentUUID=%RTuuid\n", pParentUuid, pParentUuid),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Check state. Needs a temporary read lock. Holding the write lock
+         * all the time would be blocking other activities for too long. */
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+        AssertMsgBreakStmt(pDisk->cImages != 0,
+                           ("Create diff image cannot be done without other images open\n"),
+                           rc = VERR_VD_INVALID_STATE);
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = false;
+
+        /* Set up image descriptor. */
+        pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
+        if (!pImage)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pImage->pszFilename = RTStrDup(pszFilename);
+        if (!pImage->pszFilename)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = vdFindBackend(pszBackend, &pImage->Backend);
+        if (RT_FAILURE(rc))
+            break;
+        if (!pImage->Backend)
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: unknown backend name '%s'"), pszBackend);
+            break;
+        }
+        if (   !(pImage->Backend->uBackendCaps & VD_CAP_DIFF)
+            || !(pImage->Backend->uBackendCaps & (  VD_CAP_CREATE_FIXED
+                                                  | VD_CAP_CREATE_DYNAMIC)))
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: backend '%s' cannot create diff images"), pszBackend);
+            break;
+        }
+
+        pImage->pDisk       = pDisk;
+        pImage->pVDIfsImage = pVDIfsImage;
+
+        /* Set up the I/O interface. */
+        pImage->pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IO);
+        if (pImage->pInterfaceIO)
+            pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO);
+        else
+        {
+            rc = VDInterfaceAdd(&pImage->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
+                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsImage);
+            pImage->pInterfaceIO = &pImage->VDIIO;
+            pImage->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
+        }
+
+        /* Set up the internal I/O interface. */
+        AssertBreakStmt(!VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT),
+                        rc = VERR_INVALID_PARAMETER);
+        rc = VDInterfaceAdd(&pImage->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                            &pDisk->VDIIOIntCallbacks, pImage, &pImage->pVDIfsImage);
+        AssertRC(rc);
+
+        /* Create UUID if the caller didn't specify one. */
+        if (!pUuid)
+        {
+            rc = RTUuidCreate(&uuid);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdError(pDisk, rc, RT_SRC_POS,
+                             N_("VD: cannot generate UUID for image '%s'"),
+                             pszFilename);
+                break;
+            }
+            pUuid = &uuid;
+        }
+
+        pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
+        uImageFlags |= VD_IMAGE_FLAGS_DIFF;
+        rc = pImage->Backend->pfnCreate(pImage->pszFilename, pDisk->cbSize,
+                                        uImageFlags | VD_IMAGE_FLAGS_DIFF,
+                                        pszComment, &pDisk->PCHSGeometry,
+                                        &pDisk->LCHSGeometry, pUuid,
+                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
+                                        0, 99,
+                                        pDisk->pVDIfsDisk,
+                                        pImage->pVDIfsImage,
+                                        pVDIfsOperation,
+                                        &pImage->pBackendData);
+
+        if (RT_SUCCESS(rc))
+        {
+            pImage->uImageFlags = uImageFlags;
+
+            /* Lock disk for writing, as we modify pDisk information below. */
+            rc2 = vdThreadStartWrite(pDisk);
+            AssertRC(rc2);
+            fLockWrite = true;
+
+            /* Switch previous image to read-only mode. */
+            unsigned uOpenFlagsPrevImg;
+            uOpenFlagsPrevImg = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
+            if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
+            {
+                uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
+                rc = pDisk->pLast->Backend->pfnSetOpenFlags(pDisk->pLast->pBackendData, uOpenFlagsPrevImg);
+            }
+
+            /** @todo optionally check UUIDs */
+
+            /* Re-check state, as the lock wasn't held and another image
+             * creation call could have been done by another thread. */
+            AssertMsgStmt(pDisk->cImages != 0,
+                          ("Create diff image cannot be done without other images open\n"),
+                          rc = VERR_VD_INVALID_STATE);
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            RTUUID Uuid;
+            RTTIMESPEC ts;
+
+            if (pParentUuid && !RTUuidIsNull(pParentUuid))
+            {
+                Uuid = *pParentUuid;
+                pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
+            }
+            else
+            {
+                rc2 = pDisk->pLast->Backend->pfnGetUuid(pDisk->pLast->pBackendData,
+                                                        &Uuid);
+                if (RT_SUCCESS(rc2))
+                    pImage->Backend->pfnSetParentUuid(pImage->pBackendData, &Uuid);
+            }
+            rc2 = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
+                                                                &Uuid);
+            if (RT_SUCCESS(rc2))
+                pImage->Backend->pfnSetParentModificationUuid(pImage->pBackendData,
+                                                              &Uuid);
+            if (pDisk->pLast->Backend->pfnGetTimeStamp)
+                rc2 = pDisk->pLast->Backend->pfnGetTimeStamp(pDisk->pLast->pBackendData,
+                                                             &ts);
+            else
+                rc2 = VERR_NOT_IMPLEMENTED;
+            if (RT_SUCCESS(rc2) && pImage->Backend->pfnSetParentTimeStamp)
+                pImage->Backend->pfnSetParentTimeStamp(pImage->pBackendData, &ts);
+
+            if (pImage->Backend->pfnSetParentFilename)
+                rc2 = pImage->Backend->pfnSetParentFilename(pImage->pBackendData, pDisk->pLast->pszFilename);
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Image successfully opened, make it the last image. */
+            vdAddImageToList(pDisk, pImage);
+            if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+                pDisk->uModified = VD_IMAGE_MODIFIED_FIRST;
+        }
+        else
+        {
+            /* Error detected, but image opened. Close and delete image. */
+            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, true);
+            AssertRC(rc2);
+            pImage->pBackendData = NULL;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (pImage)
+        {
+            if (pImage->pszFilename)
+                RTStrFree(pImage->pszFilename);
+            RTMemFree(pImage);
+        }
+    }
+
+    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
+        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Creates and opens new cache image file in HDD container.
+ *
+ * @return  VBox status code.
+ * @param   pDisk           Name of the cache file backend to use (case insensitive).
+ * @param   pszFilename     Name of the differencing cache file to create.
+ * @param   cbSize          Maximum size of the cache.
+ * @param   uImageFlags     Flags specifying special cache features.
+ * @param   pszComment      Pointer to image comment. NULL is ok.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ * @param   pVDIfsCache     Pointer to the per-cache VD interface list.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCreateCache(PVBOXHDD pDisk, const char *pszBackend,
+                                const char *pszFilename, uint64_t cbSize,
+                                unsigned uImageFlags, const char *pszComment,
+                                PCRTUUID pUuid, unsigned uOpenFlags,
+                                PVDINTERFACE pVDIfsCache, PVDINTERFACE pVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false, fLockRead = false;
+    PVDCACHE pCache = NULL;
+    RTUUID uuid;
+
+    LogFlowFunc(("pDisk=%#p pszBackend=\"%s\" pszFilename=\"%s\" cbSIze=%llu uImageFlags=%#x pszComment=\"%s\" Uuid=%RTuuid uOpenFlags=%#x pVDIfsImage=%#p pVDIfsOperation=%#p\n",
+                 pDisk, pszBackend, pszFilename, cbSize, uImageFlags, pszComment, pUuid, uOpenFlags, pVDIfsCache, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszBackend) && *pszBackend,
+                           ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbSize,
+                           ("cbSize=%llu\n", cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
+                           ("uImageFlags=%#x\n", uImageFlags),
+                           rc = VERR_INVALID_PARAMETER);
+        /* The UUID may be NULL. */
+        AssertMsgBreakStmt(pUuid == NULL || VALID_PTR(pUuid),
+                           ("pUuid=%#p UUID=%RTuuid\n", pUuid, pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        /* Check state. Needs a temporary read lock. Holding the write lock
+         * all the time would be blocking other activities for too long. */
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+        AssertMsgBreakStmt(pDisk->cImages != 0,
+                           ("Create diff image cannot be done without other images open\n"),
+                           rc = VERR_VD_INVALID_STATE);
+        AssertMsgBreakStmt(!pDisk->pCache,
+                           ("Create cache image cannot be done with a cache already attached\n"),
+                           rc = VERR_VD_CACHE_ALREADY_EXISTS);
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = false;
+
+        /* Set up image descriptor. */
+        pCache = (PVDCACHE)RTMemAllocZ(sizeof(VDCACHE));
+        if (!pCache)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+        pCache->pszFilename = RTStrDup(pszFilename);
+        if (!pCache->pszFilename)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        rc = vdFindCacheBackend(pszBackend, &pCache->Backend);
+        if (RT_FAILURE(rc))
+            break;
+        if (!pCache->Backend)
+        {
+            rc = vdError(pDisk, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                         N_("VD: unknown backend name '%s'"), pszBackend);
+            break;
+        }
+
+        pCache->pDisk       = pDisk;
+        pCache->pVDIfsCache = pVDIfsCache;
+
+        /* Set up the I/O interface. */
+        pCache->pInterfaceIO = VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IO);
+        if (pCache->pInterfaceIO)
+            pCache->pInterfaceIOCallbacks = VDGetInterfaceIO(pCache->pInterfaceIO);
+        else
+        {
+            rc = VDInterfaceAdd(&pCache->VDIIO, "VD_IO", VDINTERFACETYPE_IO,
+                                &pDisk->VDIIOCallbacks, pDisk, &pVDIfsCache);
+            pCache->pInterfaceIO = &pCache->VDIIO;
+            pCache->pInterfaceIOCallbacks = &pDisk->VDIIOCallbacks;
+        }
+
+        /* Set up the internal I/O interface. */
+        AssertBreakStmt(!VDInterfaceGet(pVDIfsCache, VDINTERFACETYPE_IOINT),
+                        rc = VERR_INVALID_PARAMETER);
+        rc = VDInterfaceAdd(&pCache->VDIIOInt, "VD_IOINT", VDINTERFACETYPE_IOINT,
+                            &pDisk->VDIIOIntCallbacks, pCache, &pCache->pVDIfsCache);
+        AssertRC(rc);
+
+        /* Create UUID if the caller didn't specify one. */
+        if (!pUuid)
+        {
+            rc = RTUuidCreate(&uuid);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdError(pDisk, rc, RT_SRC_POS,
+                             N_("VD: cannot generate UUID for image '%s'"),
+                             pszFilename);
+                break;
+            }
+            pUuid = &uuid;
+        }
+
+        pCache->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
+        rc = pCache->Backend->pfnCreate(pCache->pszFilename, cbSize,
+                                        uImageFlags,
+                                        pszComment, pUuid,
+                                        uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
+                                        0, 99,
+                                        pDisk->pVDIfsDisk,
+                                        pCache->pVDIfsCache,
+                                        pVDIfsOperation,
+                                        &pCache->pBackendData);
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Lock disk for writing, as we modify pDisk information below. */
+            rc2 = vdThreadStartWrite(pDisk);
+            AssertRC(rc2);
+            fLockWrite = true;
+
+            /* Re-check state, as the lock wasn't held and another image
+             * creation call could have been done by another thread. */
+            AssertMsgStmt(!pDisk->pCache,
+                          ("Create cache image cannot be done with another cache open\n"),
+                          rc = VERR_VD_CACHE_ALREADY_EXISTS);
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            RTUUID UuidModification;
+
+            /* Set same modification Uuid as the last image. */
+            rc = pDisk->pLast->Backend->pfnGetModificationUuid(pDisk->pLast->pBackendData,
+                                                               &UuidModification);
+            if (RT_SUCCESS(rc))
+            {
+                rc = pCache->Backend->pfnSetModificationUuid(pCache->pBackendData,
+                                                             &UuidModification);
+            }
+
+            if (rc == VERR_NOT_SUPPORTED)
+                rc = VINF_SUCCESS;
+        }
+
+        if (RT_SUCCESS(rc))
+        {
+            /* Cache successfully created. */
+            pDisk->pCache = pCache;
+        }
+        else
+        {
+            /* Error detected, but image opened. Close and delete image. */
+            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, true);
+            AssertRC(rc2);
+            pCache->pBackendData = NULL;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        if (pCache)
+        {
+            if (pCache->pszFilename)
+                RTStrFree(pCache->pszFilename);
+            RTMemFree(pCache);
+        }
+    }
+
+    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
+        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Merges two images (not necessarily with direct parent/child relationship).
+ * As a side effect the source image and potentially the other images which
+ * are also merged to the destination are deleted from both the disk and the
+ * images in the HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImageFrom      Name of the image file to merge from.
+ * @param   nImageTo        Name of the image file to merge to.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
+                          unsigned nImageTo, PVDINTERFACE pVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false, fLockRead = false;
+    void *pvBuf = NULL;
+
+    LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pVDIfsOperation=%#p\n",
+                 pDisk, nImageFrom, nImageTo, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* For simplicity reasons lock for writing as the image reopen below
+         * might need it. After all the reopen is usually needed. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+        PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
+        PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
+        if (!pImageFrom || !pImageTo)
+        {
+            rc = VERR_VD_IMAGE_NOT_FOUND;
+            break;
+        }
+        AssertBreakStmt(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
+
+        /* Make sure destination image is writable. */
+        unsigned uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
+            rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
+                                                    uOpenFlags);
+            if (RT_FAILURE(rc))
+                break;
+        }
+
+        /* Get size of destination image. */
+        uint64_t cbSize = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = false;
+
+        /* Allocate tmp buffer. */
+        pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
+        if (!pvBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        /* Merging is done directly on the images itself. This potentially
+         * causes trouble if the disk is full in the middle of operation. */
+        if (nImageFrom < nImageTo)
+        {
+            /* Merge parent state into child. This means writing all not
+             * allocated blocks in the destination image which are allocated in
+             * the images to be merged. */
+            uint64_t uOffset = 0;
+            uint64_t cbRemaining = cbSize;
+            do
+            {
+                size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
+
+                /* Need to hold the write lock during a read-write operation. */
+                rc2 = vdThreadStartWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = true;
+
+                rc = pImageTo->Backend->pfnRead(pImageTo->pBackendData,
+                                                uOffset, pvBuf, cbThisRead,
+                                                &cbThisRead);
+                if (rc == VERR_VD_BLOCK_FREE)
+                {
+                    /* Search for image with allocated block. Do not attempt to
+                     * read more than the previous reads marked as valid.
+                     * Otherwise this would return stale data when different
+                     * block sizes are used for the images. */
+                    for (PVDIMAGE pCurrImage = pImageTo->pPrev;
+                         pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VERR_VD_BLOCK_FREE;
+                         pCurrImage = pCurrImage->pPrev)
+                    {
+                        rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+                                                          uOffset, pvBuf,
+                                                          cbThisRead,
+                                                          &cbThisRead);
+                    }
+
+                    if (rc != VERR_VD_BLOCK_FREE)
+                    {
+                        if (RT_FAILURE(rc))
+                            break;
+                        /* Updating the cache is required because this might be a live merge. */
+                        rc = vdWriteHelper(pDisk, pImageTo, pImageFrom->pPrev,
+                                           uOffset, pvBuf, cbThisRead,
+                                           true /* fUpdateCache */);
+                        if (RT_FAILURE(rc))
+                            break;
+                    }
+                    else
+                        rc = VINF_SUCCESS;
+                }
+                else if (RT_FAILURE(rc))
+                    break;
+
+                rc2 = vdThreadFinishWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = false;
+
+                uOffset += cbThisRead;
+                cbRemaining -= cbThisRead;
+
+                if (pCbProgress && pCbProgress->pfnProgress)
+                {
+                    /** @todo r=klaus: this can update the progress to the same
+                     * percentage over and over again if the image format makes
+                     * relatively small increments. */
+                    rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                                  uOffset * 99 / cbSize);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            } while (uOffset < cbSize);
+        }
+        else
+        {
+            /*
+             * We may need to update the parent uuid of the child coming after
+             * the last image to be merged. We have to reopen it read/write.
+             *
+             * This is done before we do the actual merge to prevent an
+             * inconsistent chain if the mode change fails for some reason.
+             */
+            if (pImageFrom->pNext)
+            {
+                PVDIMAGE pImageChild = pImageFrom->pNext;
+
+                /* Take the write lock. */
+                rc2 = vdThreadStartWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = true;
+
+                /* We need to open the image in read/write mode. */
+                uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
+
+                if (uOpenFlags  & VD_OPEN_FLAGS_READONLY)
+                {
+                    uOpenFlags  &= ~VD_OPEN_FLAGS_READONLY;
+                    rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
+                                                               uOpenFlags);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+
+                rc2 = vdThreadFinishWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = false;
+            }
+
+            /* If the merge is from the last image we have to relay all writes
+             * to the merge destination as well, so that concurrent writes
+             * (in case of a live merge) are handled correctly. */
+            if (!pImageFrom->pNext)
+            {
+                /* Take the write lock. */
+                rc2 = vdThreadStartWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = true;
+
+                pDisk->pImageRelay = pImageTo;
+
+                rc2 = vdThreadFinishWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = false;
+            }
+
+            /* Merge child state into parent. This means writing all blocks
+             * which are allocated in the image up to the source image to the
+             * destination image. */
+            uint64_t uOffset = 0;
+            uint64_t cbRemaining = cbSize;
+            do
+            {
+                size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
+                rc = VERR_VD_BLOCK_FREE;
+
+                /* Need to hold the write lock during a read-write operation. */
+                rc2 = vdThreadStartWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = true;
+
+                /* Search for image with allocated block. Do not attempt to
+                 * read more than the previous reads marked as valid. Otherwise
+                 * this would return stale data when different block sizes are
+                 * used for the images. */
+                for (PVDIMAGE pCurrImage = pImageFrom;
+                     pCurrImage != NULL && pCurrImage != pImageTo && rc == VERR_VD_BLOCK_FREE;
+                     pCurrImage = pCurrImage->pPrev)
+                {
+                    rc = pCurrImage->Backend->pfnRead(pCurrImage->pBackendData,
+                                                      uOffset, pvBuf,
+                                                      cbThisRead, &cbThisRead);
+                }
+
+                if (rc != VERR_VD_BLOCK_FREE)
+                {
+                    if (RT_FAILURE(rc))
+                        break;
+                    rc = vdWriteHelper(pDisk, pImageTo, NULL, uOffset, pvBuf,
+                                       cbThisRead, true /* fUpdateCache */);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+                else
+                    rc = VINF_SUCCESS;
+
+                rc2 = vdThreadFinishWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = false;
+
+                uOffset += cbThisRead;
+                cbRemaining -= cbThisRead;
+
+                if (pCbProgress && pCbProgress->pfnProgress)
+                {
+                    /** @todo r=klaus: this can update the progress to the same
+                     * percentage over and over again if the image format makes
+                     * relatively small increments. */
+                    rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                                  uOffset * 99 / cbSize);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            } while (uOffset < cbSize);
+
+            /* In case we set up a "write proxy" image above we must clear
+             * this again now to prevent stray writes. Failure or not. */
+            if (!pImageFrom->pNext)
+            {
+                /* Take the write lock. */
+                rc2 = vdThreadStartWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = true;
+
+                pDisk->pImageRelay = NULL;
+
+                rc2 = vdThreadFinishWrite(pDisk);
+                AssertRC(rc2);
+                fLockWrite = false;
+            }
+        }
+
+        /*
+         * Leave in case of an error to avoid corrupted data in the image chain
+         * (includes cancelling the operation by the user).
+         */
+        if (RT_FAILURE(rc))
+            break;
+
+        /* Need to hold the write lock while finishing the merge. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        /* Update parent UUID so that image chain is consistent. */
+        RTUUID Uuid;
+        PVDIMAGE pImageChild = NULL;
+        if (nImageFrom < nImageTo)
+        {
+            if (pImageFrom->pPrev)
+            {
+                rc = pImageFrom->pPrev->Backend->pfnGetUuid(pImageFrom->pPrev->pBackendData,
+                                                            &Uuid);
+                AssertRC(rc);
+            }
+            else
+                RTUuidClear(&Uuid);
+            rc = pImageTo->Backend->pfnSetParentUuid(pImageTo->pBackendData,
+                                                     &Uuid);
+            AssertRC(rc);
+        }
+        else
+        {
+            /* Update the parent uuid of the child of the last merged image. */
+            if (pImageFrom->pNext)
+            {
+                rc = pImageTo->Backend->pfnGetUuid(pImageTo->pBackendData,
+                                                   &Uuid);
+                AssertRC(rc);
+
+                rc = pImageFrom->Backend->pfnSetParentUuid(pImageFrom->pNext->pBackendData,
+                                                           &Uuid);
+                AssertRC(rc);
+
+                pImageChild = pImageFrom->pNext;
+            }
+        }
+
+        /* Delete the no longer needed images. */
+        PVDIMAGE pImg = pImageFrom, pTmp;
+        while (pImg != pImageTo)
+        {
+            if (nImageFrom < nImageTo)
+                pTmp = pImg->pNext;
+            else
+                pTmp = pImg->pPrev;
+            vdRemoveImageFromList(pDisk, pImg);
+            pImg->Backend->pfnClose(pImg->pBackendData, true);
+            RTMemFree(pImg->pszFilename);
+            RTMemFree(pImg);
+            pImg = pTmp;
+        }
+
+        /* Make sure destination image is back to read only if necessary. */
+        if (pImageTo != pDisk->pLast)
+        {
+            uOpenFlags = pImageTo->Backend->pfnGetOpenFlags(pImageTo->pBackendData);
+            uOpenFlags |= VD_OPEN_FLAGS_READONLY;
+            rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
+                                                    uOpenFlags);
+            if (RT_FAILURE(rc))
+                break;
+        }
+
+        /*
+         * Make sure the child is readonly
+         * for the child -> parent merge direction
+         * if necessary.
+        */
+        if (   nImageFrom > nImageTo
+            && pImageChild
+            && pImageChild != pDisk->pLast)
+        {
+            uOpenFlags = pImageChild->Backend->pfnGetOpenFlags(pImageChild->pBackendData);
+            uOpenFlags |= VD_OPEN_FLAGS_READONLY;
+            rc = pImageChild->Backend->pfnSetOpenFlags(pImageChild->pBackendData,
+                                                       uOpenFlags);
+            if (RT_FAILURE(rc))
+                break;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+
+    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
+        pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Copies an image from one HDD container to another.
+ * The copy is opened in the target HDD container.
+ * It is possible to convert between different image formats, because the
+ * backend for the destination may be different from the source.
+ * If both the source and destination reference the same HDD container,
+ * then the image is moved (by copying/deleting or renaming) to the new location.
+ * The source container is unchanged if the move operation fails, otherwise
+ * the image at the new location is opened in the same way as the old one was.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDiskFrom       Pointer to source HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pDiskTo         Pointer to destination HDD container.
+ * @param   pszBackend      Name of the image file backend to use.
+ * @param   pszFilename     New name of the image (may be NULL if pDiskFrom == pDiskTo).
+ * @param   fMoveByRename   If true, attempt to perform a move by renaming (if successful the new size is ignored).
+ * @param   cbSize          New image size (0 means leave unchanged).
+ * @param   uImageFlags     Flags specifying special destination image features.
+ * @param   pDstUuid        New UUID of the destination image. If NULL, a new UUID is created.
+ *                          This parameter is used if and only if a true copy is created.
+ *                          In all rename/move cases the UUIDs are copied over.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ *                          Only used if the destination image is created.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ * @param   pDstVDIfsImage  Pointer to the per-image VD interface list, for the
+ *                          destination image.
+ * @param   pDstVDIfsOperation Pointer to the per-image VD interface list,
+ *                          for the destination image.
+ */
+VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
+                         const char *pszBackend, const char *pszFilename,
+                         bool fMoveByRename, uint64_t cbSize,
+                         unsigned uImageFlags, PCRTUUID pDstUuid,
+                         unsigned uOpenFlags, PVDINTERFACE pVDIfsOperation,
+                         PVDINTERFACE pDstVDIfsImage,
+                         PVDINTERFACE pDstVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockReadFrom = false, fLockWriteFrom = false, fLockWriteTo = false;
+    void *pvBuf = NULL;
+    PVDIMAGE pImageTo = NULL;
+
+    LogFlowFunc(("pDiskFrom=%#p nImage=%u pDiskTo=%#p pszBackend=\"%s\" pszFilename=\"%s\" fMoveByRename=%d cbSize=%llu uImageFlags=%#x pDstUuid=%#p uOpenFlags=%#x pVDIfsOperation=%#p pDstVDIfsImage=%#p pDstVDIfsOperation=%#p\n",
+                 pDiskFrom, nImage, pDiskTo, pszBackend, pszFilename, fMoveByRename, cbSize, uImageFlags, pDstUuid, uOpenFlags, pVDIfsOperation, pDstVDIfsImage, pDstVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    PVDINTERFACE pDstIfProgress = VDInterfaceGet(pDstVDIfsOperation,
+                                                 VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pDstCbProgress = NULL;
+    if (pDstIfProgress)
+        pDstCbProgress = VDGetInterfaceProgress(pDstIfProgress);
+
+    do {
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pDiskFrom), ("pDiskFrom=%#p\n", pDiskFrom),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDiskFrom->u32Signature == VBOXHDDDISK_SIGNATURE,
+                  ("u32Signature=%08x\n", pDiskFrom->u32Signature));
+
+        rc2 = vdThreadStartRead(pDiskFrom);
+        AssertRC(rc2);
+        fLockReadFrom = true;
+        PVDIMAGE pImageFrom = vdGetImageByNumber(pDiskFrom, nImage);
+        AssertPtrBreakStmt(pImageFrom, rc = VERR_VD_IMAGE_NOT_FOUND);
+        AssertMsgBreakStmt(VALID_PTR(pDiskTo), ("pDiskTo=%#p\n", pDiskTo),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDiskTo->u32Signature == VBOXHDDDISK_SIGNATURE,
+                  ("u32Signature=%08x\n", pDiskTo->u32Signature));
+
+        /* Move the image. */
+        if (pDiskFrom == pDiskTo)
+        {
+            /* Rename only works when backends are the same, are file based
+             * and the rename method is implemented. */
+            if (    fMoveByRename
+                &&  !RTStrICmp(pszBackend, pImageFrom->Backend->pszBackendName)
+                &&  pImageFrom->Backend->uBackendCaps & VD_CAP_FILE
+                &&  pImageFrom->Backend->pfnRename)
+            {
+                rc2 = vdThreadFinishRead(pDiskFrom);
+                AssertRC(rc2);
+                fLockReadFrom = false;
+
+                rc2 = vdThreadStartWrite(pDiskFrom);
+                AssertRC(rc2);
+                fLockWriteFrom = true;
+                rc = pImageFrom->Backend->pfnRename(pImageFrom->pBackendData, pszFilename ? pszFilename : pImageFrom->pszFilename);
+                break;
+            }
+
+            /** @todo Moving (including shrinking/growing) of the image is
+             * requested, but the rename attempt failed or it wasn't possible.
+             * Must now copy image to temp location. */
+            AssertReleaseMsgFailed(("VDCopy: moving by copy/delete not implemented\n"));
+        }
+
+        /* pszFilename is allowed to be NULL, as this indicates copy to the existing image. */
+        AssertMsgBreakStmt(pszFilename == NULL || (VALID_PTR(pszFilename) && *pszFilename),
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+
+        uint64_t cbSizeFrom;
+        cbSizeFrom = pImageFrom->Backend->pfnGetSize(pImageFrom->pBackendData);
+        if (cbSizeFrom == 0)
+        {
+            rc = VERR_VD_VALUE_NOT_FOUND;
+            break;
+        }
+
+        VDGEOMETRY PCHSGeometryFrom = {0, 0, 0};
+        VDGEOMETRY LCHSGeometryFrom = {0, 0, 0};
+        pImageFrom->Backend->pfnGetPCHSGeometry(pImageFrom->pBackendData, &PCHSGeometryFrom);
+        pImageFrom->Backend->pfnGetLCHSGeometry(pImageFrom->pBackendData, &LCHSGeometryFrom);
+
+        RTUUID ImageUuid, ImageModificationUuid;
+        if (pDiskFrom != pDiskTo)
+        {
+            if (pDstUuid)
+                ImageUuid = *pDstUuid;
+            else
+                RTUuidCreate(&ImageUuid);
+        }
+        else
+        {
+            rc = pImageFrom->Backend->pfnGetUuid(pImageFrom->pBackendData, &ImageUuid);
+            if (RT_FAILURE(rc))
+                RTUuidCreate(&ImageUuid);
+        }
+        rc = pImageFrom->Backend->pfnGetModificationUuid(pImageFrom->pBackendData, &ImageModificationUuid);
+        if (RT_FAILURE(rc))
+            RTUuidClear(&ImageModificationUuid);
+
+        char szComment[1024];
+        rc = pImageFrom->Backend->pfnGetComment(pImageFrom->pBackendData, szComment, sizeof(szComment));
+        if (RT_FAILURE(rc))
+            szComment[0] = '\0';
+        else
+            szComment[sizeof(szComment) - 1] = '\0';
+
+        rc2 = vdThreadFinishRead(pDiskFrom);
+        AssertRC(rc2);
+        fLockReadFrom = false;
+
+        rc2 = vdThreadStartRead(pDiskTo);
+        AssertRC(rc2);
+        unsigned cImagesTo = pDiskTo->cImages;
+        rc2 = vdThreadFinishRead(pDiskTo);
+        AssertRC(rc2);
+
+        if (pszFilename)
+        {
+            if (cbSize == 0)
+                cbSize = cbSizeFrom;
+
+            /* Create destination image with the properties of source image. */
+            /** @todo replace the VDCreateDiff/VDCreateBase calls by direct
+             * calls to the backend. Unifies the code and reduces the API
+             * dependencies. Would also make the synchronization explicit. */
+            if (cImagesTo > 0)
+            {
+                rc = VDCreateDiff(pDiskTo, pszBackend, pszFilename,
+                                  uImageFlags, szComment, &ImageUuid,
+                                  NULL /* pParentUuid */,
+                                  uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                  pDstVDIfsImage, NULL);
+
+                rc2 = vdThreadStartWrite(pDiskTo);
+                AssertRC(rc2);
+                fLockWriteTo = true;
+            } else {
+                /** @todo hack to force creation of a fixed image for
+                 * the RAW backend, which can't handle anything else. */
+                if (!RTStrICmp(pszBackend, "RAW"))
+                    uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+
+                /* Fix broken PCHS geometry. Can happen for two reasons: either
+                 * the backend mixes up PCHS and LCHS, or the application used
+                 * to create the source image has put garbage in it. */
+                /** @todo double-check if the VHD backend correctly handles
+                 * PCHS and LCHS geometry. also reconsider our current paranoia
+                 * level when it comes to geometry settings here and in the
+                 * backends. */
+                if (PCHSGeometryFrom.cHeads > 16 || PCHSGeometryFrom.cSectors > 63)
+                {
+                    Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) - (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383));
+                    PCHSGeometryFrom.cCylinders = (uint32_t)RT_MIN(cbSize / 512 / 16 / 63, 16383);
+                    PCHSGeometryFrom.cHeads = 16;
+                    PCHSGeometryFrom.cSectors = 63;
+                }
+
+                rc = VDCreateBase(pDiskTo, pszBackend, pszFilename, cbSize,
+                                  uImageFlags, szComment,
+                                  &PCHSGeometryFrom, &LCHSGeometryFrom,
+                                  NULL, uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                  pDstVDIfsImage, NULL);
+
+                rc2 = vdThreadStartWrite(pDiskTo);
+                AssertRC(rc2);
+                fLockWriteTo = true;
+
+                if (RT_SUCCESS(rc) && !RTUuidIsNull(&ImageUuid))
+                     pDiskTo->pLast->Backend->pfnSetUuid(pDiskTo->pLast->pBackendData, &ImageUuid);
+            }
+            if (RT_FAILURE(rc))
+                break;
+
+            pImageTo = pDiskTo->pLast;
+            AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+            cbSize = RT_MIN(cbSize, cbSizeFrom);
+        }
+        else
+        {
+            pImageTo = pDiskTo->pLast;
+            AssertPtrBreakStmt(pImageTo, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+            uint64_t cbSizeTo;
+            cbSizeTo = pImageTo->Backend->pfnGetSize(pImageTo->pBackendData);
+            if (cbSizeTo == 0)
+            {
+                rc = VERR_VD_VALUE_NOT_FOUND;
+                break;
+            }
+
+            if (cbSize == 0)
+                cbSize = RT_MIN(cbSizeFrom, cbSizeTo);
+        }
+
+        rc2 = vdThreadFinishWrite(pDiskTo);
+        AssertRC(rc2);
+        fLockWriteTo = false;
+
+        /* Allocate tmp buffer. */
+        pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
+        if (!pvBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        /* Whether we can take the optimized copy path (false) or not.
+         * Don't optimize if the image existed or if it is a child image. */
+        bool fRegularRead = (pszFilename == NULL) || (cImagesTo > 0);
+
+        /* Copy the data. */
+        uint64_t uOffset = 0;
+        uint64_t cbRemaining = cbSize;
+
+        do
+        {
+            size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
+
+            /* Note that we don't attempt to synchronize cross-disk accesses.
+             * It wouldn't be very difficult to do, just the lock order would
+             * need to be defined somehow to prevent deadlocks. Postpone such
+             * magic as there is no use case for this. */
+
+            rc2 = vdThreadStartRead(pDiskFrom);
+            AssertRC(rc2);
+            fLockReadFrom = true;
+
+            /*
+             * Updating the cache doesn't make any sense
+             * as we are looping once through the image.
+             */
+            rc = vdReadHelper(pDiskFrom, pImageFrom, NULL, uOffset, pvBuf,
+                              cbThisRead, fRegularRead,
+                              false /* fUpdateCache */);
+            if (RT_FAILURE(rc) && rc != VERR_VD_BLOCK_FREE)
+                break;
+
+            rc2 = vdThreadFinishRead(pDiskFrom);
+            AssertRC(rc2);
+            fLockReadFrom = false;
+
+            if (rc != VERR_VD_BLOCK_FREE)
+            {
+                rc2 = vdThreadStartWrite(pDiskTo);
+                AssertRC(rc2);
+                fLockWriteTo = true;
+
+                rc = vdWriteHelper(pDiskTo, pImageTo, NULL, uOffset, pvBuf,
+                                   cbThisRead, false /* fUpdateCache */);
+                if (RT_FAILURE(rc))
+                    break;
+
+                rc2 = vdThreadFinishWrite(pDiskTo);
+                AssertRC(rc2);
+                fLockWriteTo = false;
+            }
+            else /* Don't propagate the error to the outside */
+                rc = VINF_SUCCESS;
+
+            uOffset += cbThisRead;
+            cbRemaining -= cbThisRead;
+
+            if (pCbProgress && pCbProgress->pfnProgress)
+            {
+                /** @todo r=klaus: this can update the progress to the same
+                 * percentage over and over again if the image format makes
+                 * relatively small increments. */
+                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                              uOffset * 99 / cbSize);
+                if (RT_FAILURE(rc))
+                    break;
+            }
+            if (pDstCbProgress && pDstCbProgress->pfnProgress)
+            {
+                /** @todo r=klaus: this can update the progress to the same
+                 * percentage over and over again if the image format makes
+                 * relatively small increments. */
+                rc = pDstCbProgress->pfnProgress(pDstIfProgress->pvUser,
+                                                 uOffset * 99 / cbSize);
+                if (RT_FAILURE(rc))
+                    break;
+            }
+        } while (uOffset < cbSize);
+
+        if (RT_SUCCESS(rc))
+        {
+            rc2 = vdThreadStartWrite(pDiskTo);
+            AssertRC(rc2);
+            fLockWriteTo = true;
+
+            /* Only set modification UUID if it is non-null, since the source
+             * backend might not provide a valid modification UUID. */
+            if (!RTUuidIsNull(&ImageModificationUuid))
+                pImageTo->Backend->pfnSetModificationUuid(pImageTo->pBackendData, &ImageModificationUuid);
+
+            /* Set the requested open flags if they differ from the value
+             * required for creating the image and copying the contents. */
+            if (   pImageTo && pszFilename
+                && uOpenFlags != (uOpenFlags & ~VD_OPEN_FLAGS_READONLY))
+                rc = pImageTo->Backend->pfnSetOpenFlags(pImageTo->pBackendData,
+                                                        uOpenFlags);
+        }
+    } while (0);
+
+    if (RT_FAILURE(rc) && pImageTo && pszFilename)
+    {
+        /* Take the write lock only if it is not taken. Not worth making the
+         * above code even more complicated. */
+        if (RT_UNLIKELY(!fLockWriteTo))
+        {
+            rc2 = vdThreadStartWrite(pDiskTo);
+            AssertRC(rc2);
+            fLockWriteTo = true;
+        }
+        /* Error detected, but new image created. Remove image from list. */
+        vdRemoveImageFromList(pDiskTo, pImageTo);
+
+        /* Close and delete image. */
+        rc2 = pImageTo->Backend->pfnClose(pImageTo->pBackendData, true);
+        AssertRC(rc2);
+        pImageTo->pBackendData = NULL;
+
+        /* Free remaining resources. */
+        if (pImageTo->pszFilename)
+            RTStrFree(pImageTo->pszFilename);
+
+        RTMemFree(pImageTo);
+    }
+
+    if (RT_UNLIKELY(fLockWriteTo))
+    {
+        rc2 = vdThreadFinishWrite(pDiskTo);
+        AssertRC(rc2);
+    }
+    if (RT_UNLIKELY(fLockWriteFrom))
+    {
+        rc2 = vdThreadFinishWrite(pDiskFrom);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockReadFrom))
+    {
+        rc2 = vdThreadFinishRead(pDiskFrom);
+        AssertRC(rc2);
+    }
+
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pCbProgress && pCbProgress->pfnProgress)
+            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+        if (pDstCbProgress && pDstCbProgress->pfnProgress)
+            pDstCbProgress->pfnProgress(pDstIfProgress->pvUser, 100);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Optimizes the storage consumption of an image. Typically the unused blocks
+ * have to be wiped with zeroes to achieve a substantial reduced storage use.
+ * Another optimization done is reordering the image blocks, which can provide
+ * a significant performance boost, as reads and writes tend to use less random
+ * file offsets.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
+ * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
+ *                             the code for this isn't implemented yet.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
+                            PVDINTERFACE pVDIfsOperation)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false, fLockWrite = false;
+    void *pvBuf = NULL;
+    void *pvTmp = NULL;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pVDIfsOperation=%#p\n",
+                 pDisk, nImage, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do {
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
+                  ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        /* If there is no compact callback for not file based backends then
+         * the backend doesn't need compaction. No need to make much fuss about
+         * this. For file based ones signal this as not yet supported. */
+        if (!pImage->Backend->pfnCompact)
+        {
+            if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
+                rc = VERR_NOT_SUPPORTED;
+            else
+                rc = VINF_SUCCESS;
+            break;
+        }
+
+        /* Insert interface for reading parent state into per-operation list,
+         * if there is a parent image. */
+        VDINTERFACE IfOpParent;
+        VDINTERFACEPARENTSTATE ParentCb;
+        VDPARENTSTATEDESC ParentUser;
+        if (pImage->pPrev)
+        {
+            ParentCb.cbSize = sizeof(ParentCb);
+            ParentCb.enmInterface = VDINTERFACETYPE_PARENTSTATE;
+            ParentCb.pfnParentRead = vdParentRead;
+            ParentUser.pDisk = pDisk;
+            ParentUser.pImage = pImage->pPrev;
+            rc = VDInterfaceAdd(&IfOpParent, "VDCompact_ParentState", VDINTERFACETYPE_PARENTSTATE,
+                                &ParentCb, &ParentUser, &pVDIfsOperation);
+            AssertRC(rc);
+        }
+
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = false;
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        rc = pImage->Backend->pfnCompact(pImage->pBackendData,
+                                         0, 99,
+                                         pDisk->pVDIfsDisk,
+                                         pImage->pVDIfsImage,
+                                         pVDIfsOperation);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+    if (pvTmp)
+        RTMemTmpFree(pvTmp);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pCbProgress && pCbProgress->pfnProgress)
+            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Resizes the the given disk image to the given size.
+ *
+ * @return  VBox status
+ * @return  VERR_VD_IMAGE_READ_ONLY if image is not writable.
+ * @return  VERR_NOT_SUPPORTED if this kind of image can be compacted, but
+ *
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   cbSize          New size of the image.
+ * @param   pPCHSGeometry   Pointer to the new physical disk geometry <= (16383,16,63). Not NULL.
+ * @param   pLCHSGeometry   Pointer to the new logical disk geometry <= (x,255,63). Not NULL.
+ * @param   pVDIfsOperation Pointer to the per-operation VD interface list.
+ */
+VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, uint64_t cbSize,
+                           PCVDGEOMETRY pPCHSGeometry,
+                           PCVDGEOMETRY pLCHSGeometry,
+                           PVDINTERFACE pVDIfsOperation)
+{
+    /** @todo r=klaus resizing was designed to be part of VDCopy, so having a separate function is not desirable. */
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false, fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p cbSize=%llu pVDIfsOperation=%#p\n",
+                 pDisk, cbSize, pVDIfsOperation));
+
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+
+    do {
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pDisk), ("pDisk=%#p\n", pDisk),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE,
+                  ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        /* Not supported if the disk has child images attached. */
+        AssertMsgBreakStmt(pDisk->cImages == 1, ("cImages=%u\n", pDisk->cImages),
+                           rc = VERR_NOT_SUPPORTED);
+
+        PVDIMAGE pImage = pDisk->pBase;
+
+        /* If there is no compact callback for not file based backends then
+         * the backend doesn't need compaction. No need to make much fuss about
+         * this. For file based ones signal this as not yet supported. */
+        if (!pImage->Backend->pfnResize)
+        {
+            if (pImage->Backend->uBackendCaps & VD_CAP_FILE)
+                rc = VERR_NOT_SUPPORTED;
+            else
+                rc = VINF_SUCCESS;
+            break;
+        }
+
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = false;
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        VDGEOMETRY PCHSGeometryOld;
+        VDGEOMETRY LCHSGeometryOld;
+        PCVDGEOMETRY pPCHSGeometryNew;
+        PCVDGEOMETRY pLCHSGeometryNew;
+
+        if (pPCHSGeometry->cCylinders == 0)
+        {
+            /* Auto-detect marker, calculate new value ourself. */
+            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData, &PCHSGeometryOld);
+            if (RT_SUCCESS(rc) && (PCHSGeometryOld.cCylinders != 0))
+                PCHSGeometryOld.cCylinders = RT_MIN(cbSize / 512 / PCHSGeometryOld.cHeads / PCHSGeometryOld.cSectors, 16383);
+            else if (rc == VERR_VD_GEOMETRY_NOT_SET)
+                rc = VINF_SUCCESS;
+
+            pPCHSGeometryNew = &PCHSGeometryOld;
+        }
+        else
+            pPCHSGeometryNew = pPCHSGeometry;
+
+        if (pLCHSGeometry->cCylinders == 0)
+        {
+            /* Auto-detect marker, calculate new value ourself. */
+            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData, &LCHSGeometryOld);
+            if (RT_SUCCESS(rc) && (LCHSGeometryOld.cCylinders != 0))
+                LCHSGeometryOld.cCylinders = cbSize / 512 / LCHSGeometryOld.cHeads / LCHSGeometryOld.cSectors;
+            else if (rc == VERR_VD_GEOMETRY_NOT_SET)
+                rc = VINF_SUCCESS;
+
+            pLCHSGeometryNew = &LCHSGeometryOld;
+        }
+        else
+            pLCHSGeometryNew = pLCHSGeometry;
+
+        if (RT_SUCCESS(rc))
+            rc = pImage->Backend->pfnResize(pImage->pBackendData,
+                                            cbSize,
+                                            pPCHSGeometryNew,
+                                            pLCHSGeometryNew,
+                                            0, 99,
+                                            pDisk->pVDIfsDisk,
+                                            pImage->pVDIfsImage,
+                                            pVDIfsOperation);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+    else if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pCbProgress && pCbProgress->pfnProgress)
+            pCbProgress->pfnProgress(pIfProgress->pvUser, 100);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Closes the last opened image file in HDD container.
+ * If previous image file was opened in read-only mode (the normal case) and
+ * the last opened image is in read-write mode then the previous image will be
+ * reopened in read/write mode.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   fDelete         If true, delete the image from the host disk.
+ */
+VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Not worth splitting this up into a read lock phase and write
+         * lock phase, as closing an image is a relatively fast operation
+         * dominated by the part which needs the write lock. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = pDisk->pLast;
+        if (!pImage)
+        {
+            rc = VERR_VD_NOT_OPENED;
+            break;
+        }
+        unsigned uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
+        /* Remove image from list of opened images. */
+        vdRemoveImageFromList(pDisk, pImage);
+        /* Close (and optionally delete) image. */
+        rc = pImage->Backend->pfnClose(pImage->pBackendData, fDelete);
+        /* Free remaining resources related to the image. */
+        RTStrFree(pImage->pszFilename);
+        RTMemFree(pImage);
+
+        pImage = pDisk->pLast;
+        if (!pImage)
+            break;
+
+        /* If disk was previously in read/write mode, make sure it will stay
+         * like this (if possible) after closing this image. Set the open flags
+         * accordingly. */
+        if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            uOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
+            uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
+            rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData, uOpenFlags);
+        }
+
+        /* Cache disk information. */
+        pDisk->cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
+
+        /* Cache PCHS geometry. */
+        rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                 &pDisk->PCHSGeometry);
+        if (RT_FAILURE(rc2))
+        {
+            pDisk->PCHSGeometry.cCylinders = 0;
+            pDisk->PCHSGeometry.cHeads = 0;
+            pDisk->PCHSGeometry.cSectors = 0;
+        }
+        else
+        {
+            /* Make sure the PCHS geometry is properly clipped. */
+            pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
+            pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
+            pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
+        }
+
+        /* Cache LCHS geometry. */
+        rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                  &pDisk->LCHSGeometry);
+        if (RT_FAILURE(rc2))
+        {
+            pDisk->LCHSGeometry.cCylinders = 0;
+            pDisk->LCHSGeometry.cHeads = 0;
+            pDisk->LCHSGeometry.cSectors = 0;
+        }
+        else
+        {
+            /* Make sure the LCHS geometry is properly clipped. */
+            pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
+            pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Closes the currently opened cache image file in HDD container.
+ *
+ * @return  VBox status code.
+ * @return  VERR_VD_NOT_OPENED if no cache is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   fDelete         If true, delete the image from the host disk.
+ */
+VBOXDDU_DECL(int) VDCacheClose(PVBOXHDD pDisk, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+    PVDCACHE pCache = NULL;
+
+    LogFlowFunc(("pDisk=%#p fDelete=%d\n", pDisk, fDelete));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        AssertPtrBreakStmt(pDisk->pCache, rc = VERR_VD_CACHE_NOT_FOUND);
+
+        pCache = pDisk->pCache;
+        pDisk->pCache = NULL;
+
+        pCache->Backend->pfnClose(pCache->pBackendData, fDelete);
+        if (pCache->pszFilename)
+            RTStrFree(pCache->pszFilename);
+        RTMemFree(pCache);
+    } while (0);
+
+    if (RT_LIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Closes all opened image files in HDD container.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Lock the entire operation. */
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDCACHE pCache = pDisk->pCache;
+        if (pCache)
+        {
+            rc2 = pCache->Backend->pfnClose(pCache->pBackendData, false);
+            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+                rc = rc2;
+
+            if (pCache->pszFilename)
+                RTStrFree(pCache->pszFilename);
+            RTMemFree(pCache);
+        }
+
+        PVDIMAGE pImage = pDisk->pLast;
+        while (VALID_PTR(pImage))
+        {
+            PVDIMAGE pPrev = pImage->pPrev;
+            /* Remove image from list of opened images. */
+            vdRemoveImageFromList(pDisk, pImage);
+            /* Close image. */
+            rc2 = pImage->Backend->pfnClose(pImage->pBackendData, false);
+            if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+                rc = rc2;
+            /* Free remaining resources related to the image. */
+            RTStrFree(pImage->pszFilename);
+            RTMemFree(pImage);
+            pImage = pPrev;
+        }
+        Assert(!VALID_PTR(pDisk->pLast));
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Read data from virtual HDD.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   uOffset         Offset of first reading byte from start of disk.
+ * @param   pvBuf           Pointer to buffer for reading data.
+ * @param   cbRead          Number of bytes to read.
+ */
+VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
+                         size_t cbRead)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%zu\n",
+                 pDisk, uOffset, pvBuf, cbRead));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pvBuf),
+                           ("pvBuf=%#p\n", pvBuf),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbRead,
+                           ("cbRead=%zu\n", cbRead),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
+                           ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
+                            uOffset, cbRead, pDisk->cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+
+        rc = vdReadHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbRead,
+                          true /* fZeroFreeBlocks */,
+                          true /* fUpdateCache */);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Write data to virtual HDD.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   uOffset         Offset of the first byte being
+ *                          written from start of disk.
+ * @param   pvBuf           Pointer to buffer for writing data.
+ * @param   cbWrite         Number of bytes to write.
+ */
+VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf,
+                          size_t cbWrite)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%zu\n",
+                 pDisk, uOffset, pvBuf, cbWrite));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pvBuf),
+                           ("pvBuf=%#p\n", pvBuf),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbWrite,
+                           ("cbWrite=%zu\n", cbWrite),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
+                           ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
+                            uOffset, cbWrite, pDisk->cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+
+        vdSetModifiedFlag(pDisk);
+        rc = vdWriteHelper(pDisk, pImage, NULL, uOffset, pvBuf, cbWrite,
+                           true /* fUpdateCache */);
+        if (RT_FAILURE(rc))
+            break;
+
+        /* If there is a merge (in the direction towards a parent) running
+         * concurrently then we have to also "relay" the write to this parent,
+         * as the merge position might be already past the position where
+         * this write is going. The "context" of the write can come from the
+         * natural chain, since merging either already did or will take care
+         * of the "other" content which is might be needed to fill the block
+         * to a full allocation size. The cache doesn't need to be touched
+         * as this write is covered by the previous one. */
+        if (RT_UNLIKELY(pDisk->pImageRelay))
+            rc = vdWriteHelper(pDisk, pDisk->pImageRelay, NULL, uOffset,
+                               pvBuf, cbWrite, false /* fUpdateCache */);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Make sure the on disk representation of a virtual HDD is up to date.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_NOT_OPENED if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+
+        vdResetModifiedFlag(pDisk);
+        rc = pImage->Backend->pfnFlush(pImage->pBackendData);
+
+        if (   RT_SUCCESS(rc)
+            && pDisk->pCache)
+            rc = pDisk->pCache->Backend->pfnFlush(pDisk->pCache->pBackendData);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get number of opened images in HDD container.
+ *
+ * @returns Number of opened images for HDD container. 0 if no images have been opened.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
+{
+    unsigned cImages;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, cImages = 0);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        cImages = pDisk->cImages;
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %u\n", cImages));
+    return cImages;
+}
+
+/**
+ * Get read/write mode of HDD container.
+ *
+ * @returns Virtual disk ReadOnly status.
+ * @returns true if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
+{
+    bool fReadOnly;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, fReadOnly = false);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, fReadOnly = true);
+
+        unsigned uOpenFlags;
+        uOpenFlags = pDisk->pLast->Backend->pfnGetOpenFlags(pDisk->pLast->pBackendData);
+        fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %d\n", fReadOnly));
+    return fReadOnly;
+}
+
+/**
+ * Get total capacity of an image in HDD container.
+ *
+ * @returns Virtual disk size in bytes.
+ * @returns 0 if no image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ */
+VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
+{
+    uint64_t cbSize;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, cbSize = 0);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, cbSize = 0);
+        cbSize = pImage->Backend->pfnGetSize(pImage->pBackendData);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %llu\n", cbSize));
+    return cbSize;
+}
+
+/**
+ * Get total file size of an image in HDD container.
+ *
+ * @returns Virtual disk size in bytes.
+ * @returns 0 if no image is opened in HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ */
+VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
+{
+    uint64_t cbSize;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, cbSize = 0);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, cbSize = 0);
+        cbSize = pImage->Backend->pfnGetFileSize(pImage->pBackendData);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %llu\n", cbSize));
+    return cbSize;
+}
+
+/**
+ * Get virtual disk PCHS geometry stored in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pPCHSGeometry   Where to store PCHS geometry. Not NULL.
+ */
+VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                    PVDGEOMETRY pPCHSGeometry)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
+                 pDisk, nImage, pPCHSGeometry));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pPCHSGeometry),
+                           ("pPCHSGeometry=%#p\n", pPCHSGeometry),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        if (pImage == pDisk->pLast)
+        {
+            /* Use cached information if possible. */
+            if (pDisk->PCHSGeometry.cCylinders != 0)
+                *pPCHSGeometry = pDisk->PCHSGeometry;
+            else
+                rc = VERR_VD_GEOMETRY_NOT_SET;
+        }
+        else
+            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                     pPCHSGeometry);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("%Rrc (PCHS=%u/%u/%u)\n", rc,
+                 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
+                 pDisk->PCHSGeometry.cSectors));
+    return rc;
+}
+
+/**
+ * Store virtual disk PCHS geometry in HDD container.
+ *
+ * Note that in case of unrecoverable error all images in HDD container will be closed.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pPCHSGeometry   Where to load PCHS geometry from. Not NULL.
+ */
+VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                    PCVDGEOMETRY pPCHSGeometry)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
+                 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
+                 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(   VALID_PTR(pPCHSGeometry)
+                           && pPCHSGeometry->cHeads <= 16
+                           && pPCHSGeometry->cSectors <= 63,
+                           ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
+                            pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
+                            pPCHSGeometry->cSectors),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        if (pImage == pDisk->pLast)
+        {
+            if (    pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
+                ||  pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
+                ||  pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
+            {
+                /* Only update geometry if it is changed. Avoids similar checks
+                 * in every backend. Most of the time the new geometry is set
+                 * to the previous values, so no need to go through the hassle
+                 * of updating an image which could be opened in read-only mode
+                 * right now. */
+                rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
+                                                         pPCHSGeometry);
+
+                /* Cache new geometry values in any case. */
+                rc2 = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                          &pDisk->PCHSGeometry);
+                if (RT_FAILURE(rc2))
+                {
+                    pDisk->PCHSGeometry.cCylinders = 0;
+                    pDisk->PCHSGeometry.cHeads = 0;
+                    pDisk->PCHSGeometry.cSectors = 0;
+                }
+                else
+                {
+                    /* Make sure the CHS geometry is properly clipped. */
+                    pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
+                    pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
+                }
+            }
+        }
+        else
+        {
+            VDGEOMETRY PCHS;
+            rc = pImage->Backend->pfnGetPCHSGeometry(pImage->pBackendData,
+                                                     &PCHS);
+            if (    RT_FAILURE(rc)
+                ||  pPCHSGeometry->cCylinders != PCHS.cCylinders
+                ||  pPCHSGeometry->cHeads != PCHS.cHeads
+                ||  pPCHSGeometry->cSectors != PCHS.cSectors)
+            {
+                /* Only update geometry if it is changed. Avoids similar checks
+                 * in every backend. Most of the time the new geometry is set
+                 * to the previous values, so no need to go through the hassle
+                 * of updating an image which could be opened in read-only mode
+                 * right now. */
+                rc = pImage->Backend->pfnSetPCHSGeometry(pImage->pBackendData,
+                                                         pPCHSGeometry);
+            }
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get virtual disk LCHS geometry stored in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pLCHSGeometry   Where to store LCHS geometry. Not NULL.
+ */
+VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                    PVDGEOMETRY pLCHSGeometry)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
+                 pDisk, nImage, pLCHSGeometry));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pLCHSGeometry),
+                           ("pLCHSGeometry=%#p\n", pLCHSGeometry),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        if (pImage == pDisk->pLast)
+        {
+            /* Use cached information if possible. */
+            if (pDisk->LCHSGeometry.cCylinders != 0)
+                *pLCHSGeometry = pDisk->LCHSGeometry;
+            else
+                rc = VERR_VD_GEOMETRY_NOT_SET;
+        }
+        else
+            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                     pLCHSGeometry);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc((": %Rrc (LCHS=%u/%u/%u)\n", rc,
+                 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
+                 pDisk->LCHSGeometry.cSectors));
+    return rc;
+}
+
+/**
+ * Store virtual disk LCHS geometry in HDD container.
+ *
+ * Note that in case of unrecoverable error all images in HDD container will be closed.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_VD_GEOMETRY_NOT_SET if no geometry present in the HDD container.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pLCHSGeometry   Where to load LCHS geometry from. Not NULL.
+ */
+VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
+                                    PCVDGEOMETRY pLCHSGeometry)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
+                 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
+                 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(   VALID_PTR(pLCHSGeometry)
+                           && pLCHSGeometry->cHeads <= 255
+                           && pLCHSGeometry->cSectors <= 63,
+                           ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
+                            pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
+                            pLCHSGeometry->cSectors),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        if (pImage == pDisk->pLast)
+        {
+            if (    pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
+                ||  pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
+                ||  pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
+            {
+                /* Only update geometry if it is changed. Avoids similar checks
+                 * in every backend. Most of the time the new geometry is set
+                 * to the previous values, so no need to go through the hassle
+                 * of updating an image which could be opened in read-only mode
+                 * right now. */
+                rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
+                                                         pLCHSGeometry);
+
+                /* Cache new geometry values in any case. */
+                rc2 = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                          &pDisk->LCHSGeometry);
+                if (RT_FAILURE(rc2))
+                {
+                    pDisk->LCHSGeometry.cCylinders = 0;
+                    pDisk->LCHSGeometry.cHeads = 0;
+                    pDisk->LCHSGeometry.cSectors = 0;
+                }
+                else
+                {
+                    /* Make sure the CHS geometry is properly clipped. */
+                    pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
+                    pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
+                }
+            }
+        }
+        else
+        {
+            VDGEOMETRY LCHS;
+            rc = pImage->Backend->pfnGetLCHSGeometry(pImage->pBackendData,
+                                                     &LCHS);
+            if (    RT_FAILURE(rc)
+                ||  pLCHSGeometry->cCylinders != LCHS.cCylinders
+                ||  pLCHSGeometry->cHeads != LCHS.cHeads
+                ||  pLCHSGeometry->cSectors != LCHS.cSectors)
+            {
+                /* Only update geometry if it is changed. Avoids similar checks
+                 * in every backend. Most of the time the new geometry is set
+                 * to the previous values, so no need to go through the hassle
+                 * of updating an image which could be opened in read-only mode
+                 * right now. */
+                rc = pImage->Backend->pfnSetLCHSGeometry(pImage->pBackendData,
+                                                         pLCHSGeometry);
+            }
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get version of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puVersion       Where to store the image version.
+ */
+VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
+                               unsigned *puVersion)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
+                 pDisk, nImage, puVersion));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(puVersion),
+                           ("puVersion=%#p\n", puVersion),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        *puVersion = pImage->Backend->pfnGetVersion(pImage->pBackendData);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc uVersion=%#x\n", rc, *puVersion));
+    return rc;
+}
+
+/**
+ * List the capabilities of image backend in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pbackendInfo    Where to store the backend information.
+ */
+VBOXDDU_DECL(int) VDBackendInfoSingle(PVBOXHDD pDisk, unsigned nImage,
+                                      PVDBACKENDINFO pBackendInfo)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pBackendInfo=%#p\n",
+                 pDisk, nImage, pBackendInfo));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pBackendInfo),
+                           ("pBackendInfo=%#p\n", pBackendInfo),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        pBackendInfo->pszBackend = pImage->Backend->pszBackendName;
+        pBackendInfo->uBackendCaps = pImage->Backend->uBackendCaps;
+        pBackendInfo->paFileExtensions = pImage->Backend->paFileExtensions;
+        pBackendInfo->paConfigInfo = pImage->Backend->paConfigInfo;
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get flags of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puImageFlags    Where to store the image flags.
+ */
+VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
+                                  unsigned *puImageFlags)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
+                 pDisk, nImage, puImageFlags));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(puImageFlags),
+                           ("puImageFlags=%#p\n", puImageFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        *puImageFlags = pImage->uImageFlags;
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc uImageFlags=%#x\n", rc, *puImageFlags));
+    return rc;
+}
+
+/**
+ * Get open flags of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   puOpenFlags     Where to store the image open flags.
+ */
+VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
+                                 unsigned *puOpenFlags)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
+                 pDisk, nImage, puOpenFlags));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(puOpenFlags),
+                           ("puOpenFlags=%#p\n", puOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        *puOpenFlags = pImage->Backend->pfnGetOpenFlags(pImage->pBackendData);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc uOpenFlags=%#x\n", rc, *puOpenFlags));
+    return rc;
+}
+
+/**
+ * Set open flags of image in HDD container.
+ * This operation may cause file locking changes and/or files being reopened.
+ * Note that in case of unrecoverable error all images in HDD container will be closed.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   uOpenFlags      Image file open mode, see VD_OPEN_FLAGS_* constants.
+ */
+VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
+                                 unsigned uOpenFlags)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
+                           ("uOpenFlags=%#x\n", uOpenFlags),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnSetOpenFlags(pImage->pBackendData,
+                                              uOpenFlags);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get base filename of image in HDD container. Some image formats use
+ * other filenames as well, so don't use this for anything but informational
+ * purposes.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszFilename     Where to store the image file name.
+ * @param   cbFilename      Size of buffer pszFilename points to.
+ */
+VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
+                                char *pszFilename, unsigned cbFilename)
+{
+    int rc;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
+                 pDisk, nImage, pszFilename, cbFilename));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszFilename) && *pszFilename,
+                           ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbFilename,
+                           ("cbFilename=%u\n", cbFilename),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        size_t cb = strlen(pImage->pszFilename);
+        if (cb <= cbFilename)
+        {
+            strcpy(pszFilename, pImage->pszFilename);
+            rc = VINF_SUCCESS;
+        }
+        else
+        {
+            strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
+            pszFilename[cbFilename - 1] = '\0';
+            rc = VERR_BUFFER_OVERFLOW;
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, pszFilename=\"%s\"\n", rc, pszFilename));
+    return rc;
+}
+
+/**
+ * Get the comment line of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszComment      Where to store the comment string of image. NULL is ok.
+ * @param   cbComment       The size of pszComment buffer. 0 is ok.
+ */
+VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
+                               char *pszComment, unsigned cbComment)
+{
+    int rc;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
+                 pDisk, nImage, pszComment, cbComment));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszComment),
+                           ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cbComment,
+                           ("cbComment=%u\n", cbComment),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnGetComment(pImage->pBackendData, pszComment,
+                                            cbComment);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, pszComment=\"%s\"\n", rc, pszComment));
+    return rc;
+}
+
+/**
+ * Changes the comment line of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
+ */
+VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
+                               const char *pszComment)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
+                 pDisk, nImage, pszComment, pszComment));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pszComment) || pszComment == NULL,
+                           ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnSetComment(pImage->pBackendData, pszComment);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Get UUID of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           Where to store the image creation UUID.
+ */
+VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
+{
+    int rc;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pUuid),
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnGetUuid(pImage->pBackendData, pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
+    return rc;
+}
+
+/**
+ * Set the image's UUID. Should not be used by normal applications.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
+                 pDisk, nImage, pUuid, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        RTUUID Uuid;
+        if (!pUuid)
+        {
+            RTUuidCreate(&Uuid);
+            pUuid = &Uuid;
+        }
+        rc = pImage->Backend->pfnSetUuid(pImage->pBackendData, pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get last modification UUID of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           Where to store the image modification UUID.
+ */
+VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pUuid),
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnGetModificationUuid(pImage->pBackendData,
+                                                     pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
+    return rc;
+}
+
+/**
+ * Set the image's last modification UUID. Should not be used by normal applications.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New modification UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
+                 pDisk, nImage, pUuid, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        RTUUID Uuid;
+        if (!pUuid)
+        {
+            RTUuidCreate(&Uuid);
+            pUuid = &Uuid;
+        }
+        rc = pImage->Backend->pfnSetModificationUuid(pImage->pBackendData,
+                                                     pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Get parent UUID of image in HDD container.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           Where to store the parent image UUID.
+ */
+VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
+                                  PRTUUID pUuid)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pUuid),
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        rc = pImage->Backend->pfnGetParentUuid(pImage->pBackendData, pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, Uuid={%RTuuid}\n", rc, pUuid));
+    return rc;
+}
+
+/**
+ * Set the image's parent UUID. Should not be used by normal applications.
+ *
+ * @returns VBox status code.
+ * @param   pDisk           Pointer to HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pUuid           New parent UUID of the image. If NULL, a new UUID is created.
+ */
+VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
+                                  PCRTUUID pUuid)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%RTuuid}\n",
+                 pDisk, nImage, pUuid, pUuid));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pUuid) || pUuid == NULL,
+                           ("pUuid=%#p\n", pUuid),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        RTUUID Uuid;
+        if (!pUuid)
+        {
+            RTUuidCreate(&Uuid);
+            pUuid = &Uuid;
+        }
+        rc = pImage->Backend->pfnSetParentUuid(pImage->pBackendData, pUuid);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Debug helper - dumps all opened images in HDD container into the log file.
+ *
+ * @param   pDisk           Pointer to HDD container.
+ */
+VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
+{
+    int rc2;
+    bool fLockRead = false;
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreak(pDisk);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        if (!pDisk->pInterfaceErrorCallbacks || !VALID_PTR(pDisk->pInterfaceErrorCallbacks->pfnMessage))
+            pDisk->pInterfaceErrorCallbacks->pfnMessage = vdLogMessage;
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        vdMessageWrapper(pDisk, "--- Dumping VD Disk, Images=%u\n", pDisk->cImages);
+        for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
+        {
+            vdMessageWrapper(pDisk, "Dumping VD image \"%s\" (Backend=%s)\n",
+                             pImage->pszFilename, pImage->Backend->pszBackendName);
+            pImage->Backend->pfnDump(pImage->pBackendData);
+        }
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+}
+
+/**
+ * Query if asynchronous operations are supported for this disk.
+ *
+ * @returns VBox status code.
+ * @returns VERR_VD_IMAGE_NOT_FOUND if image with specified number was not opened.
+ * @param   pDisk           Pointer to the HDD container.
+ * @param   nImage          Image number, counts from 0. 0 is always base image of container.
+ * @param   pfAIOSupported  Where to store if async IO is supported.
+ */
+VBOXDDU_DECL(int) VDImageIsAsyncIOSupported(PVBOXHDD pDisk, unsigned nImage, bool *pfAIOSupported)
+{
+    int rc = VINF_SUCCESS;
+    int rc2;
+    bool fLockRead = false;
+
+    LogFlowFunc(("pDisk=%#p nImage=%u pfAIOSupported=%#p\n", pDisk, nImage, pfAIOSupported));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(VALID_PTR(pfAIOSupported),
+                           ("pfAIOSupported=%#p\n", pfAIOSupported),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_IMAGE_NOT_FOUND);
+
+        if (pImage->Backend->uBackendCaps & VD_CAP_ASYNC)
+            *pfAIOSupported = pImage->Backend->pfnIsAsyncIOSupported(pImage->pBackendData);
+        else
+            *pfAIOSupported = false;
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc, fAIOSupported=%u\n", rc, *pfAIOSupported));
+    return rc;
+}
+
+
+VBOXDDU_DECL(int) VDAsyncRead(PVBOXHDD pDisk, uint64_t uOffset, size_t cbRead,
+                              PCRTSGSEG paSeg, unsigned cSeg,
+                              PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                              void *pvUser1, void *pvUser2)
+{
+    int rc = VERR_VD_BLOCK_FREE;
+    int rc2;
+    bool fLockRead = false;
+    PVDIOCTX pIoCtx = NULL;
+
+    LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbRead=%zu pvUser1=%#p pvUser2=%#p\n",
+                 pDisk, uOffset, paSeg, cSeg, cbRead, pvUser1, pvUser2));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(cbRead,
+                           ("cbRead=%zu\n", cbRead),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(paSeg),
+                           ("paSeg=%#p\n", paSeg),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cSeg,
+                           ("cSeg=%zu\n", cSeg),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartRead(pDisk);
+        AssertRC(rc2);
+        fLockRead = true;
+
+        AssertMsgBreakStmt(uOffset + cbRead <= pDisk->cbSize,
+                           ("uOffset=%llu cbRead=%zu pDisk->cbSize=%llu\n",
+                            uOffset, cbRead, pDisk->cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+
+        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_READ, uOffset,
+                                  cbRead, paSeg, cSeg,
+                                  pfnComplete, pvUser1, pvUser2,
+                                  NULL, vdReadHelperAsync);
+        if (!pIoCtx)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        pIoCtx->pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pIoCtx->pImage, rc = VERR_VD_NOT_OPENED);
+
+        rc = vdIoCtxProcess(pIoCtx);
+        if (rc == VINF_VD_ASYNC_IO_FINISHED)
+        {
+            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
+                vdIoCtxFree(pDisk, pIoCtx);
+            else
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
+            vdIoCtxFree(pDisk, pIoCtx);
+
+    } while (0);
+
+    if (RT_UNLIKELY(fLockRead) && (   rc == VINF_VD_ASYNC_IO_FINISHED
+                                   || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+    {
+        rc2 = vdThreadFinishRead(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+VBOXDDU_DECL(int) VDAsyncWrite(PVBOXHDD pDisk, uint64_t uOffset, size_t cbWrite,
+                               PCRTSGSEG paSeg, unsigned cSeg,
+                               PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                               void *pvUser1, void *pvUser2)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+    PVDIOCTX pIoCtx = NULL;
+
+    LogFlowFunc(("pDisk=%#p uOffset=%llu paSeg=%p cSeg=%u cbWrite=%zu pvUser1=%#p pvUser2=%#p\n",
+                 pDisk, uOffset, paSeg, cSeg, cbWrite, pvUser1, pvUser2));
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        /* Check arguments. */
+        AssertMsgBreakStmt(cbWrite,
+                           ("cbWrite=%zu\n", cbWrite),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(VALID_PTR(paSeg),
+                           ("paSeg=%#p\n", paSeg),
+                           rc = VERR_INVALID_PARAMETER);
+        AssertMsgBreakStmt(cSeg,
+                           ("cSeg=%zu\n", cSeg),
+                           rc = VERR_INVALID_PARAMETER);
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        AssertMsgBreakStmt(uOffset + cbWrite <= pDisk->cbSize,
+                           ("uOffset=%llu cbWrite=%zu pDisk->cbSize=%llu\n",
+                            uOffset, cbWrite, pDisk->cbSize),
+                           rc = VERR_INVALID_PARAMETER);
+
+        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_WRITE, uOffset,
+                                  cbWrite, paSeg, cSeg,
+                                  pfnComplete, pvUser1, pvUser2,
+                                  NULL, vdWriteHelperAsync);
+        if (!pIoCtx)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+        pIoCtx->pImage = pImage;
+
+        rc = vdIoCtxProcess(pIoCtx);
+        if (rc == VINF_VD_ASYNC_IO_FINISHED)
+        {
+            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
+                vdIoCtxFree(pDisk, pIoCtx);
+            else
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
+            vdIoCtxFree(pDisk, pIoCtx);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
+                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+VBOXDDU_DECL(int) VDAsyncFlush(PVBOXHDD pDisk, PFNVDASYNCTRANSFERCOMPLETE pfnComplete,
+                               void *pvUser1, void *pvUser2)
+{
+    int rc;
+    int rc2;
+    bool fLockWrite = false;
+    PVDIOCTX pIoCtx = NULL;
+
+    LogFlowFunc(("pDisk=%#p\n", pDisk));
+
+    do
+    {
+        /* sanity check */
+        AssertPtrBreakStmt(pDisk, rc = VERR_INVALID_PARAMETER);
+        AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
+
+        rc2 = vdThreadStartWrite(pDisk);
+        AssertRC(rc2);
+        fLockWrite = true;
+
+        pIoCtx = vdIoCtxRootAlloc(pDisk, VDIOCTXTXDIR_FLUSH, 0,
+                                  0, NULL, 0,
+                                  pfnComplete, pvUser1, pvUser2,
+                                  NULL, vdFlushHelperAsync);
+        if (!pIoCtx)
+        {
+            rc = VERR_NO_MEMORY;
+            break;
+        }
+
+        PVDIMAGE pImage = pDisk->pLast;
+        AssertPtrBreakStmt(pImage, rc = VERR_VD_NOT_OPENED);
+        pIoCtx->pImage = pImage;
+
+        rc = vdIoCtxProcess(pIoCtx);
+        if (rc == VINF_VD_ASYNC_IO_FINISHED)
+        {
+            if (ASMAtomicCmpXchgBool(&pIoCtx->fComplete, true, false))
+                vdIoCtxFree(pDisk, pIoCtx);
+            else
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS; /* Let the other handler complete the request. */
+        }
+        else if (rc != VERR_VD_ASYNC_IO_IN_PROGRESS) /* Another error */
+            vdIoCtxFree(pDisk, pIoCtx);
+    } while (0);
+
+    if (RT_UNLIKELY(fLockWrite) && (   rc == VINF_VD_ASYNC_IO_FINISHED
+                                    || rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+    {
+        rc2 = vdThreadFinishWrite(pDisk);
+        AssertRC(rc2);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
Index: /trunk/src/VBox/Storage/VDI.cpp
===================================================================
--- /trunk/src/VBox/Storage/VDI.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/VDI.cpp	(revision 33567)
@@ -0,0 +1,2751 @@
+/** @file
+ * Virtual Disk Image (VDI), Core Code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_VDI
+#include <VBox/vd-plugin.h>
+#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
+#include "VDICore.h"
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+
+#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aVdiFileExtensions[] =
+{
+    {"vdi", VDTYPE_HDD},
+    {NULL, VDTYPE_INVALID}
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static unsigned getPowerOfTwo(unsigned uNumber);
+static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
+static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
+static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
+                          const char *pszComment, uint64_t cbDisk,
+                          uint32_t cbBlock, uint32_t cbBlockExtra);
+static int  vdiValidateHeader(PVDIHEADER pHeader);
+static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
+static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
+static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
+static int  vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
+static int  vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx);
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) vdiError(PVDIIMAGEDESC pImage, int rc, RT_SRC_POS_DECL,
+                         const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser,
+                                                   rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) vdiMessage(PVDIIMAGEDESC pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) vdiFileOpen(PVDIIMAGEDESC pImage, const char *pszFilename,
+                            uint32_t fOpen)
+{
+    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                  pszFilename, fOpen,
+                                                  &pImage->pStorage);
+}
+
+DECLINLINE(int) vdiFileClose(PVDIIMAGEDESC pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                   pImage->pStorage);
+}
+
+DECLINLINE(int) vdiFileDelete(PVDIIMAGEDESC pImage, const char *pszFilename)
+{
+    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                    pszFilename);
+}
+
+DECLINLINE(int) vdiFileMove(PVDIIMAGEDESC pImage, const char *pszSrc,
+                            const char *pszDst, unsigned fMove)
+{
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) vdiFileGetFreeSpace(PVDIIMAGEDESC pImage, const char *pszFilename,
+                                    int64_t *pcbFree)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
+                                                          pszFilename, pcbFree);
+}
+
+DECLINLINE(int) vdiFileGetSize(PVDIIMAGEDESC pImage, uint64_t *pcbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, pcbSize);
+}
+
+DECLINLINE(int) vdiFileSetSize(PVDIIMAGEDESC pImage, uint64_t cbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, cbSize);
+}
+
+DECLINLINE(int) vdiFileWriteSync(PVDIIMAGEDESC pImage, uint64_t uOffset,
+                                 const void *pvBuffer, size_t cbBuffer,
+                                 size_t *pcbWritten)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage, uOffset,
+                                                       pvBuffer, cbBuffer, pcbWritten);
+}
+
+DECLINLINE(int) vdiFileReadSync(PVDIIMAGEDESC pImage, uint64_t uOffset,
+                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pImage->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, pcbRead);
+}
+
+DECLINLINE(int) vdiFileFlushSync(PVDIIMAGEDESC pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage);
+}
+
+DECLINLINE(int) vdiFileReadUserAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
+                                     PVDIOCTX pIoCtx, size_t cbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
+                                                           pImage->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+DECLINLINE(int) vdiFileWriteUserAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
+                                      PVDIOCTX pIoCtx, size_t cbWrite,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) vdiFileWriteMetaAsync(PVDIIMAGEDESC pImage, uint64_t uOffset,
+                                      void *pvBuffer, size_t cbBuffer,
+                                      PVDIOCTX pIoCtx,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pvBuffer,
+                                                            cbBuffer, pIoCtx,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) vdiFileFlushAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
+                                  PFNVDXFERCOMPLETED pfnComplete,
+                                  void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                        pImage->pStorage,
+                                                        pIoCtx, pfnComplete,
+                                                        pvCompleteUser);
+}
+
+DECLINLINE(size_t) vdiFileIoCtxSet(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
+                                   int ch, size_t cbSet)
+{
+    return pImage->pInterfaceIOCallbacks->pfnIoCtxSet(pImage->pInterfaceIO->pvUser,
+                                                      pIoCtx, ch, cbSet);
+}
+
+
+/**
+ * Internal: Flush the image file to disk.
+ */
+static void vdiFlushImage(PVDIIMAGEDESC pImage)
+{
+    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+    {
+        /* Save header. */
+        int rc = vdiUpdateHeader(pImage);
+        AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Rrc\n",
+                         pImage->pszFilename, rc));
+        vdiFileFlushSync(pImage);
+    }
+}
+
+/**
+ * Internal: Free all allocated space for representing an image, and optionally
+ * delete the image from disk.
+ */
+static int vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (pImage->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+                vdiFlushImage(pImage);
+
+            vdiFileClose(pImage);
+            pImage->pStorage = NULL;
+        }
+
+        if (pImage->paBlocks)
+        {
+            RTMemFree(pImage->paBlocks);
+            pImage->paBlocks = NULL;
+        }
+
+        if (fDelete && pImage->pszFilename)
+            vdiFileDelete(pImage, pImage->pszFilename);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * 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 VDI 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)-1);
+}
+
+/**
+ * Internal: check VDI preheader.
+ */
+static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
+{
+    if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
+        return VERR_VD_VDI_INVALID_HEADER;
+
+    if (    VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
+        &&  pPreHdr->u32Version != 0x00000002)    /* old version. */
+        return VERR_VD_VDI_UNSUPPORTED_VERSION;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal: translate VD image flags to VDI image type enum.
+ */
+static VDIIMAGETYPE vdiTranslateImageFlags2VDI(unsigned uImageFlags)
+{
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        return VDI_IMAGE_TYPE_FIXED;
+    else if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
+        return VDI_IMAGE_TYPE_DIFF;
+    else
+        return VDI_IMAGE_TYPE_NORMAL;
+}
+
+/**
+ * Internal: translate VDI image type enum to VD image type enum.
+ */
+static unsigned vdiTranslateVDI2ImageFlags(VDIIMAGETYPE enmType)
+{
+    switch (enmType)
+    {
+        case VDI_IMAGE_TYPE_NORMAL:
+            return VD_IMAGE_FLAGS_NONE;
+        case VDI_IMAGE_TYPE_FIXED:
+            return VD_IMAGE_FLAGS_FIXED;
+        case VDI_IMAGE_TYPE_DIFF:
+            return VD_IMAGE_FLAGS_DIFF;
+        default:
+            AssertMsgFailed(("invalid VDIIMAGETYPE enmType=%d\n", (int)enmType));
+            return VD_IMAGE_FLAGS_NONE;
+    }
+}
+
+/**
+ * Internal: Init VDI header. Always use latest header version.
+ * @param   pHeader     Assumes it was initially initialized to all zeros.
+ */
+static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
+                          const char *pszComment, uint64_t cbDisk,
+                          uint32_t cbBlock, uint32_t cbBlockExtra)
+{
+    pHeader->uVersion = VDI_IMAGE_VERSION;
+    pHeader->u.v1plus.cbHeader = sizeof(VDIHEADER1PLUS);
+    pHeader->u.v1plus.u32Type = (uint32_t)vdiTranslateImageFlags2VDI(uImageFlags);
+    pHeader->u.v1plus.fFlags = (uImageFlags & VD_VDI_IMAGE_FLAGS_ZERO_EXPAND) ? 1 : 0;
+#ifdef VBOX_STRICT
+    char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
+    Assert(!memcmp(pHeader->u.v1plus.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
+#endif
+    pHeader->u.v1plus.szComment[0] = '\0';
+    if (pszComment)
+    {
+        AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1plus.szComment),
+                  ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
+        strncat(pHeader->u.v1plus.szComment, pszComment, sizeof(pHeader->u.v1plus.szComment)-1);
+    }
+
+    /* Mark the legacy geometry not-calculated. */
+    pHeader->u.v1plus.LegacyGeometry.cCylinders = 0;
+    pHeader->u.v1plus.LegacyGeometry.cHeads = 0;
+    pHeader->u.v1plus.LegacyGeometry.cSectors = 0;
+    pHeader->u.v1plus.LegacyGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+    pHeader->u.v1plus.u32Dummy = 0; /* used to be the translation value */
+
+    pHeader->u.v1plus.cbDisk = cbDisk;
+    pHeader->u.v1plus.cbBlock = cbBlock;
+    pHeader->u.v1plus.cBlocks = (uint32_t)(cbDisk / cbBlock);
+    if (cbDisk % cbBlock)
+        pHeader->u.v1plus.cBlocks++;
+    pHeader->u.v1plus.cbBlockExtra = cbBlockExtra;
+    pHeader->u.v1plus.cBlocksAllocated = 0;
+
+    /* Init offsets. */
+    pHeader->u.v1plus.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1PLUS), VDI_DATA_ALIGN);
+    pHeader->u.v1plus.offData = RT_ALIGN_32(pHeader->u.v1plus.offBlocks + (pHeader->u.v1plus.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_DATA_ALIGN);
+
+    /* Init uuids. */
+    RTUuidCreate(&pHeader->u.v1plus.uuidCreate);
+    RTUuidClear(&pHeader->u.v1plus.uuidModify);
+    RTUuidClear(&pHeader->u.v1plus.uuidLinkage);
+    RTUuidClear(&pHeader->u.v1plus.uuidParentModify);
+
+    /* Mark LCHS geometry not-calculated. */
+    pHeader->u.v1plus.LCHSGeometry.cCylinders = 0;
+    pHeader->u.v1plus.LCHSGeometry.cHeads = 0;
+    pHeader->u.v1plus.LCHSGeometry.cSectors = 0;
+    pHeader->u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+}
+
+/**
+ * Internal: Check VDI header.
+ */
+static int vdiValidateHeader(PVDIHEADER pHeader)
+{
+    /* Check version-dependent 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_VD_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_VD_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_VD_VDI_INVALID_HEADER;
+            }
+
+            break;
+        }
+        default:
+            /* Unsupported. */
+            return VERR_VD_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) & ~VD_VDI_IMAGE_FLAGS_MASK)
+    {
+        LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
+        fFailed = true;
+    }
+
+    if (   getImageLCHSGeometry(pHeader)
+        && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
+    {
+        LogRel(("VDI: wrong sector size (%d != %d)\n",
+               (getImageLCHSGeometry(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_VD_VDI_INVALID_HEADER : VINF_SUCCESS;
+}
+
+/**
+ * Internal: Set up VDIIMAGEDESC structure by image header.
+ */
+static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
+{
+    pImage->uImageFlags        = getImageFlags(&pImage->Header);
+    pImage->uImageFlags       |= vdiTranslateVDI2ImageFlags(getImageType(&pImage->Header));
+    pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
+    pImage->offStartData       = getImageDataOffset(&pImage->Header);
+    pImage->uBlockMask         = getImageBlockSize(&pImage->Header) - 1;
+    pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
+    pImage->offStartBlockData  = getImageExtraBlockSize(&pImage->Header);
+    pImage->cbTotalBlockData   =   pImage->offStartBlockData
+                                 + getImageBlockSize(&pImage->Header);
+}
+
+/**
+ * Internal: Create VDI image file.
+ */
+static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
+                          unsigned uImageFlags, const char *pszComment,
+                          PCVDGEOMETRY pPCHSGeometry,
+                          PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
+                          unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
+                          void *pvUser, unsigned uPercentStart,
+                          unsigned uPercentSpan)
+{
+    int rc;
+    uint64_t cbTotal;
+    uint64_t cbFill;
+    uint64_t uOff;
+
+    /* Special check for comment length. */
+    if (   VALID_PTR(pszComment)
+        && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
+    {
+        rc = vdiError(pImage, VERR_VD_VDI_COMMENT_TOO_LONG, RT_SRC_POS, N_("VDI: comment is too long for '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    AssertPtr(pPCHSGeometry);
+    AssertPtr(pLCHSGeometry);
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    vdiInitPreHeader(&pImage->PreHeader);
+    vdiInitHeader(&pImage->Header, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
+    /* Save PCHS geometry. Not much work, and makes the flow of information
+     * quite a bit clearer - relying on the higher level isn't obvious. */
+    pImage->PCHSGeometry = *pPCHSGeometry;
+    /* Set LCHS geometry (legacy geometry is ignored for the current 1.1+). */
+    pImage->Header.u.v1plus.LCHSGeometry.cCylinders = pLCHSGeometry->cCylinders;
+    pImage->Header.u.v1plus.LCHSGeometry.cHeads = pLCHSGeometry->cHeads;
+    pImage->Header.u.v1plus.LCHSGeometry.cSectors = pLCHSGeometry->cSectors;
+    pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+
+    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
+    if (!pImage->paBlocks)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+    {
+        /* for growing images mark all blocks in paBlocks as free. */
+        for (unsigned 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 */
+        for (unsigned 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 image file. */
+    rc = vdiFileOpen(pImage, pImage->pszFilename,
+                     VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                                true /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: cannot create image '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    cbTotal =   pImage->offStartData
+              + (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
+
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        /* Check the free space on the disk and leave early if there is not
+         * sufficient space available. */
+        int64_t cbFree = 0;
+        rc = vdiFileGetFreeSpace(pImage, pImage->pszFilename, &cbFree);
+        if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbTotal))
+        {
+            rc = vdiError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("VDI: disk would overflow creating image '%s'"), pImage->pszFilename);
+            goto out;
+        }
+    }
+
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        /*
+         * Allocate & commit whole file if fixed image, it must be more
+         * effective than expanding file by write operations.
+         */
+        rc = vdiFileSetSize(pImage, cbTotal);
+    }
+    else
+    {
+        /* Set file size to hold header and blocks array. */
+        rc = vdiFileSetSize(pImage, pImage->offStartData);
+    }
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: setting image size failed for '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Use specified image uuid */
+    *getImageCreationUUID(&pImage->Header) = *pUuid;
+
+    /* Generate image last-modify uuid */
+    RTUuidCreate(getImageModificationUUID(&pImage->Header));
+
+    /* Write pre-header. */
+    rc = vdiFileWriteSync(pImage, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Write header. */
+    rc = vdiFileWriteSync(pImage, sizeof(pImage->PreHeader),
+                          &pImage->Header.u.v1plus,
+                          sizeof(pImage->Header.u.v1plus), NULL);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    rc = vdiFileWriteSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
+                          getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
+                          NULL);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block pointers failed for '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        /* Fill image with zeroes. We do this for every fixed-size image since on some systems
+         * (for example Windows Vista), it takes ages to write a block near the end of a sparse
+         * file and the guest could complain about an ATA timeout. */
+
+        /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
+         *        Currently supported file systems are ext4 and ocfs2. */
+
+        /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
+        const size_t cbBuf = 128 * _1K;
+        void *pvBuf = RTMemTmpAllocZ(cbBuf);
+        if (!pvBuf)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        cbFill = (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
+        uOff = 0;
+        /* Write data to all image blocks. */
+        while (uOff < cbFill)
+        {
+            unsigned cbChunk = (unsigned)RT_MIN(cbFill, cbBuf);
+
+            rc = vdiFileWriteSync(pImage, pImage->offStartData + uOff,
+                                  pvBuf, cbChunk, NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block failed for '%s'"), pImage->pszFilename);
+                RTMemTmpFree(pvBuf);
+                goto out;
+            }
+
+            uOff += cbChunk;
+
+            if (pfnProgress)
+            {
+                rc = pfnProgress(pvUser,
+                                 uPercentStart + uOff * uPercentSpan / cbFill);
+                if (RT_FAILURE(rc))
+                    goto out;
+            }
+        }
+        RTMemTmpFree(pvBuf);
+    }
+
+out:
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan);
+
+    if (RT_FAILURE(rc))
+        vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
+    return rc;
+}
+
+/**
+ * Internal: Open a VDI image.
+ */
+static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
+{
+    int rc;
+
+    pImage->uOpenFlags = uOpenFlags;
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the image.
+     */
+    rc = vdiFileOpen(pImage, pImage->pszFilename,
+                     VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        goto out;
+    }
+
+    /* Read pre-header. */
+    rc = vdiFileReadSync(pImage, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    rc = vdiValidatePreHeader(&pImage->PreHeader);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: invalid pre-header in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Read header. */
+    pImage->Header.uVersion = pImage->PreHeader.u32Version;
+    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
+    {
+        case 0:
+            rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
+                                 &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
+                                 NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
+                goto out;
+            }
+            break;
+        case 1:
+            rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
+                                 &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
+                                 NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
+                goto out;
+            }
+            /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write mode.
+             * Conversion is harmless, as any VirtualBox version supporting VDI
+             * 1.1 doesn't touch fields it doesn't know about. */
+            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                && GET_MINOR_HEADER_VERSION(&pImage->Header) == 1
+                && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
+            {
+                pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
+                /* Mark LCHS geometry not-calculated. */
+                pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
+                pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
+                pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
+                pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+            }
+            else if (pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
+            {
+                /* Read the actual VDI 1.1+ header completely. */
+                rc = vdiFileReadSync(pImage, sizeof(pImage->PreHeader),
+                                     &pImage->Header.u.v1plus,
+                                     sizeof(pImage->Header.u.v1plus), NULL);
+                if (RT_FAILURE(rc))
+                {
+                    rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
+                    goto out;
+                }
+            }
+            break;
+        default:
+            rc = vdiError(pImage, VERR_VD_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: unsupported major version %u in '%s'"), GET_MAJOR_HEADER_VERSION(&pImage->Header), pImage->pszFilename);
+            goto out;
+    }
+
+    rc = vdiValidateHeader(&pImage->Header);
+    if (RT_FAILURE(rc))
+    {
+        rc = vdiError(pImage, VERR_VD_VDI_INVALID_HEADER, RT_SRC_POS, N_("VDI: invalid header in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Setup image parameters by header. */
+    vdiSetupImageDesc(pImage);
+
+    /* Allocate memory for blocks array. */
+    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
+    if (!pImage->paBlocks)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    /* Read blocks array. */
+    rc = vdiFileReadSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
+                         getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
+                         NULL);
+
+out:
+    if (RT_FAILURE(rc))
+        vdiFreeImage(pImage, false);
+    return rc;
+}
+
+/**
+ * Internal: Save header to file.
+ */
+static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
+{
+    int rc;
+    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
+    {
+        case 0:
+            rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
+                                  &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
+                                  NULL);
+            break;
+        case 1:
+            if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
+                rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
+                                      &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
+                                      NULL);
+            else
+                rc = vdiFileWriteSync(pImage, sizeof(VDIPREHEADER),
+                                      &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus),
+                                      NULL);
+            break;
+        default:
+            rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            break;
+    }
+    AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
+    return rc;
+}
+
+/**
+ * Internal: Save header to file - async version.
+ */
+static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
+{
+    int rc;
+    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
+    {
+        case 0:
+            rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
+                                       &pImage->Header.u.v0,
+                                       sizeof(pImage->Header.u.v0),
+                                       pIoCtx, NULL, NULL);
+            break;
+        case 1:
+            if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
+                rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
+                                           &pImage->Header.u.v1,
+                                           sizeof(pImage->Header.u.v1),
+                                           pIoCtx, NULL, NULL);
+            else
+                rc = vdiFileWriteMetaAsync(pImage, sizeof(VDIPREHEADER),
+                                           &pImage->Header.u.v1plus,
+                                           sizeof(pImage->Header.u.v1plus),
+                                           pIoCtx, NULL, NULL);
+            break;
+        default:
+            rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            break;
+    }
+    AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
+              ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, 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 (RT_SUCCESS(rc))
+    {
+        /* write only one block pointer. */
+        rc = vdiFileWriteSync(pImage, pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
+                              &pImage->paBlocks[uBlock], sizeof(VDIIMAGEBLOCKPOINTER),
+                              NULL);
+        AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
+                         uBlock, pImage->pszFilename, rc));
+    }
+    return rc;
+}
+
+/**
+ * Internal: Save block pointer to file, save header to file - async version.
+ */
+static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
+                                   PVDIOCTX pIoCtx)
+{
+    /* Update image header. */
+    int rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
+    if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+    {
+        /* write only one block pointer. */
+        rc = vdiFileWriteMetaAsync(pImage,
+                                   pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
+                                   &pImage->paBlocks[uBlock],
+                                   sizeof(VDIIMAGEBLOCKPOINTER),
+                                   pIoCtx, NULL, NULL);
+        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
+                  ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
+                  uBlock, pImage->pszFilename, rc));
+    }
+    return rc;
+}
+
+/**
+ * Internal: Flush the image file to disk - async version.
+ */
+static int vdiFlushImageAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+
+    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+    {
+        /* Save header. */
+        rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
+        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
+                  ("vdiUpdateHeaderAsync() failed, filename=\"%s\", rc=%Rrc\n",
+                  pImage->pszFilename, rc));
+        rc = vdiFileFlushAsync(pImage, pIoCtx, NULL, NULL);
+        AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
+                  ("Flushing data to disk failed rc=%Rrc\n", rc));
+    }
+
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int vdiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
+    int rc = VINF_SUCCESS;
+    PVDIIMAGEDESC pImage;
+
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->paBlocks = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
+    vdiFreeImage(pImage, false);
+    RTMemFree(pImage);
+
+    if (RT_SUCCESS(rc))
+        *penmType = VDTYPE_HDD;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int vdiOpen(const char *pszFilename, unsigned uOpenFlags,
+                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                   VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PVDIIMAGEDESC pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->paBlocks = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = vdiOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pImage;
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int vdiCreate(const char *pszFilename, uint64_t cbSize,
+                     unsigned uImageFlags, const char *pszComment,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     PCRTUUID pUuid, unsigned uOpenFlags,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc;
+    PVDIIMAGEDESC pImage;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check size. Maximum 4PB-3M. No tricks with adjusting the 1M block size
+     * so far, which would extend the size. */
+    cbSize = RT_ALIGN_64(cbSize, _1M);
+    if (    !cbSize
+        ||  cbSize >= _1P * 2 - _1M * 3)
+    {
+        rc = VERR_VD_INVALID_SIZE;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename
+        || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
+        || !VALID_PTR(pPCHSGeometry)
+        || !VALID_PTR(pLCHSGeometry))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->paBlocks = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = vdiCreateImage(pImage, cbSize, uImageFlags, pszComment,
+                        pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
+                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
+    if (RT_SUCCESS(rc))
+    {
+        /* So far the image is opened in read/write mode. Make sure the
+         * image is opened in read-only mode if the caller requested that. */
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            vdiFreeImage(pImage, false);
+            rc = vdiOpenImage(pImage, uOpenFlags);
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pImage);
+                goto out;
+            }
+        }
+        *ppBackendData = pImage;
+    }
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int vdiRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+
+    int rc = VINF_SUCCESS;
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+
+    /* Check arguments. */
+    if (   !pImage
+        || !pszFilename
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Close the image. */
+    vdiFreeImage(pImage, false);
+
+    /* Rename the file. */
+    rc = vdiFileMove(pImage, pImage->pszFilename, pszFilename, 0);
+    if (RT_FAILURE(rc))
+    {
+        /* The move failed, try to reopen the original image. */
+        int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
+        if (RT_FAILURE(rc2))
+            rc = rc2;
+
+        goto out;
+    }
+
+    /* Update pImage with the new information. */
+    pImage->pszFilename = pszFilename;
+
+    /* Open the new image. */
+    rc = vdiOpenImage(pImage, pImage->uOpenFlags);
+    if (RT_FAILURE(rc))
+        goto out;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int vdiClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    rc = vdiFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                   size_t cbToRead, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uBlock;
+    unsigned offRead;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(!(uOffset % 512));
+    Assert(!(cbToRead % 512));
+
+    if (   uOffset + cbToRead > getImageDiskSize(&pImage->Header)
+        || !VALID_PTR(pvBuf)
+        || !cbToRead)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Calculate starting block number and offset inside it. */
+    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
+    offRead = (unsigned)uOffset & pImage->uBlockMask;
+
+    /* Clip read range to at most the rest of the block. */
+    cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
+    Assert(!(cbToRead % 512));
+
+    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
+        rc = VERR_VD_BLOCK_FREE;
+    else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
+    {
+        memset(pvBuf, 0, cbToRead);
+        rc = VINF_SUCCESS;
+    }
+    else
+    {
+        /* Block present in image file, read relevant data. */
+        uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
+                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
+        rc = vdiFileReadSync(pImage, u64Offset, pvBuf, cbToRead, NULL);
+    }
+
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbToRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**@copydoc VBOXHDDBACKEND::pfnWrite */
+static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                    size_t cbToWrite, size_t *pcbWriteProcess,
+                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uBlock;
+    unsigned offWrite;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+    Assert(!(uOffset % 512));
+    Assert(!(cbToWrite % 512));
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    if (!VALID_PTR(pvBuf) || !cbToWrite)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* No size check here, will do that later.  For dynamic images which are
+     * not multiples of the block size in length, this would prevent writing to
+     * the last block. */
+
+    /* Calculate starting block number and offset inside it. */
+    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
+    offWrite = (unsigned)uOffset & pImage->uBlockMask;
+
+    /* Clip write range to at most the rest of the block. */
+    cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
+    Assert(!(cbToWrite % 512));
+
+    do
+    {
+        if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+        {
+            /* Block is either free or zero. */
+            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
+                && (   pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
+                    || cbToWrite == getImageBlockSize(&pImage->Header)))
+            {
+                /* 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));
+                Assert(cbToWrite * 8 <= UINT32_MAX);
+                if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
+                {
+                    pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
+                    break;
+                }
+            }
+
+            if (cbToWrite == getImageBlockSize(&pImage->Header))
+            {
+                /* Full block write to previously unallocated block.
+                 * Allocate block and write data. */
+                Assert(!offWrite);
+                unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
+                uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
+                                   + (pImage->offStartData + pImage->offStartBlockData);
+                rc = vdiFileWriteSync(pImage, u64Offset, pvBuf, cbToWrite, NULL);
+                if (RT_FAILURE(rc))
+                    goto out;
+                pImage->paBlocks[uBlock] = cBlocksAllocated;
+                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
+
+                rc = vdiUpdateBlockInfo(pImage, uBlock);
+                if (RT_FAILURE(rc))
+                    goto out;
+
+                *pcbPreRead = 0;
+                *pcbPostRead = 0;
+            }
+            else
+            {
+                /* Trying to do a partial write to an unallocated block. Don't do
+                 * anything except letting the upper layer know what to do. */
+                *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
+                *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
+                rc = VERR_VD_BLOCK_FREE;
+            }
+        }
+        else
+        {
+            /* Block present in image file, write relevant data. */
+            uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
+                               + (pImage->offStartData + pImage->offStartBlockData + offWrite);
+            rc = vdiFileWriteSync(pImage, u64Offset, pvBuf, cbToWrite, NULL);
+        }
+    } while (0);
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbToWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int vdiFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    Assert(pImage);
+
+    vdiFlushImage(pImage);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned vdiGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uVersion;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uVersion = pImage->PreHeader.u32Version;
+    else
+        uVersion = 0;
+
+    LogFlowFunc(("returns %#x\n", uVersion));
+    return uVersion;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t vdiGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    uint64_t cbSize;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        cbSize = getImageDiskSize(&pImage->Header);
+    else
+        cbSize = 0;
+
+    LogFlowFunc(("returns %llu\n", cbSize));
+    return cbSize;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t vdiGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        uint64_t cbFile;
+        if (pImage->pStorage)
+        {
+            int rc = vdiFileGetSize(pImage, &cbFile);
+            if (RT_SUCCESS(rc))
+                cb += cbFile;
+        }
+    }
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pImage->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
+        PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
+        if (!pGeometry)
+            pGeometry = &DummyGeo;
+
+        if (    pGeometry->cCylinders > 0
+            &&  pGeometry->cHeads > 0
+            &&  pGeometry->cSectors > 0)
+        {
+            pLCHSGeometry->cCylinders = pGeometry->cCylinders;
+            pLCHSGeometry->cHeads = pGeometry->cHeads;
+            pLCHSGeometry->cSectors = pGeometry->cSectors;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    PVDIDISKGEOMETRY pGeometry;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pGeometry = getImageLCHSGeometry(&pImage->Header);
+        if (pGeometry)
+        {
+            pGeometry->cCylinders = pLCHSGeometry->cCylinders;
+            pGeometry->cHeads = pLCHSGeometry->cHeads;
+            pGeometry->cSectors = pLCHSGeometry->cSectors;
+            pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+
+            /* Update header information in base image file. */
+            vdiFlushImage(pImage);
+        }
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned vdiGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uImageFlags = pImage->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned vdiGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+    const char *pszFilename;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    pszFilename = pImage->pszFilename;
+    rc = vdiFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vdiOpenImage(pImage, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int vdiGetComment(void *pBackendData, char *pszComment,
+                         size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        char *pszTmp = getImageComment(&pImage->Header);
+        /* Make this foolproof even if the image doesn't have the zero
+         * termination. With some luck the repaired header will be saved. */
+        size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
+        if (cb == VDI_IMAGE_COMMENT_SIZE)
+        {
+            pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
+            cb--;
+        }
+        if (cb < cbComment)
+        {
+            /* memcpy is much better than strncpy. */
+            memcpy(pszComment, pszTmp, cb + 1);
+        }
+        else
+            rc = VERR_BUFFER_OVERFLOW;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int vdiSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+        {
+            size_t cchComment = pszComment ? strlen(pszComment) : 0;
+            if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
+            {
+                LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
+                rc = VERR_VD_VDI_COMMENT_TOO_LONG;
+                goto out;
+            }
+
+            /* we don't support old style images */
+            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
+            {
+                /*
+                 * Update the comment field, making sure to zero out all of the previous comment.
+                 */
+                memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
+                memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
+
+                /* write out new the header */
+                rc = vdiUpdateHeader(pImage);
+            }
+            else
+                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+        }
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = *getImageCreationUUID(&pImage->Header);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
+                pImage->Header.u.v1.uuidCreate = *pUuid;
+            /* Make it possible to clone old VDIs. */
+            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
+                pImage->Header.u.v0.uuidCreate = *pUuid;
+            else
+            {
+                LogFunc(("Version is not supported!\n"));
+                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            }
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = *getImageModificationUUID(&pImage->Header);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
+                pImage->Header.u.v1.uuidModify = *pUuid;
+            /* Make it possible to clone old VDIs. */
+            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
+                pImage->Header.u.v0.uuidModify = *pUuid;
+            else
+            {
+                LogFunc(("Version is not supported!\n"));
+                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            }
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = *getImageParentUUID(&pImage->Header);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
+                pImage->Header.u.v1.uuidLinkage = *pUuid;
+            /* Make it possible to clone old VDIs. */
+            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
+                pImage->Header.u.v0.uuidLinkage = *pUuid;
+            else
+            {
+                LogFunc(("Version is not supported!\n"));
+                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            }
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = *getImageParentModificationUUID(&pImage->Header);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
+                pImage->Header.u.v1.uuidParentModify = *pUuid;
+            else
+            {
+                LogFunc(("Version is not supported!\n"));
+                rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
+            }
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void vdiDump(void *pBackendData)
+{
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+
+    vdiMessage(pImage, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
+                pImage->pszFilename,
+                (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
+                pImage->uOpenFlags,
+                pImage->pStorage);
+    vdiMessage(pImage, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
+                pImage->PreHeader.u32Version,
+                getImageType(&pImage->Header),
+                getImageFlags(&pImage->Header),
+                getImageDiskSize(&pImage->Header));
+    vdiMessage(pImage, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
+                getImageBlockSize(&pImage->Header),
+                getImageExtraBlockSize(&pImage->Header),
+                getImageBlocks(&pImage->Header),
+                getImageBlocksAllocated(&pImage->Header));
+    vdiMessage(pImage, "Header: offBlocks=%u offData=%u\n",
+                getImageBlocksOffset(&pImage->Header),
+                getImageDataOffset(&pImage->Header));
+    PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
+    if (pg)
+        vdiMessage(pImage, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
+                    pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
+    vdiMessage(pImage, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
+    vdiMessage(pImage, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
+    vdiMessage(pImage, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
+    if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
+        vdiMessage(pImage, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
+    vdiMessage(pImage, "Image:  fFlags=%08X offStartBlocks=%u offStartData=%u\n",
+                pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
+    vdiMessage(pImage, "Image:  uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
+                pImage->uBlockMask,
+                pImage->cbTotalBlockData,
+                pImage->uShiftOffset2Index,
+                pImage->offStartBlockData);
+
+    unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
+    for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
+    {
+        if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+        {
+            cBlocksNotFree++;
+            if (pImage->paBlocks[uBlock] >= cBlocks)
+                cBadBlocks++;
+        }
+    }
+    if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
+    {
+        vdiMessage(pImage, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
+                cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
+    }
+    if (cBadBlocks)
+    {
+        vdiMessage(pImage, "!! WARNING: %u bad blocks found !!\n",
+                cBadBlocks);
+    }
+}
+
+static bool vdiIsAsyncIOSupported(void *pBackendData)
+{
+    return true;
+}
+
+static int vdiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
+                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uBlock;
+    unsigned offRead;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(!(uOffset % 512));
+    Assert(!(cbToRead % 512));
+
+    if (   uOffset + cbToRead > getImageDiskSize(&pImage->Header)
+        || !VALID_PTR(pIoCtx)
+        || !cbToRead)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Calculate starting block number and offset inside it. */
+    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
+    offRead = (unsigned)uOffset & pImage->uBlockMask;
+
+    /* Clip read range to at most the rest of the block. */
+    cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
+    Assert(!(cbToRead % 512));
+
+    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
+        rc = VERR_VD_BLOCK_FREE;
+    else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
+    {
+        size_t cbSet;
+
+        cbSet = vdiFileIoCtxSet(pImage, pIoCtx, 0, cbToRead);
+        Assert(cbSet == cbToRead);
+
+        rc = VINF_SUCCESS;
+    }
+    else
+    {
+        /* Block present in image file, read relevant data. */
+        uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
+                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
+        rc = vdiFileReadUserAsync(pImage, u64Offset, pIoCtx, cbToRead);
+    }
+
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbToRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+static int vdiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+                         PVDIOCTX pIoCtx,
+                         size_t *pcbWriteProcess, size_t *pcbPreRead,
+                         size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    unsigned uBlock;
+    unsigned offWrite;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+    Assert(!(uOffset % 512));
+    Assert(!(cbToWrite % 512));
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    if (!VALID_PTR(pIoCtx) || !cbToWrite)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* No size check here, will do that later.  For dynamic images which are
+     * not multiples of the block size in length, this would prevent writing to
+     * the last block. */
+
+    /* Calculate starting block number and offset inside it. */
+    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
+    offWrite = (unsigned)uOffset & pImage->uBlockMask;
+
+    /* Clip write range to at most the rest of the block. */
+    cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
+    Assert(!(cbToWrite % 512));
+
+    do
+    {
+        if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
+        {
+            /* Block is either free or zero. */
+            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
+                && (   pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
+                    || cbToWrite == getImageBlockSize(&pImage->Header)))
+            {
+#if 0 /** @todo Provide interface to check an I/O context for a specific value */
+                /* 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));
+                Assert(cbToWrite * 8 <= UINT32_MAX);
+                if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
+                {
+                    pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
+                    break;
+                }
+#endif
+            }
+
+            if (   cbToWrite == getImageBlockSize(&pImage->Header)
+                && !(fWrite & VD_WRITE_NO_ALLOC))
+            {
+                /* Full block write to previously unallocated block.
+                 * Allocate block and write data. */
+                Assert(!offWrite);
+                unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
+                uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
+                                   + (pImage->offStartData + pImage->offStartBlockData);
+                rc = vdiFileWriteUserAsync(pImage, u64Offset, pIoCtx, cbToWrite, NULL, NULL);
+                if (RT_UNLIKELY(RT_FAILURE_NP(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)))
+                    goto out;
+                pImage->paBlocks[uBlock] = cBlocksAllocated;
+                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
+
+                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx);
+                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+                    goto out;
+
+                *pcbPreRead = 0;
+                *pcbPostRead = 0;
+            }
+            else
+            {
+                /* Trying to do a partial write to an unallocated block. Don't do
+                 * anything except letting the upper layer know what to do. */
+                *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
+                *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
+                rc = VERR_VD_BLOCK_FREE;
+            }
+        }
+        else
+        {
+            /* Block present in image file, write relevant data. */
+            uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
+                               + (pImage->offStartData + pImage->offStartBlockData + offWrite);
+            rc = vdiFileWriteUserAsync(pImage, u64Offset, pIoCtx, cbToWrite, NULL, NULL);
+        }
+    } while (0);
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbToWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+static int vdiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    Assert(pImage);
+
+    rc = vdiFlushImageAsync(pImage, pIoCtx);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCompact */
+static int vdiCompact(void *pBackendData, unsigned uPercentStart,
+                      unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
+                      PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
+{
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+    void *pvBuf = NULL, *pvTmp = NULL;
+    unsigned *paBlocks2 = NULL;
+
+    int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
+    void *pvParent = NULL;
+    PVDINTERFACE pIfParentState = VDInterfaceGet(pVDIfsOperation,
+                                                 VDINTERFACETYPE_PARENTSTATE);
+    PVDINTERFACEPARENTSTATE pCbParentState = NULL;
+    if (pIfParentState)
+    {
+        pCbParentState = VDGetInterfaceParentState(pIfParentState);
+        if (pCbParentState)
+            pfnParentRead = pCbParentState->pfnParentRead;
+        pvParent = pIfParentState->pvUser;
+    }
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    do {
+        AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
+
+        AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
+                        rc = VERR_VD_IMAGE_READ_ONLY);
+
+        unsigned cBlocks;
+        unsigned cBlocksToMove = 0;
+        size_t cbBlock;
+        cBlocks = getImageBlocks(&pImage->Header);
+        cbBlock = getImageBlockSize(&pImage->Header);
+        if (pfnParentRead)
+        {
+            pvBuf = RTMemTmpAlloc(cbBlock);
+            AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
+        }
+        pvTmp = RTMemTmpAlloc(cbBlock);
+        AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
+
+        uint64_t cbFile;
+        rc = vdiFileGetSize(pImage, &cbFile);
+        AssertRCBreak(rc);
+        unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
+        if (cBlocksAllocated == 0)
+        {
+            /* No data blocks in this image, no need to compact. */
+            rc = VINF_SUCCESS;
+            break;
+        }
+
+        /* Allocate block array for back resolving. */
+        paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
+        AssertBreakStmt(VALID_PTR(paBlocks2), rc = VERR_NO_MEMORY);
+        /* Fill out back resolving, check/fix allocation errors before
+         * compacting the image, just to be on the safe side. Update the
+         * image contents straight away, as this enables cancelling. */
+        for (unsigned i = 0; i < cBlocksAllocated; i++)
+            paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
+        rc = VINF_SUCCESS;
+        for (unsigned i = 0; i < cBlocks; i++)
+        {
+            VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
+            if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
+            {
+                if (ptrBlock < cBlocksAllocated)
+                {
+                    if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
+                        paBlocks2[ptrBlock] = i;
+                    else
+                    {
+                        LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
+                                 i, pImage->pszFilename));
+                        pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
+                        rc = vdiUpdateBlockInfo(pImage, i);
+                        if (RT_FAILURE(rc))
+                            break;
+                    }
+                }
+                else
+                {
+                    LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
+                             i, pImage->pszFilename));
+                    pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
+                    rc = vdiUpdateBlockInfo(pImage, i);
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            }
+        }
+        if (RT_FAILURE(rc))
+            break;
+
+        /* Find redundant information and update the block pointers
+         * accordingly, creating bubbles. Keep disk up to date, as this
+         * enables cancelling. */
+        for (unsigned i = 0; i < cBlocks; i++)
+        {
+            VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
+            if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
+            {
+                /* Block present in image file, read relevant data. */
+                uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
+                                   + (pImage->offStartData + pImage->offStartBlockData);
+                rc = vdiFileReadSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
+                if (RT_FAILURE(rc))
+                    break;
+
+                if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
+                {
+                    pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
+                    rc = vdiUpdateBlockInfo(pImage, i);
+                    if (RT_FAILURE(rc))
+                        break;
+                    paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
+                    /* Adjust progress info, one block to be relocated. */
+                    cBlocksToMove++;
+                }
+                else if (pfnParentRead)
+                {
+                    rc = pfnParentRead(pvParent, i * cbBlock, pvBuf, cbBlock);
+                    if (RT_FAILURE(rc))
+                        break;
+                    if (!memcmp(pvTmp, pvBuf, cbBlock))
+                    {
+                        pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
+                        rc = vdiUpdateBlockInfo(pImage, i);
+                        if (RT_FAILURE(rc))
+                            break;
+                        paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
+                        /* Adjust progress info, one block to be relocated. */
+                        cBlocksToMove++;
+                    }
+                }
+            }
+
+            if (pCbProgress && pCbProgress->pfnProgress)
+            {
+                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                              (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
+                if (RT_FAILURE(rc))
+                    break;
+            }
+        }
+        if (RT_FAILURE(rc))
+            break;
+
+        /* Fill bubbles with other data (if available). */
+        unsigned cBlocksMoved = 0;
+        unsigned uBlockUsedPos = cBlocksAllocated;
+        for (unsigned i = 0; i < cBlocksAllocated; i++)
+        {
+            unsigned uBlock = paBlocks2[i];
+            if (uBlock == VDI_IMAGE_BLOCK_FREE)
+            {
+                unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
+                while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
+                {
+                    uBlockUsedPos--;
+                    uBlockData = paBlocks2[uBlockUsedPos];
+                }
+                /* Terminate early if there is no block which needs copying. */
+                if (uBlockUsedPos == i)
+                    break;
+                uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
+                                   + (pImage->offStartData + pImage->offStartBlockData);
+                rc = vdiFileReadSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
+                u64Offset = (uint64_t)i * pImage->cbTotalBlockData
+                          + (pImage->offStartData + pImage->offStartBlockData);
+                rc = vdiFileWriteSync(pImage, u64Offset, pvTmp, cbBlock, NULL);
+                pImage->paBlocks[uBlockData] = i;
+                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
+                rc = vdiUpdateBlockInfo(pImage, uBlockData);
+                if (RT_FAILURE(rc))
+                    break;
+                paBlocks2[i] = uBlockData;
+                paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
+                cBlocksMoved++;
+            }
+
+            if (pCbProgress && pCbProgress->pfnProgress)
+            {
+                rc = pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                              (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
+
+                if (RT_FAILURE(rc))
+                    break;
+            }
+        }
+        if (RT_FAILURE(rc))
+            break;
+
+        /* Update image header. */
+        setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
+        vdiUpdateHeader(pImage);
+
+        /* Truncate the image to the proper size to finish compacting. */
+        rc = vdiFileSetSize(pImage,
+                              (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
+                            + pImage->offStartData + pImage->offStartBlockData);
+    } while (0);
+
+    if (paBlocks2)
+        RTMemTmpFree(paBlocks2);
+    if (pvTmp)
+        RTMemTmpFree(pvTmp);
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+
+    if (RT_SUCCESS(rc) && pCbProgress && pCbProgress->pfnProgress)
+    {
+        pCbProgress->pfnProgress(pIfProgress->pvUser,
+                                 uPercentStart + uPercentSpan);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnResize */
+static int vdiResize(void *pBackendData, uint64_t cbSize,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation)
+{
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /*
+     * Making the image smaller is not supported at the moment.
+     * Resizing is also not supported for fixed size images and
+     * very old images.
+     */
+    /** @todo implement making the image smaller, it is the responsibility of
+     * the user to know what he's doing. */
+    if (   cbSize < getImageDiskSize(&pImage->Header)
+        || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
+        || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        rc = VERR_NOT_SUPPORTED;
+    else if (cbSize > getImageDiskSize(&pImage->Header))
+    {
+        unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
+        uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header);    /** < New number of blocks in the image after the resize */
+        if (cbSize % getImageBlockSize(&pImage->Header))
+            cBlocksNew++;
+
+        uint32_t cBlocksOld      = getImageBlocks(&pImage->Header);           /** < Number of blocks before the resize. */
+        uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
+        uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
+
+        if (   pImage->offStartData != offStartDataNew
+            && cBlocksAllocated > 0)
+        {
+            /* Calculate how many sectors need to be relocated. */
+            uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
+            unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
+            if (cbOverlapping % getImageBlockSize(&pImage->Header))
+                cBlocksReloc++;
+
+            /* Since only full blocks can be relocated the new data start is
+             * determined by moving it block by block. */
+            cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
+            offStartDataNew = pImage->offStartData;
+
+            /* Do the relocation. */
+            LogFlow(("Relocating %u blocks\n", cBlocksReloc));
+
+            /*
+             * Get the blocks we need to relocate first, they are appended to the end
+             * of the image.
+             */
+            void *pvBuf = NULL, *pvZero = NULL;
+            do
+            {
+                VDIIMAGEBLOCKPOINTER uBlock = 0;
+
+                /* Allocate data buffer. */
+                pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
+                if (!pvBuf)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                /* Allocate buffer for overwriting with zeroes. */
+                pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
+                if (!pvZero)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                for (unsigned i = 0; i < cBlocksReloc; i++)
+                {
+                    /* Search the index in the block table. */
+                    for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
+                    {
+                        if (pImage->paBlocks[idxBlock] == uBlock)
+                        {
+                            /* Read data and append to the end of the image. */
+                            rc = vdiFileReadSync(pImage, offStartDataNew, pvBuf, pImage->cbTotalBlockData, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            uint64_t offBlockAppend;
+                            rc = vdiFileGetSize(pImage, &offBlockAppend);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            rc = vdiFileWriteSync(pImage, offBlockAppend, pvBuf, pImage->cbTotalBlockData, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            /* Zero out the old block area. */
+                            rc = vdiFileWriteSync(pImage, offStartDataNew, pvZero, pImage->cbTotalBlockData, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            /* Update block counter. */
+                            pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
+
+                            /*
+                             * Decrease the block number of all other entries in the array.
+                             * They were moved one block to the front.
+                             * Doing it as a separate step iterating over the array again
+                             * because an error while relocating the block might end up
+                             * in a corrupted image otherwise.
+                             */
+                            for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
+                            {
+                                if (   idxBlock2 != idxBlock
+                                    && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
+                                    pImage->paBlocks[idxBlock2]--;
+                            }
+
+                            /* Continue with the next block. */
+                            break;
+                        }
+                    }
+
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    uBlock++;
+                    offStartDataNew += pImage->cbTotalBlockData;
+                }
+            } while (0);
+
+            if (pvBuf)
+                RTMemFree(pvBuf);
+            if (pvZero)
+                RTMemFree(pvZero);
+        }
+
+        /*
+         * We need to update the new offsets for the image data in the out of memory
+         * case too because we relocated the blocks already.
+         */
+        pImage->offStartData = offStartDataNew;
+        setImageDataOffset(&pImage->Header, offStartDataNew);
+
+        /*
+         * Relocation done, expand the block array and update the header with
+         * the new data.
+         */
+        if (RT_SUCCESS(rc))
+        {
+            PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
+            if (paBlocksNew)
+            {
+                pImage->paBlocks = paBlocksNew;
+
+                /* Mark the new blocks as unallocated. */
+                for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
+                    pImage->paBlocks[idxBlock] = VDI_IMAGE_BLOCK_FREE;
+            }
+            else
+                rc = VERR_NO_MEMORY;
+
+            /* Write the block array before updating the rest. */
+            rc = vdiFileWriteSync(pImage, pImage->offStartBlocks, pImage->paBlocks,
+                                  cbBlockspaceNew, NULL);
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Update size and new block count. */
+                setImageDiskSize(&pImage->Header, cbSize);
+                setImageBlocks(&pImage->Header, cBlocksNew);
+                /* Update geometry. */
+                pImage->PCHSGeometry = *pPCHSGeometry;
+
+                PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
+                if (pGeometry)
+                {
+                    pGeometry->cCylinders = pLCHSGeometry->cCylinders;
+                    pGeometry->cHeads = pLCHSGeometry->cHeads;
+                    pGeometry->cSectors = pLCHSGeometry->cSectors;
+                    pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
+                }
+            }
+        }
+
+        /* Update header information in base image file. */
+        vdiFlushImage(pImage);
+    }
+    /* Same size doesn't change the image at all. */
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_VDIBackend =
+{
+    /* pszBackendName */
+    "VDI",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+      VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
+    | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aVdiFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    vdiCheckIfValid,
+    /* pfnOpen */
+    vdiOpen,
+    /* pfnCreate */
+    vdiCreate,
+    /* pfnRename */
+    vdiRename,
+    /* pfnClose */
+    vdiClose,
+    /* pfnRead */
+    vdiRead,
+    /* pfnWrite */
+    vdiWrite,
+    /* pfnFlush */
+    vdiFlush,
+    /* pfnGetVersion */
+    vdiGetVersion,
+    /* pfnGetSize */
+    vdiGetSize,
+    /* pfnGetFileSize */
+    vdiGetFileSize,
+    /* pfnGetPCHSGeometry */
+    vdiGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    vdiSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    vdiGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    vdiSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    vdiGetImageFlags,
+    /* pfnGetOpenFlags */
+    vdiGetOpenFlags,
+    /* pfnSetOpenFlags */
+    vdiSetOpenFlags,
+    /* pfnGetComment */
+    vdiGetComment,
+    /* pfnSetComment */
+    vdiSetComment,
+    /* pfnGetUuid */
+    vdiGetUuid,
+    /* pfnSetUuid */
+    vdiSetUuid,
+    /* pfnGetModificationUuid */
+    vdiGetModificationUuid,
+    /* pfnSetModificationUuid */
+    vdiSetModificationUuid,
+    /* pfnGetParentUuid */
+    vdiGetParentUuid,
+    /* pfnSetParentUuid */
+    vdiSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    vdiGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    vdiSetParentModificationUuid,
+    /* pfnDump */
+    vdiDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    vdiIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    vdiAsyncRead,
+    /* pfnAsyncWrite */
+    vdiAsyncWrite,
+    /* pfnAsyncFlush */
+    vdiAsyncFlush,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    vdiCompact,
+    /* pfnResize */
+    vdiResize
+};
Index: /trunk/src/VBox/Storage/VDICore.h
===================================================================
--- /trunk/src/VBox/Storage/VDICore.h	(revision 33567)
+++ /trunk/src/VBox/Storage/VDICore.h	(revision 33567)
@@ -0,0 +1,694 @@
+/* $Id$ */
+/** @file
+ * Virtual Disk Image (VDI), Core Code Header (internal).
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef __VDICore_h__
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <VBox/vd.h>
+#ifndef VBOX_VDICORE_VD
+#include <VBox/pdm.h>
+#endif /* !VBOX_VDICORE_VD */
+#include <VBox/mm.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+
+
+/*******************************************************************************
+*   Constants And Macros, Structures and Typedefs                              *
+*******************************************************************************/
+
+/** Image info, not handled anyhow.
+ *  Must be less than 64 bytes in length, including the trailing 0.
+ */
+#define VDI_IMAGE_FILE_INFO   "<<< Oracle VM VirtualBox Disk Image >>>\n"
+
+/** The Sector size.
+ * Currently we support only 512 bytes sectors.
+ */
+#define VDI_GEOMETRY_SECTOR_SIZE    (512)
+/**  512 = 2^^9 */
+#define VDI_GEOMETRY_SECTOR_SHIFT   (9)
+
+/**
+ * Harddisk geometry.
+ */
+#pragma pack(1)
+typedef struct VDIDISKGEOMETRY
+{
+    /** Cylinders. */
+    uint32_t    cCylinders;
+    /** Heads. */
+    uint32_t    cHeads;
+    /** Sectors per track. */
+    uint32_t    cSectors;
+    /** Sector size. (bytes per sector) */
+    uint32_t    cbSector;
+} VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
+#pragma pack()
+
+/** Image signature. */
+#define VDI_IMAGE_SIGNATURE   (0xbeda107f)
+
+/**
+ * Pre-Header to be stored in image file - used for version control.
+ */
+#pragma pack(1)
+typedef struct VDIPREHEADER
+{
+    /** Just text info about image type, for eyes only. */
+    char            szFileInfo[64];
+    /** The image signature (VDI_IMAGE_SIGNATURE). */
+    uint32_t        u32Signature;
+    /** The image version (VDI_IMAGE_VERSION). */
+    uint32_t        u32Version;
+} VDIPREHEADER, *PVDIPREHEADER;
+#pragma pack()
+
+/**
+ * Size of szComment field of HDD image header.
+ */
+#define VDI_IMAGE_COMMENT_SIZE    256
+
+/**
+ * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
+ * Prepended by VDIPREHEADER.
+ */
+#pragma pack(1)
+typedef struct VDIHEADER0
+{
+    /** The image type (VDI_IMAGE_TYPE_*). */
+    uint32_t        u32Type;
+    /** Image flags (VDI_IMAGE_FLAGS_*). */
+    uint32_t        fFlags;
+    /** Image comment. (UTF-8) */
+    char            szComment[VDI_IMAGE_COMMENT_SIZE];
+    /** Legacy image geometry (previous code stored PCHS there). */
+    VDIDISKGEOMETRY LegacyGeometry;
+    /** Size of disk (in bytes). */
+    uint64_t        cbDisk;
+    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
+    uint32_t        cbBlock;
+    /** Number of blocks. */
+    uint32_t        cBlocks;
+    /** Number of allocated blocks. */
+    uint32_t        cBlocksAllocated;
+    /** UUID of image. */
+    RTUUID          uuidCreate;
+    /** UUID of image's last modification. */
+    RTUUID          uuidModify;
+    /** Only for secondary images - UUID of primary image. */
+    RTUUID          uuidLinkage;
+} VDIHEADER0, *PVDIHEADER0;
+#pragma pack()
+
+/**
+ * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
+ * VDI_IMAGE_VERSION_MINOR = 1. Prepended by VDIPREHEADER.
+ */
+#pragma pack(1)
+typedef struct VDIHEADER1
+{
+    /** Size of this structure in bytes. */
+    uint32_t        cbHeader;
+    /** The image type (VDI_IMAGE_TYPE_*). */
+    uint32_t        u32Type;
+    /** Image flags (VDI_IMAGE_FLAGS_*). */
+    uint32_t        fFlags;
+    /** Image comment. (UTF-8) */
+    char            szComment[VDI_IMAGE_COMMENT_SIZE];
+    /** Offset of Blocks array from the beginning of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offBlocks;
+    /** Offset of image data from the beginning of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offData;
+    /** Legacy image geometry (previous code stored PCHS there). */
+    VDIDISKGEOMETRY LegacyGeometry;
+    /** Was BIOS HDD translation mode, now unused. */
+    uint32_t        u32Dummy;
+    /** Size of disk (in bytes). */
+    uint64_t        cbDisk;
+    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
+    uint32_t        cbBlock;
+    /** Size of additional service information of every data block.
+     * Prepended before block data. May be 0.
+     * Should be a power of 2 and sector-aligned for optimization reasons. */
+    uint32_t        cbBlockExtra;
+    /** Number of blocks. */
+    uint32_t        cBlocks;
+    /** Number of allocated blocks. */
+    uint32_t        cBlocksAllocated;
+    /** UUID of image. */
+    RTUUID          uuidCreate;
+    /** UUID of image's last modification. */
+    RTUUID          uuidModify;
+    /** Only for secondary images - UUID of previous image. */
+    RTUUID          uuidLinkage;
+    /** Only for secondary images - UUID of previous image's last modification. */
+    RTUUID          uuidParentModify;
+} VDIHEADER1, *PVDIHEADER1;
+#pragma pack()
+
+/**
+ * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1,
+ * VDI_IMAGE_VERSION_MINOR = 1, the slightly changed variant necessary as the
+ * old released code doesn't support changing the minor version at all.
+ */
+#pragma pack(1)
+typedef struct VDIHEADER1PLUS
+{
+    /** Size of this structure in bytes. */
+    uint32_t        cbHeader;
+    /** The image type (VDI_IMAGE_TYPE_*). */
+    uint32_t        u32Type;
+    /** Image flags (VDI_IMAGE_FLAGS_*). */
+    uint32_t        fFlags;
+    /** Image comment. (UTF-8) */
+    char            szComment[VDI_IMAGE_COMMENT_SIZE];
+    /** Offset of blocks array from the beginning of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offBlocks;
+    /** Offset of image data from the beginning of image file.
+     * Should be sector-aligned for HDD access optimization. */
+    uint32_t        offData;
+    /** Legacy image geometry (previous code stored PCHS there). */
+    VDIDISKGEOMETRY LegacyGeometry;
+    /** Was BIOS HDD translation mode, now unused. */
+    uint32_t        u32Dummy;
+    /** Size of disk (in bytes). */
+    uint64_t        cbDisk;
+    /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
+    uint32_t        cbBlock;
+    /** Size of additional service information of every data block.
+     * Prepended before block data. May be 0.
+     * Should be a power of 2 and sector-aligned for optimization reasons. */
+    uint32_t        cbBlockExtra;
+    /** Number of blocks. */
+    uint32_t        cBlocks;
+    /** Number of allocated blocks. */
+    uint32_t        cBlocksAllocated;
+    /** UUID of image. */
+    RTUUID          uuidCreate;
+    /** UUID of image's last modification. */
+    RTUUID          uuidModify;
+    /** Only for secondary images - UUID of previous image. */
+    RTUUID          uuidLinkage;
+    /** Only for secondary images - UUID of previous image's last modification. */
+    RTUUID          uuidParentModify;
+    /** LCHS image geometry (new field in VDI1.2 version. */
+    VDIDISKGEOMETRY LCHSGeometry;
+} VDIHEADER1PLUS, *PVDIHEADER1PLUS;
+#pragma pack()
+
+/**
+ * Header structure for all versions.
+ */
+typedef struct VDIHEADER
+{
+    unsigned        uVersion;
+    union
+    {
+        VDIHEADER0    v0;
+        VDIHEADER1    v1;
+        VDIHEADER1PLUS v1plus;
+    } u;
+} VDIHEADER, *PVDIHEADER;
+
+/**
+ * File alignment boundary for both the block array and data area. Should be
+ * at least the size of a physical sector on disk for performance reasons.
+ * With the growing market share of disks with 4K sectors this needs to be
+ * bumped, and maybe again later. */
+#define VDI_DATA_ALIGN _4K
+
+/** Block 'pointer'. */
+typedef uint32_t    VDIIMAGEBLOCKPOINTER;
+/** Pointer to a block 'pointer'. */
+typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
+
+/**
+ * Block marked as free is not allocated in image file, read from this
+ * block may returns any random data.
+ */
+#define VDI_IMAGE_BLOCK_FREE   ((VDIIMAGEBLOCKPOINTER)~0)
+
+/**
+ * Block marked as zero is not allocated in image file, read from this
+ * block returns zeroes.
+ */
+#define VDI_IMAGE_BLOCK_ZERO   ((VDIIMAGEBLOCKPOINTER)~1)
+
+/**
+ * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
+ * allocated in image file.
+ */
+#define VDI_IMAGE_BLOCK_UNALLOCATED   (VDI_IMAGE_BLOCK_ZERO)
+#define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp)   (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
+
+#define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
+#define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
+
+#ifdef VBOX_VDICORE_VD
+/** @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;
+/** @} */
+#endif /* VBOX_VDICORE_VD */
+
+/*******************************************************************************
+*   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;
+}
+
+#ifdef VBOX_VDICORE_VD
+DECLINLINE(unsigned) getImageFlags(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0:
+            /* VDI image flag conversion to VD image flags. */
+            return ph->u.v0.fFlags << 8;
+        case 1:
+            /* VDI image flag conversion to VD image flags. */
+            return ph->u.v1.fFlags << 8;
+    }
+    AssertFailed();
+    return 0;
+}
+#else /* !VBOX_VDICORE_VD */
+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;
+}
+#endif /* !VBOX_VDICORE_VD */
+
+DECLINLINE(char *) getImageComment(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return &ph->u.v0.szComment[0];
+        case 1: return &ph->u.v1.szComment[0];
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(unsigned) getImageBlocksOffset(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
+        case 1: return ph->u.v1.offBlocks;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(uint32_t) getImageDataOffset(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
+                       (ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
+        case 1: return ph->u.v1.offData;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(void) setImageDataOffset(PVDIHEADER ph, uint32_t offData)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return;
+        case 1: ph->u.v1.offData = offData; return;
+    }
+    AssertFailed();
+}
+
+DECLINLINE(PVDIDISKGEOMETRY) getImageLCHSGeometry(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return NULL;
+        case 1:
+            switch (GET_MINOR_HEADER_VERSION(ph))
+            {
+                case 1:
+                    if (ph->u.v1.cbHeader < sizeof(ph->u.v1plus))
+                        return NULL;
+                    else
+                        return &ph->u.v1plus.LCHSGeometry;
+            }
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(uint64_t) getImageDiskSize(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return ph->u.v0.cbDisk;
+        case 1: return ph->u.v1.cbDisk;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(void) setImageDiskSize(PVDIHEADER ph, uint64_t cbDisk)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: ph->u.v0.cbDisk = cbDisk; return;
+        case 1: ph->u.v1.cbDisk = cbDisk; return;
+    }
+    AssertFailed();
+}
+
+DECLINLINE(unsigned) getImageBlockSize(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return ph->u.v0.cbBlock;
+        case 1: return ph->u.v1.cbBlock;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(unsigned) getImageExtraBlockSize(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return 0;
+        case 1: return ph->u.v1.cbBlockExtra;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(unsigned) getImageBlocks(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return ph->u.v0.cBlocks;
+        case 1: return ph->u.v1.cBlocks;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(void) setImageBlocks(PVDIHEADER ph, unsigned cBlocks)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: ph->u.v0.cBlocks = cBlocks; return;
+        case 1: ph->u.v1.cBlocks = cBlocks; return;
+    }
+    AssertFailed();
+}
+
+
+DECLINLINE(unsigned) getImageBlocksAllocated(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return ph->u.v0.cBlocksAllocated;
+        case 1: return ph->u.v1.cBlocksAllocated;
+    }
+    AssertFailed();
+    return 0;
+}
+
+DECLINLINE(void) setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: ph->u.v0.cBlocksAllocated = cBlocks; return;
+        case 1: ph->u.v1.cBlocksAllocated = cBlocks; return;
+    }
+    AssertFailed();
+}
+
+DECLINLINE(PRTUUID) getImageCreationUUID(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return &ph->u.v0.uuidCreate;
+        case 1: return &ph->u.v1.uuidCreate;
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(PRTUUID) getImageModificationUUID(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return &ph->u.v0.uuidModify;
+        case 1: return &ph->u.v1.uuidModify;
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(PRTUUID) getImageParentUUID(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 0: return &ph->u.v0.uuidLinkage;
+        case 1: return &ph->u.v1.uuidLinkage;
+    }
+    AssertFailed();
+    return NULL;
+}
+
+DECLINLINE(PRTUUID) getImageParentModificationUUID(PVDIHEADER ph)
+{
+    switch (GET_MAJOR_HEADER_VERSION(ph))
+    {
+        case 1: return &ph->u.v1.uuidParentModify;
+    }
+    AssertFailed();
+    return NULL;
+}
+
+#ifndef VBOX_VDICORE_VD
+/**
+ * Default image block size, may be changed by setBlockSize/getBlockSize.
+ *
+ * Note: for speed reasons block size should be a power of 2 !
+ */
+#define VDI_IMAGE_DEFAULT_BLOCK_SIZE            _1M
+#endif /* !VBOX_VDICORE_VD */
+
+#ifndef VBOX_VDICORE_VD
+/**
+ * fModified bit flags.
+ */
+#define VDI_IMAGE_MODIFIED_FLAG                 RT_BIT(0)
+#define VDI_IMAGE_MODIFIED_FIRST                RT_BIT(1)
+#define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE  RT_BIT(2)
+#endif /* !VBOX_VDICORE_VD */
+
+/**
+ * Image structure
+ */
+typedef struct VDIIMAGEDESC
+{
+#ifndef VBOX_VDICORE_VD
+    /** Link to parent image descriptor, if any. */
+    struct VDIIMAGEDESC    *pPrev;
+    /** Link to child image descriptor, if any. */
+    struct VDIIMAGEDESC    *pNext;
+#endif /* !VBOX_VDICORE_VD */
+    /** Opaque storage handle. */
+    PVDIOSTORAGE            pStorage;
+#ifndef VBOX_VDICORE_VD
+    /** True if the image is operating in readonly mode. */
+    bool                    fReadOnly;
+    /** Image open flags, VDI_OPEN_FLAGS_*. */
+    unsigned                fOpen;
+#else /* VBOX_VDICORE_VD */
+    /** Image open flags, VD_OPEN_FLAGS_*. */
+    unsigned                uOpenFlags;
+#endif /* VBOX_VDICORE_VD */
+    /** Image pre-header. */
+    VDIPREHEADER            PreHeader;
+    /** Image header. */
+    VDIHEADER               Header;
+    /** Pointer to a block array. */
+    PVDIIMAGEBLOCKPOINTER   paBlocks;
+#ifndef VBOX_VDICORE_VD
+    /** fFlags copy from image header, for speed optimization. */
+    unsigned                fFlags;
+#else /* VBOX_VDICORE_VD */
+    /** fFlags copy from image header, for speed optimization. */
+    unsigned                uImageFlags;
+#endif /* VBOX_VDICORE_VD */
+    /** 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;
+#ifndef VBOX_VDICORE_VD
+    /** Block shift value for converting block index into offset in image. */
+    unsigned                uShiftIndex2Offset;
+#endif /* !VBOX_VDICORE_VD */
+    /** Offset of data from the beginning of block. */
+    unsigned                offStartBlockData;
+#ifndef VBOX_VDICORE_VD
+    /** 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];
+#else /* VBOX_VDICORE_VD */
+    /** Total size of image block (including the extra data). */
+    unsigned                cbTotalBlockData;
+    /** Container filename. (UTF-8) */
+    const char             *pszFilename;
+    /** Physical geometry of this image (never actually stored). */
+    VDGEOMETRY              PCHSGeometry;
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE            pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE            pVDIfsImage;
+    /** Error interface. */
+    PVDINTERFACE            pInterfaceError;
+    /** Error interface callback table. */
+    PVDINTERFACEERROR       pInterfaceErrorCallbacks;
+    /** I/O interface. */
+    PVDINTERFACE            pInterfaceIO;
+    /** I/O interface callbacks. */
+    PVDINTERFACEIOINT       pInterfaceIOCallbacks;
+#endif /* VBOX_VDICORE_VD */
+} VDIIMAGEDESC, *PVDIIMAGEDESC;
+
+#ifndef VBOX_VDICORE_VD
+/**
+ * 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)
+#endif /* !VBOX_VDICORE_VD */
+
+/** 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;
+
+#ifndef VBOX_VDICORE_VD
+    /** The media interface. */
+    PDMIMEDIA       IMedia;
+    /** Pointer to the driver instance. */
+    PPDMDRVINS      pDrvIns;
+#endif /* !VBOX_VDICORE_VD */
+};
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+RT_C_DECLS_BEGIN
+
+#ifndef VBOX_VDICORE_VD
+VBOXDDU_DECL(void) vdiInitVDIDisk(PVDIDISK pDisk);
+VBOXDDU_DECL(void) VDIFlushImage(PVDIIMAGEDESC pImage);
+VBOXDDU_DECL(int)  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
+#endif /* !VBOX_VDICORE_VD */
+
+RT_C_DECLS_END
+
+#endif
Index: /trunk/src/VBox/Storage/VHD.cpp
===================================================================
--- /trunk/src/VBox/Storage/VHD.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/VHD.cpp	(revision 33567)
@@ -0,0 +1,3115 @@
+/* $Id$ */
+/** @file
+ * VHD Disk image, Core Code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_VHD
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#define VHD_RELATIVE_MAX_PATH 512
+#define VHD_ABSOLUTE_MAX_PATH 512
+
+#define VHD_SECTOR_SIZE 512
+#define VHD_BLOCK_SIZE  (2 * _1M)
+
+/* This is common to all VHD disk types and is located at the end of the image */
+#pragma pack(1)
+typedef struct VHDFooter
+{
+    char     Cookie[8];
+    uint32_t Features;
+    uint32_t Version;
+    uint64_t DataOffset;
+    uint32_t TimeStamp;
+    uint8_t  CreatorApp[4];
+    uint32_t CreatorVer;
+    uint32_t CreatorOS;
+    uint64_t OrigSize;
+    uint64_t CurSize;
+    uint16_t DiskGeometryCylinder;
+    uint8_t  DiskGeometryHeads;
+    uint8_t  DiskGeometrySectors;
+    uint32_t DiskType;
+    uint32_t Checksum;
+    char     UniqueID[16];
+    uint8_t  SavedState;
+    uint8_t  Reserved[427];
+} VHDFooter;
+#pragma pack()
+
+/* this really is spelled with only one n */
+#define VHD_FOOTER_COOKIE "conectix"
+#define VHD_FOOTER_COOKIE_SIZE 8
+
+#define VHD_FOOTER_FEATURES_NOT_ENABLED   0
+#define VHD_FOOTER_FEATURES_TEMPORARY     1
+#define VHD_FOOTER_FEATURES_RESERVED      2
+
+#define VHD_FOOTER_FILE_FORMAT_VERSION    0x00010000
+#define VHD_FOOTER_DATA_OFFSET_FIXED      UINT64_C(0xffffffffffffffff)
+#define VHD_FOOTER_DISK_TYPE_FIXED        2
+#define VHD_FOOTER_DISK_TYPE_DYNAMIC      3
+#define VHD_FOOTER_DISK_TYPE_DIFFERENCING 4
+
+#define VHD_MAX_LOCATOR_ENTRIES           8
+#define VHD_PLATFORM_CODE_NONE            0
+#define VHD_PLATFORM_CODE_WI2R            0x57693272
+#define VHD_PLATFORM_CODE_WI2K            0x5769326B
+#define VHD_PLATFORM_CODE_W2RU            0x57327275
+#define VHD_PLATFORM_CODE_W2KU            0x57326B75
+#define VHD_PLATFORM_CODE_MAC             0x4D163220
+#define VHD_PLATFORM_CODE_MACX            0x4D163258
+
+/* Header for expanding disk images. */
+#pragma pack(1)
+typedef struct VHDParentLocatorEntry
+{
+    uint32_t u32Code;
+    uint32_t u32DataSpace;
+    uint32_t u32DataLength;
+    uint32_t u32Reserved;
+    uint64_t u64DataOffset;
+} VHDPLE, *PVHDPLE;
+
+typedef struct VHDDynamicDiskHeader
+{
+    char     Cookie[8];
+    uint64_t DataOffset;
+    uint64_t TableOffset;
+    uint32_t HeaderVersion;
+    uint32_t MaxTableEntries;
+    uint32_t BlockSize;
+    uint32_t Checksum;
+    uint8_t  ParentUuid[16];
+    uint32_t ParentTimeStamp;
+    uint32_t Reserved0;
+    uint16_t ParentUnicodeName[256];
+    VHDPLE   ParentLocatorEntry[VHD_MAX_LOCATOR_ENTRIES];
+    uint8_t  Reserved1[256];
+} VHDDynamicDiskHeader;
+#pragma pack()
+
+#define VHD_DYNAMIC_DISK_HEADER_COOKIE "cxsparse"
+#define VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE 8
+#define VHD_DYNAMIC_DISK_HEADER_VERSION 0x00010000
+
+/**
+ * Complete VHD image data structure.
+ */
+typedef struct VHDIMAGE
+{
+    /** Image file name. */
+    const char       *pszFilename;
+    /** Opaque storage handle. */
+    PVDIOSTORAGE      pStorage;
+
+    /** I/O interface. */
+    PVDINTERFACE      pInterfaceIO;
+    /** I/O interface callbacks. */
+    PVDINTERFACEIOINT pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE      pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE      pVDIfsImage;
+    /** Error interface. */
+    PVDINTERFACE      pInterfaceError;
+    /** Error interface callback table. */
+    PVDINTERFACEERROR pInterfaceErrorCallbacks;
+
+    /** Open flags passed by VBoxHDD layer. */
+    unsigned        uOpenFlags;
+    /** Image flags defined during creation or determined during open. */
+    unsigned        uImageFlags;
+    /** Total size of the image. */
+    uint64_t        cbSize;
+
+    /** Physical geometry of this image. */
+    VDGEOMETRY      PCHSGeometry;
+    /** Logical geometry of this image. */
+    VDGEOMETRY      LCHSGeometry;
+
+    /** Image UUID. */
+    RTUUID          ImageUuid;
+    /** Parent image UUID. */
+    RTUUID          ParentUuid;
+
+    /** Parent's time stamp at the time of image creation. */
+    uint32_t        u32ParentTimeStamp;
+    /** Relative path to the parent image. */
+    char            *pszParentFilename;
+
+    /** The Block Allocation Table. */
+    uint32_t        *pBlockAllocationTable;
+    /** Number of entries in the table. */
+    uint32_t        cBlockAllocationTableEntries;
+
+    /** Size of one data block. */
+    uint32_t        cbDataBlock;
+    /** Sectors per data block. */
+    uint32_t        cSectorsPerDataBlock;
+    /** Length of the sector bitmap in bytes. */
+    uint32_t        cbDataBlockBitmap;
+    /** A copy of the disk footer. */
+    VHDFooter       vhdFooterCopy;
+    /** Current end offset of the file (without the disk footer). */
+    uint64_t        uCurrentEndOfFile;
+    /** Size of the data block bitmap in sectors. */
+    uint32_t        cDataBlockBitmapSectors;
+    /** Start of the block allocation table. */
+    uint64_t        uBlockAllocationTableOffset;
+    /** Buffer to hold block's bitmap for bit search operations. */
+    uint8_t         *pu8Bitmap;
+    /** Offset to the next data structure (dynamic disk header). */
+    uint64_t        u64DataOffset;
+    /** Flag to force dynamic disk header update. */
+    bool            fDynHdrNeedsUpdate;
+} VHDIMAGE, *PVHDIMAGE;
+
+/**
+ * Structure tracking the expansion process of the image
+ * for async access.
+ */
+typedef struct VHDIMAGEEXPAND
+{
+    /** Flag indicating the status of each step. */
+    volatile uint32_t fFlags;
+    /** The index in the  block allocation table which is written. */
+    uint32_t          idxBatAllocated;
+    /** Big endian representation of the block index
+     * which is written in the BAT. */
+    uint32_t          idxBlockBe;
+    /** Old end of the file - used for rollback in case of an error. */
+    uint64_t          cbEofOld;
+    /** Sector bitmap written to the new block - variable in size. */
+    uint8_t           au8Bitmap[1];
+} VHDIMAGEEXPAND, *PVHDIMAGEEXPAND;
+
+/**
+ * Flag defines
+ */
+#define VHDIMAGEEXPAND_STEP_IN_PROGRESS (0x0)
+#define VHDIMAGEEXPAND_STEP_FAILED      (0x2)
+#define VHDIMAGEEXPAND_STEP_SUCCESS     (0x3)
+/** All steps completed successfully. */
+#define VHDIMAGEEXPAND_ALL_SUCCESS      (0xff)
+/** All steps completed (no success indicator) */
+#define VHDIMAGEEXPAND_ALL_COMPLETE     (0xaa)
+
+/** Every status field has 2 bits so we can encode 4 steps in one byte. */
+#define VHDIMAGEEXPAND_STATUS_MASK              0x03
+#define VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT 0x00
+#define VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT   0x02
+#define VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT      0x04
+#define VHDIMAGEEXPAND_BAT_STATUS_SHIFT         0x06
+
+/**
+ * Helper macros to get and set the status field.
+ */
+#define VHDIMAGEEXPAND_STATUS_GET(fFlags, cShift) \
+    (((fFlags) >> (cShift)) & VHDIMAGEEXPAND_STATUS_MASK)
+#define VHDIMAGEEXPAND_STATUS_SET(fFlags, cShift, uVal) \
+    ASMAtomicOrU32(&(fFlags), ((uVal) & VHDIMAGEEXPAND_STATUS_MASK) << (cShift))
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aVhdFileExtensions[] =
+{
+    {"vhd", VDTYPE_HDD},
+    {NULL, VDTYPE_INVALID}
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) vhdError(PVHDIMAGE pImage, int rc, RT_SRC_POS_DECL,
+                         const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) vhdMessage(PVHDIMAGE pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+
+DECLINLINE(int) vhdFileOpen(PVHDIMAGE pImage, const char *pszFilename,
+                            uint32_t fOpen)
+{
+    return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                  pszFilename, fOpen,
+                                                  &pImage->pStorage);
+}
+
+DECLINLINE(int) vhdFileClose(PVHDIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                   pImage->pStorage);
+}
+
+DECLINLINE(int) vhdFileDelete(PVHDIMAGE pImage, const char *pszFilename)
+{
+    return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                    pszFilename);
+}
+
+DECLINLINE(int) vhdFileMove(PVHDIMAGE pImage, const char *pszSrc,
+                            const char *pszDst, unsigned fMove)
+{
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+DECLINLINE(int) vhdFileGetFreeSpace(PVHDIMAGE pImage, const char *pszFilename,
+                                    int64_t *pcbFree)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
+                                                          pszFilename, pcbFree);
+}
+
+DECLINLINE(int) vhdFileGetModificationTime(PVHDIMAGE pImage,
+                                           const char *pszFilename,
+                                           PRTTIMESPEC pModificationTime)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
+                                                                 pszFilename,
+                                                                 pModificationTime);
+}
+
+DECLINLINE(int) vhdFileGetSize(PVHDIMAGE pImage, uint64_t *pcbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, pcbSize);
+}
+
+DECLINLINE(int) vhdFileSetSize(PVHDIMAGE pImage, uint64_t cbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pImage->pStorage, cbSize);
+}
+
+DECLINLINE(int) vhdFileWriteSync(PVHDIMAGE pImage, uint64_t uOffset,
+                                 const void *pvBuffer, size_t cbBuffer,
+                                 size_t *pcbWritten)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage, uOffset,
+                                                       pvBuffer, cbBuffer, pcbWritten);
+}
+
+DECLINLINE(int) vhdFileReadSync(PVHDIMAGE pImage, uint64_t uOffset,
+                                void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pImage->pStorage, uOffset,
+                                                      pvBuffer, cbBuffer, pcbRead);
+}
+
+DECLINLINE(int) vhdFileFlushSync(PVHDIMAGE pImage)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pImage->pStorage);
+}
+
+DECLINLINE(int) vhdFileReadUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
+                                     PVDIOCTX pIoCtx, size_t cbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
+                                                           pImage->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+DECLINLINE(int) vhdFileWriteUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
+                                      PVDIOCTX pIoCtx, size_t cbWrite,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) vhdFileReadMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
+                                     void *pvBuffer, size_t cbBuffer,
+                                     PVDIOCTX pIoCtx, PPVDMETAXFER ppMetaXfer,
+                                     PFNVDXFERCOMPLETED pfnComplete,
+                                     void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                           pImage->pStorage,
+                                                           uOffset, pvBuffer,
+                                                           cbBuffer, pIoCtx,
+                                                           ppMetaXfer,
+                                                           pfnComplete,
+                                                           pvCompleteUser);
+}
+
+DECLINLINE(int) vhdFileWriteMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
+                                      void *pvBuffer, size_t cbBuffer,
+                                      PVDIOCTX pIoCtx,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                            pImage->pStorage,
+                                                            uOffset, pvBuffer,
+                                                            cbBuffer, pIoCtx,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+DECLINLINE(int) vhdFileFlushAsync(PVHDIMAGE pImage, PVDIOCTX pIoCtx,
+                                  PFNVDXFERCOMPLETED pfnComplete,
+                                  void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                        pImage->pStorage,
+                                                        pIoCtx, pfnComplete,
+                                                        pvCompleteUser);
+}
+
+DECLINLINE(void) vhdFileMetaXferRelease(PVHDIMAGE pImage, PVDMETAXFER pMetaXfer)
+{
+    pImage->pInterfaceIOCallbacks->pfnMetaXferRelease(pImage->pInterfaceIO->pvUser,
+                                                      pMetaXfer);
+}
+
+
+/**
+ * Internal: Compute and update header checksum.
+ */
+static uint32_t vhdChecksum(void *pHeader, uint32_t cbSize)
+{
+    uint32_t checksum = 0;
+    for (uint32_t i = 0; i < cbSize; i++)
+        checksum += ((unsigned char *)pHeader)[i];
+    return ~checksum;
+}
+
+/**
+ * Internal: Convert filename to UTF16 with appropriate endianness.
+ */
+static int vhdFilenameToUtf16(const char *pszFilename, uint16_t *pu16Buf,
+                              uint32_t cbBufSize, uint32_t *pcbActualSize,
+                              bool fBigEndian)
+{
+    int      rc;
+    PRTUTF16 tmp16 = NULL;
+    size_t   cTmp16Len;
+
+    rc = RTStrToUtf16(pszFilename, &tmp16);
+    if (RT_FAILURE(rc))
+        goto out;
+    cTmp16Len = RTUtf16Len(tmp16);
+    if (cTmp16Len * sizeof(*tmp16) > cbBufSize)
+    {
+        rc = VERR_FILENAME_TOO_LONG;
+        goto out;
+    }
+
+    if (fBigEndian)
+        for (unsigned i = 0; i < cTmp16Len; i++)
+            pu16Buf[i] = RT_H2BE_U16(tmp16[i]);
+    else
+        memcpy(pu16Buf, tmp16, cTmp16Len * sizeof(*tmp16));
+    if (pcbActualSize)
+        *pcbActualSize = (uint32_t)(cTmp16Len * sizeof(*tmp16));
+
+out:
+    if (tmp16)
+        RTUtf16Free(tmp16);
+    return rc;
+}
+
+/**
+ * Internal: Update one locator entry.
+ */
+static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszFilename)
+{
+    int      rc;
+    uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE;
+    void     *pvBuf = RTMemTmpAllocZ(cbMaxLen);
+    char     *pszTmp;
+
+    if (!pvBuf)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    switch (RT_BE2H_U32(pLocator->u32Code))
+    {
+        case VHD_PLATFORM_CODE_WI2R:
+            /* Update plain relative name. */
+            cb = (uint32_t)strlen(pszFilename);
+            if (cb > cbMaxLen)
+            {
+                rc = VERR_FILENAME_TOO_LONG;
+                goto out;
+            }
+            memcpy(pvBuf, pszFilename, cb);
+            pLocator->u32DataLength = RT_H2BE_U32(cb);
+            break;
+        case VHD_PLATFORM_CODE_WI2K:
+            /* Update plain absolute name. */
+            rc = RTPathAbs(pszFilename, (char *)pvBuf, cbMaxLen);
+            if (RT_FAILURE(rc))
+                goto out;
+            pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
+            break;
+        case VHD_PLATFORM_CODE_W2RU:
+            /* Update unicode relative name. */
+            rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
+            if (RT_FAILURE(rc))
+                goto out;
+            pLocator->u32DataLength = RT_H2BE_U32(cb);
+            break;
+        case VHD_PLATFORM_CODE_W2KU:
+            /* Update unicode absolute name. */
+            pszTmp = (char*)RTMemTmpAllocZ(cbMaxLen);
+            if (!pszTmp)
+            {
+                rc = VERR_NO_MEMORY;
+                goto out;
+            }
+            rc = RTPathAbs(pszFilename, pszTmp, cbMaxLen);
+            if (RT_FAILURE(rc))
+            {
+                RTMemTmpFree(pszTmp);
+                goto out;
+            }
+            rc = vhdFilenameToUtf16(pszTmp, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
+            RTMemTmpFree(pszTmp);
+            if (RT_FAILURE(rc))
+                goto out;
+            pLocator->u32DataLength = RT_H2BE_U32(cb);
+            break;
+        default:
+            rc = VERR_NOT_IMPLEMENTED;
+            goto out;
+    }
+    rc = vhdFileWriteSync(pImage, RT_BE2H_U64(pLocator->u64DataOffset),
+                          pvBuf, RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE,
+                          NULL);
+
+out:
+    if (pvBuf)
+        RTMemTmpFree(pvBuf);
+    return rc;
+}
+
+/**
+ * Internal: Update dynamic disk header from VHDIMAGE.
+ */
+static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
+{
+    VHDDynamicDiskHeader ddh;
+    int rc, i;
+
+    if (!pImage)
+        return VERR_VD_NOT_OPENED;
+
+    rc = vhdFileReadSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    if (memcmp(ddh.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE) != 0)
+        return VERR_VD_VHD_INVALID_HEADER;
+
+    uint32_t u32Checksum = RT_BE2H_U32(ddh.Checksum);
+    ddh.Checksum = 0;
+    if (u32Checksum != vhdChecksum(&ddh, sizeof(ddh)))
+        return VERR_VD_VHD_INVALID_HEADER;
+
+    /* Update parent's timestamp. */
+    ddh.ParentTimeStamp = RT_H2BE_U32(pImage->u32ParentTimeStamp);
+    /* Update parent's filename. */
+    if (pImage->pszParentFilename)
+    {
+        rc = vhdFilenameToUtf16(RTPathFilename(pImage->pszParentFilename),
+             ddh.ParentUnicodeName, sizeof(ddh.ParentUnicodeName) - 1, NULL, true);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /* Update parent's locators. */
+    for (i = 0; i < VHD_MAX_LOCATOR_ENTRIES; i++)
+    {
+        /* Skip empty locators */
+        if (ddh.ParentLocatorEntry[i].u32Code != RT_H2BE_U32(VHD_PLATFORM_CODE_NONE))
+        {
+            if (pImage->pszParentFilename)
+            {
+                rc = vhdLocatorUpdate(pImage, &ddh.ParentLocatorEntry[i], pImage->pszParentFilename);
+                if (RT_FAILURE(rc))
+                    return rc;
+            }
+            else
+            {
+                /* The parent was deleted. */
+                ddh.ParentLocatorEntry[i].u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_NONE);
+            }
+        }
+    }
+    /* Update parent's UUID */
+    memcpy(ddh.ParentUuid, pImage->ParentUuid.au8, sizeof(ddh.ParentUuid));
+
+    /* Update data offset and number of table entries. */
+    ddh.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
+
+    ddh.Checksum = 0;
+    ddh.Checksum = RT_H2BE_U32(vhdChecksum(&ddh, sizeof(ddh)));
+    rc = vhdFileWriteSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
+    return rc;
+}
+
+/**
+ * Internal: Update the VHD footer.
+ */
+static int vhdUpdateFooter(PVHDIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Update fields which can change. */
+    pImage->vhdFooterCopy.CurSize              = RT_H2BE_U64(pImage->cbSize);
+    pImage->vhdFooterCopy.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
+    pImage->vhdFooterCopy.DiskGeometryHeads    = pImage->PCHSGeometry.cHeads;
+    pImage->vhdFooterCopy.DiskGeometrySectors  = pImage->PCHSGeometry.cSectors;
+
+    pImage->vhdFooterCopy.Checksum = 0;
+    pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
+
+    if (pImage->pBlockAllocationTable)
+        rc = vhdFileWriteSync(pImage, 0, &pImage->vhdFooterCopy,
+                              sizeof(VHDFooter), NULL);
+
+    if (RT_SUCCESS(rc))
+        rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
+                              &pImage->vhdFooterCopy, sizeof(VHDFooter), NULL);
+
+    return rc;
+}
+
+/**
+ * Internal. Flush image data to disk.
+ */
+static int vhdFlushImage(PVHDIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        return VINF_SUCCESS;
+
+    if (pImage->pBlockAllocationTable)
+    {
+        /*
+         * This is an expanding image. Write the BAT and copy of the disk footer.
+         */
+        size_t   cbBlockAllocationTableToWrite = pImage->cBlockAllocationTableEntries * sizeof(uint32_t);
+        uint32_t *pBlockAllocationTableToWrite = (uint32_t *)RTMemAllocZ(cbBlockAllocationTableToWrite);
+
+        if (!pBlockAllocationTableToWrite)
+            return VERR_NO_MEMORY;
+
+        /*
+         * The BAT entries have to be stored in big endian format.
+         */
+        for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
+            pBlockAllocationTableToWrite[i] = RT_H2BE_U32(pImage->pBlockAllocationTable[i]);
+
+        /*
+         * Write the block allocation table after the copy of the disk footer and the dynamic disk header.
+         */
+        vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
+                         pBlockAllocationTableToWrite,
+                         cbBlockAllocationTableToWrite, NULL);
+        if (pImage->fDynHdrNeedsUpdate)
+            rc = vhdDynamicHeaderUpdate(pImage);
+        RTMemFree(pBlockAllocationTableToWrite);
+    }
+
+    if (RT_SUCCESS(rc))
+        rc = vhdUpdateFooter(pImage);
+
+    if (RT_SUCCESS(rc))
+        rc = vhdFileFlushSync(pImage);
+
+    return rc;
+}
+
+/**
+ * Internal. Free all allocated space for representing an image except pImage,
+ * and optionally delete the image from disk.
+ */
+static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (pImage->pStorage)
+        {
+            /* No point updating the file that is deleted anyway. */
+            if (!fDelete)
+                vhdFlushImage(pImage);
+
+            vhdFileClose(pImage);
+            pImage->pStorage = NULL;
+        }
+
+        if (pImage->pszParentFilename)
+        {
+            RTStrFree(pImage->pszParentFilename);
+            pImage->pszParentFilename = NULL;
+        }
+        if (pImage->pBlockAllocationTable)
+        {
+            RTMemFree(pImage->pBlockAllocationTable);
+            pImage->pBlockAllocationTable = NULL;
+        }
+        if (pImage->pu8Bitmap)
+        {
+            RTMemFree(pImage->pu8Bitmap);
+            pImage->pu8Bitmap = NULL;
+        }
+
+        if (fDelete && pImage->pszFilename)
+            rc = vhdFileDelete(pImage, pImage->pszFilename);
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/* 946684800 is the number of seconds between 1/1/1970 and 1/1/2000 */
+#define VHD_TO_UNIX_EPOCH_SECONDS UINT64_C(946684800)
+
+static uint32_t vhdRtTime2VhdTime(PCRTTIMESPEC pRtTimeStamp)
+{
+    uint64_t u64Seconds = RTTimeSpecGetSeconds(pRtTimeStamp);
+    return (uint32_t)(u64Seconds - VHD_TO_UNIX_EPOCH_SECONDS);
+}
+
+static void vhdTime2RtTime(PRTTIMESPEC pRtTimeStamp, uint32_t u32VhdTimeStamp)
+{
+    RTTimeSpecSetSeconds(pRtTimeStamp, VHD_TO_UNIX_EPOCH_SECONDS + u32VhdTimeStamp);
+}
+
+/**
+ * Internal: Allocates the block bitmap rounding up to the next 32bit or 64bit boundary.
+ *           Can be freed with RTMemFree. The memory is zeroed.
+ */
+DECLINLINE(uint8_t *)vhdBlockBitmapAllocate(PVHDIMAGE pImage)
+{
+#ifdef RT_ARCH_AMD64
+    return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 8);
+#else
+    return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 4);
+#endif
+}
+
+/**
+ * Internal: called when the async expansion process completed (failure or success).
+ *           Will do the necessary rollback if an error occurred.
+ */
+static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAGEEXPAND pExpand)
+{
+    int rc = VINF_SUCCESS;
+    uint32_t fFlags = ASMAtomicReadU32(&pExpand->fFlags);
+    bool fIoInProgress = false;
+
+    /* Quick path, check if everything succeeded. */
+    if (fFlags == VHDIMAGEEXPAND_ALL_SUCCESS)
+    {
+        RTMemFree(pExpand);
+    }
+    else
+    {
+        uint32_t uStatus;
+
+        uStatus = VHDIMAGEEXPAND_STATUS_GET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
+        if (   uStatus == VHDIMAGEEXPAND_STEP_FAILED
+            || uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
+        {
+            /* Undo and restore the old value. */
+            pImage->pBlockAllocationTable[pExpand->idxBatAllocated] = ~0U;
+
+            /* Restore the old value on the disk.
+             * No need for a completion callback because we can't
+             * do anything if this fails. */
+            if (uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
+            {
+                rc = vhdFileWriteMetaAsync(pImage,
+                                             pImage->uBlockAllocationTableOffset
+                                           + pExpand->idxBatAllocated * sizeof(uint32_t),
+                                           &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
+                                           sizeof(uint32_t), pIoCtx, NULL, NULL);
+                fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
+            }
+        }
+
+        /* Restore old size (including the footer because another application might
+         * fill up the free space making it impossible to add the footer)
+         * and add the footer at the right place again. */
+        rc = vhdFileSetSize(pImage, pExpand->cbEofOld + sizeof(VHDFooter));
+        AssertRC(rc);
+
+        pImage->uCurrentEndOfFile = pExpand->cbEofOld;
+        rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
+                                   &pImage->vhdFooterCopy, sizeof(VHDFooter),
+                                   pIoCtx, NULL, NULL);
+        fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
+    }
+
+    return fIoInProgress ? VERR_VD_ASYNC_IO_IN_PROGRESS : rc;
+}
+
+static int vhdAsyncExpansionStepCompleted(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq, unsigned iStep)
+{
+   PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+   PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)pvUser;
+
+   LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc iStep=%u\n",
+                pBackendData, pIoCtx, pvUser, rcReq, iStep));
+
+   if (RT_SUCCESS(rcReq))
+       VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_SUCCESS);
+   else
+       VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_FAILED);
+
+   if ((pExpand->fFlags & VHDIMAGEEXPAND_ALL_COMPLETE) == VHDIMAGEEXPAND_ALL_COMPLETE)
+       return vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
+
+   return VERR_VD_ASYNC_IO_IN_PROGRESS;
+}
+
+static int vhdAsyncExpansionDataBlockBitmapComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT);
+}
+
+static int vhdAsyncExpansionDataComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT);
+}
+
+static int vhdAsyncExpansionBatUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
+}
+
+static int vhdAsyncExpansionFooterUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT);
+}
+
+static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffset)
+{
+    VHDDynamicDiskHeader vhdDynamicDiskHeader;
+    int rc = VINF_SUCCESS;
+    uint32_t *pBlockAllocationTable;
+    uint64_t uBlockAllocationTableOffset;
+    unsigned i = 0;
+
+    Log(("Open a dynamic disk.\n"));
+
+    /*
+     * Read the dynamic disk header.
+     */
+    rc = vhdFileReadSync(pImage, uDynamicDiskHeaderOffset,
+                         &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader),
+                         NULL);
+    if (memcmp(vhdDynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE))
+        return VERR_INVALID_PARAMETER;
+
+    pImage->cbDataBlock = RT_BE2H_U32(vhdDynamicDiskHeader.BlockSize);
+    LogFlowFunc(("BlockSize=%u\n", pImage->cbDataBlock));
+    pImage->cBlockAllocationTableEntries = RT_BE2H_U32(vhdDynamicDiskHeader.MaxTableEntries);
+    LogFlowFunc(("MaxTableEntries=%lu\n", pImage->cBlockAllocationTableEntries));
+    AssertMsg(!(pImage->cbDataBlock % VHD_SECTOR_SIZE), ("%s: Data block size is not a multiple of %!\n", __FUNCTION__, VHD_SECTOR_SIZE));
+
+    pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
+    LogFlowFunc(("SectorsPerDataBlock=%u\n", pImage->cSectorsPerDataBlock));
+
+    /*
+     * Every block starts with a bitmap indicating which sectors are valid and which are not.
+     * We store the size of it to be able to calculate the real offset.
+     */
+    pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
+    pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
+    /* Round up to full sector size */
+    if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
+        pImage->cDataBlockBitmapSectors++;
+    LogFlowFunc(("cbDataBlockBitmap=%u\n", pImage->cbDataBlockBitmap));
+    LogFlowFunc(("cDataBlockBitmapSectors=%u\n", pImage->cDataBlockBitmapSectors));
+
+    pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
+    if (!pImage->pu8Bitmap)
+        return VERR_NO_MEMORY;
+
+    pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
+    if (!pBlockAllocationTable)
+        return VERR_NO_MEMORY;
+
+    /*
+     * Read the table.
+     */
+    uBlockAllocationTableOffset = RT_BE2H_U64(vhdDynamicDiskHeader.TableOffset);
+    LogFlowFunc(("uBlockAllocationTableOffset=%llu\n", uBlockAllocationTableOffset));
+    pImage->uBlockAllocationTableOffset = uBlockAllocationTableOffset;
+    rc = vhdFileReadSync(pImage, uBlockAllocationTableOffset,
+                         pBlockAllocationTable,
+                         pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
+                         NULL);
+
+    /*
+     * Because the offset entries inside the allocation table are stored big endian
+     * we need to convert them into host endian.
+     */
+    pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
+    if (!pImage->pBlockAllocationTable)
+    {
+        RTMemFree(pBlockAllocationTable);
+        return VERR_NO_MEMORY;
+    }
+
+    for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
+        pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
+
+    RTMemFree(pBlockAllocationTable);
+
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
+        memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
+
+    return rc;
+}
+
+static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
+{
+    uint64_t FileSize;
+    VHDFooter vhdFooter;
+
+    pImage->uOpenFlags = uOpenFlags;
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the image.
+     */
+    int rc = vhdFileOpen(pImage, pImage->pszFilename,
+                         VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                    false /* fCreate */));
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        return rc;
+    }
+
+    rc = vhdFileGetSize(pImage, &FileSize);
+    pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
+
+    rc = vhdFileReadSync(pImage, pImage->uCurrentEndOfFile,
+                         &vhdFooter, sizeof(VHDFooter), NULL);
+    if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
+        return VERR_VD_VHD_INVALID_HEADER;
+
+    switch (RT_BE2H_U32(vhdFooter.DiskType))
+    {
+        case VHD_FOOTER_DISK_TYPE_FIXED:
+            {
+                pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+            }
+            break;
+        case VHD_FOOTER_DISK_TYPE_DYNAMIC:
+            {
+                pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
+            }
+            break;
+        case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
+            {
+                pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
+                pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
+            }
+            break;
+        default:
+            return VERR_NOT_IMPLEMENTED;
+    }
+
+    pImage->cbSize       = RT_BE2H_U64(vhdFooter.CurSize);
+    pImage->LCHSGeometry.cCylinders   = 0;
+    pImage->LCHSGeometry.cHeads       = 0;
+    pImage->LCHSGeometry.cSectors     = 0;
+    pImage->PCHSGeometry.cCylinders   = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
+    pImage->PCHSGeometry.cHeads       = vhdFooter.DiskGeometryHeads;
+    pImage->PCHSGeometry.cSectors     = vhdFooter.DiskGeometrySectors;
+
+    /*
+     * Copy of the disk footer.
+     * If we allocate new blocks in differencing disks on write access
+     * the footer is overwritten. We need to write it at the end of the file.
+     */
+    memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
+
+    /*
+     * Is there a better way?
+     */
+    memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
+
+    pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
+    LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
+
+    if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
+
+    if (RT_FAILURE(rc))
+        vhdFreeImage(pImage, false);
+    return rc;
+}
+
+/**
+ * Internal: Checks if a sector in the block bitmap is set
+ */
+DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
+{
+    uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
+
+    /*
+     * The index of the bit in the byte of the data block bitmap.
+     * The most significant bit stands for a lower sector number.
+     */
+    uint8_t  iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
+    uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
+
+    AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
+                ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
+
+    return ASMBitTest(puBitmap, iBitInByte);
+}
+
+/**
+ * Internal: Sets the given sector in the sector bitmap.
+ */
+DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
+{
+    uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
+
+    /*
+     * The index of the bit in the byte of the data block bitmap.
+     * The most significant bit stands for a lower sector number.
+     */
+    uint8_t  iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
+    uint8_t  *puBitmap  = pu8Bitmap + iBitmap;
+
+    AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
+                ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
+
+    return !ASMBitTestAndSet(puBitmap, iBitInByte);
+}
+
+/**
+ * Internal: Derive drive geometry from its size.
+ */
+static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
+{
+    uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
+    uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
+
+    if (u64TotalSectors > 65535 * 16 * 255)
+    {
+        /* ATA disks limited to 127 GB. */
+        u64TotalSectors = 65535 * 16 * 255;
+    }
+
+    if (u64TotalSectors >= 65535 * 16 * 63)
+    {
+        u32SectorsPerTrack    = 255;
+        u32Heads              = 16;
+        u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
+    }
+    else
+    {
+        u32SectorsPerTrack    = 17;
+        u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
+
+        u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
+
+        if (u32Heads < 4)
+        {
+            u32Heads = 4;
+        }
+        if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
+        {
+            u32SectorsPerTrack    = 31;
+            u32Heads              = 16;
+            u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
+        }
+        if (u32CylinderTimesHeads >= (u32Heads * 1024))
+        {
+            u32SectorsPerTrack    = 63;
+            u32Heads              = 16;
+            u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
+        }
+    }
+    pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
+    pImage->PCHSGeometry.cHeads     = u32Heads;
+    pImage->PCHSGeometry.cSectors   = u32SectorsPerTrack;
+    pImage->LCHSGeometry.cCylinders = 0;
+    pImage->LCHSGeometry.cHeads     = 0;
+    pImage->LCHSGeometry.cSectors   = 0;
+}
+
+
+static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
+{
+    PVHDPLE pLocator = pDDH->ParentLocatorEntry;
+    /* Relative Windows path. */
+    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
+    pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
+    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
+    u64Offset += VHD_RELATIVE_MAX_PATH;
+    pLocator++;
+    /* Absolute Windows path. */
+    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
+    pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
+    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
+    u64Offset += VHD_ABSOLUTE_MAX_PATH;
+    pLocator++;
+    /* Unicode relative Windows path. */
+    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
+    pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
+    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
+    u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
+    pLocator++;
+    /* Unicode absolute Windows path. */
+    pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
+    pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
+    pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
+    return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
+}
+
+/**
+ * Internal: Additional code for dynamic VHD image creation.
+ */
+static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
+{
+    int rc;
+    VHDDynamicDiskHeader DynamicDiskHeader;
+    uint32_t u32BlockAllocationTableSectors;
+    void    *pvTmp = NULL;
+
+    memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
+
+    pImage->u64DataOffset           = sizeof(VHDFooter);
+    pImage->cbDataBlock             = VHD_BLOCK_SIZE; /* 2 MB */
+    pImage->cSectorsPerDataBlock    = pImage->cbDataBlock / VHD_SECTOR_SIZE;
+    pImage->cbDataBlockBitmap       = pImage->cSectorsPerDataBlock / 8;
+    pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
+    /* Align to sector boundary */
+    if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
+        pImage->cDataBlockBitmapSectors++;
+    pImage->pu8Bitmap               = vhdBlockBitmapAllocate(pImage);
+    if (!pImage->pu8Bitmap)
+        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
+
+    /* Initialize BAT. */
+    pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
+    pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
+    u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
+    pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
+    if (!pImage->pBlockAllocationTable)
+        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
+
+    for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
+    {
+        pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
+    }
+
+    /* Round up to the sector size. */
+    if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
+        pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
+                                                              pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
+    else
+        pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
+
+    /* Set dynamic image size. */
+    pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
+    if (!pvTmp)
+        return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
+
+    rc = vhdFileWriteSync(pImage, 0, pvTmp,
+                          pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
+    if (RT_FAILURE(rc))
+    {
+        RTMemTmpFree(pvTmp);
+        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
+    }
+
+    RTMemTmpFree(pvTmp);
+
+    /* Initialize and write the dynamic disk header. */
+    memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
+    DynamicDiskHeader.DataOffset      = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
+    DynamicDiskHeader.TableOffset     = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
+    DynamicDiskHeader.HeaderVersion   = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
+    DynamicDiskHeader.BlockSize       = RT_H2BE_U32(pImage->cbDataBlock);
+    DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
+    /* Compute and update checksum. */
+    DynamicDiskHeader.Checksum = 0;
+    DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
+
+    rc = vhdFileWriteSync(pImage, sizeof(VHDFooter), &DynamicDiskHeader,
+                          sizeof(DynamicDiskHeader), NULL);
+    if (RT_FAILURE(rc))
+        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
+
+    /* Write BAT. */
+    rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
+                          pImage->pBlockAllocationTable,
+                          pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
+                          NULL);
+    if (RT_FAILURE(rc))
+        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
+
+    return rc;
+}
+
+/**
+ * Internal: The actual code for VHD image creation, both fixed and dynamic.
+ */
+static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
+                          unsigned uImageFlags, const char *pszComment,
+                          PCVDGEOMETRY pPCHSGeometry,
+                          PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
+                          unsigned uOpenFlags,
+                          PFNVDPROGRESS pfnProgress, void *pvUser,
+                          unsigned uPercentStart, unsigned uPercentSpan)
+{
+    int rc;
+    VHDFooter Footer;
+    RTTIMESPEC now;
+
+    pImage->uOpenFlags = uOpenFlags;
+    pImage->uImageFlags = uImageFlags;
+
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    rc = vhdFileOpen(pImage, pImage->pszFilename,
+                     VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                                true /* fCreate */));
+    if (RT_FAILURE(rc))
+        return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
+
+
+    pImage->cbSize = cbSize;
+    pImage->ImageUuid = *pUuid;
+    RTUuidClear(&pImage->ParentUuid);
+    vhdSetDiskGeometry(pImage, cbSize);
+
+    /* Initialize the footer. */
+    memset(&Footer, 0, sizeof(Footer));
+    memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
+    Footer.Features = RT_H2BE_U32(0x2);
+    Footer.Version  = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
+    Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
+    memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
+    Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
+#ifdef RT_OS_DARWIN
+    Footer.CreatorOS  = RT_H2BE_U32(0x4D616320); /* "Mac " */
+#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
+    Footer.CreatorOS  = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
+#endif
+    Footer.OrigSize   = RT_H2BE_U64(cbSize);
+    Footer.CurSize    = Footer.OrigSize;
+    Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
+    Footer.DiskGeometryHeads    = pImage->PCHSGeometry.cHeads;
+    Footer.DiskGeometrySectors  = pImage->PCHSGeometry.cSectors;
+    memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
+    Footer.SavedState = 0;
+
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        Footer.DiskType   = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
+        /*
+         * Initialize fixed image.
+         * "The size of the entire file is the size of the hard disk in
+         * the guest operating system plus the size of the footer."
+         */
+        pImage->u64DataOffset     = VHD_FOOTER_DATA_OFFSET_FIXED;
+        pImage->uCurrentEndOfFile = cbSize;
+        /** @todo r=klaus replace this with actual data writes, see the experience
+         * with VDI files on Windows, can cause long freezes when writing. */
+        rc = vhdFileSetSize(pImage, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
+        if (RT_FAILURE(rc))
+        {
+            vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
+            goto out;
+        }
+    }
+    else
+    {
+        /*
+         * Initialize dynamic image.
+         *
+         * The overall structure of dynamic disk is:
+         *
+         * [Copy of hard disk footer (512 bytes)]
+         * [Dynamic disk header (1024 bytes)]
+         * [BAT (Block Allocation Table)]
+         * [Parent Locators]
+         * [Data block 1]
+         * [Data block 2]
+         * ...
+         * [Data block N]
+         * [Hard disk footer (512 bytes)]
+         */
+        Footer.DiskType   = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
+                              ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
+                              : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
+        /* We are half way thorough with creation of image, let the caller know. */
+        if (pfnProgress)
+            pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
+
+        rc = vhdCreateDynamicImage(pImage, cbSize);
+        if (RT_FAILURE(rc))
+            goto out;
+    }
+
+    Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
+
+    /* Compute and update the footer checksum. */
+    Footer.Checksum = 0;
+    Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
+
+    pImage->vhdFooterCopy = Footer;
+
+    /* Store the footer */
+    rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, &Footer,
+                          sizeof(Footer), NULL);
+    if (RT_FAILURE(rc))
+    {
+        vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Dynamic images contain a copy of the footer at the very beginning of the file. */
+    if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+    {
+        /* Write the copy of the footer. */
+        rc = vhdFileWriteSync(pImage, 0, &Footer, sizeof(Footer), NULL);
+        if (RT_FAILURE(rc))
+        {
+            vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
+            goto out;
+        }
+    }
+
+out:
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan);
+
+    if (RT_FAILURE(rc))
+        vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
+    return rc;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                           PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
+    int rc;
+    PVDIOSTORAGE pStorage;
+    uint64_t cbFile;
+    VHDFooter vhdFooter;
+
+    /* Get I/O interface. */
+    PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
+    PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
+    AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
+                                        VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
+                                                                   false /* fCreate */),
+                                        &pStorage);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
+                                           &cbFile);
+    if (RT_FAILURE(rc))
+    {
+        pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+        rc = VERR_VD_VHD_INVALID_HEADER;
+        goto out;
+    }
+
+    rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
+                                            cbFile - sizeof(VHDFooter),
+                                            &vhdFooter, sizeof(VHDFooter), NULL);
+    if (RT_FAILURE(rc) || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
+        rc = VERR_VD_VHD_INVALID_HEADER;
+    else
+    {
+        *penmType = VDTYPE_HDD;
+        rc = VINF_SUCCESS;
+    }
+
+    pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int vhdOpen(const char *pszFilename, unsigned uOpenFlags,
+                   PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                   VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = vhdOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pImage;
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int vhdCreate(const char *pszFilename, uint64_t cbSize,
+                     unsigned uImageFlags, const char *pszComment,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     PCRTUUID pUuid, unsigned uOpenFlags,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        return rc;
+    }
+
+    /* @todo Check the values of other params */
+
+    pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        return rc;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pStorage = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIO)))
+    {
+        RTMemFree(pImage);
+        return VERR_INVALID_PARAMETER;
+    }
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIOCallbacks)))
+    {
+        RTMemFree(pImage);
+        return VERR_INVALID_PARAMETER;
+    }
+
+    rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
+                        pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
+                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
+
+    if (RT_SUCCESS(rc))
+    {
+        /* So far the image is opened in read/write mode. Make sure the
+         * image is opened in read-only mode if the caller requested that. */
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            vhdFreeImage(pImage, false);
+            rc = vhdOpenImage(pImage, uOpenFlags);
+            if (RT_FAILURE(rc))
+            {
+                RTMemFree(pImage);
+                goto out;
+            }
+        }
+        *ppBackendData = pImage;
+    }
+    else
+        RTMemFree(pImage);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int vhdRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    /* Check arguments. */
+    if (   !pImage
+        || !pszFilename
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Close the image. */
+    rc = vhdFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Rename the file. */
+    rc = vhdFileMove(pImage, pImage->pszFilename, pszFilename, 0);
+    if (RT_FAILURE(rc))
+    {
+        /* The move failed, try to reopen the original image. */
+        int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
+        if (RT_FAILURE(rc2))
+            rc = rc2;
+
+        goto out;
+    }
+
+    /* Update pImage with the new information. */
+    pImage->pszFilename = pszFilename;
+
+    /* Open the old file with new name. */
+    rc = vhdOpenImage(pImage, pImage->uOpenFlags);
+    if (RT_FAILURE(rc))
+        goto out;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int vhdClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    rc = vhdFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                   size_t cbBuf, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%p uOffset=%#llx pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    if (uOffset + cbBuf > pImage->cbSize)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /*
+     * If we have a dynamic disk image, we need to find the data block and sector to read.
+     */
+    if (pImage->pBlockAllocationTable)
+    {
+        /*
+         * Get the data block first.
+         */
+        uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
+        uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
+        uint64_t uVhdOffset;
+
+        LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
+        LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
+
+        /*
+         * If the block is not allocated the content of the entry is ~0
+         */
+        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
+        {
+            /* Return block size as read. */
+            *pcbActuallyRead = RT_MIN(cbBuf, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
+            rc = VERR_VD_BLOCK_FREE;
+            goto out;
+        }
+
+        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
+        LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
+
+        /*
+         * Clip read range to remain in this data block.
+         */
+        cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+
+        /* Read in the block's bitmap. */
+        rc = vhdFileReadSync(pImage,
+                             ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                             pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
+                             NULL);
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t cSectors = 0;
+
+            if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+            {
+                cBATEntryIndex++;
+                cSectors = 1;
+
+                /*
+                 * The first sector being read is marked dirty, read as much as we
+                 * can from child. Note that only sectors that are marked dirty
+                 * must be read from child.
+                 */
+                while (   (cSectors < (cbBuf / VHD_SECTOR_SIZE))
+                       && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+                {
+                    cBATEntryIndex++;
+                    cSectors++;
+                }
+
+                cbBuf = cSectors * VHD_SECTOR_SIZE;
+
+                LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
+                rc = vhdFileReadSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
+            }
+            else
+            {
+                /*
+                 * The first sector being read is marked clean, so we should read from
+                 * our parent instead, but only as much as there are the following
+                 * clean sectors, because the block may still contain dirty sectors
+                 * further on. We just need to compute the number of clean sectors
+                 * and pass it to our caller along with the notification that they
+                 * should be read from the parent.
+                 */
+                cBATEntryIndex++;
+                cSectors = 1;
+
+                while (   (cSectors < (cbBuf / VHD_SECTOR_SIZE))
+                       && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+                {
+                    cBATEntryIndex++;
+                    cSectors++;
+                }
+
+                cbBuf = cSectors * VHD_SECTOR_SIZE;
+                LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
+                rc = VERR_VD_BLOCK_FREE;
+            }
+        }
+        else
+            AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
+    }
+    else
+    {
+        rc = vhdFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        if (pcbActuallyRead)
+            *pcbActuallyRead = cbBuf;
+
+        Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n"
+                "%.*Rhxd\n",
+                uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
+    }
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                    size_t cbBuf, size_t *pcbWriteProcess,
+                    size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
+             pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
+
+    AssertPtr(pImage);
+    Assert(uOffset % VHD_SECTOR_SIZE == 0);
+    Assert(cbBuf % VHD_SECTOR_SIZE == 0);
+
+    if (pImage->pBlockAllocationTable)
+    {
+        /*
+         * Get the data block first.
+         */
+        uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
+        uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
+        uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
+        uint64_t uVhdOffset;
+
+        /*
+         * Clip write range.
+         */
+        cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+
+        /*
+         * If the block is not allocated the content of the entry is ~0
+         * and we need to allocate a new block. Note that while blocks are
+         * allocated with a relatively big granularity, each sector has its
+         * own bitmap entry, indicating whether it has been written or not.
+         * So that means for the purposes of the higher level that the
+         * granularity is invisible. This means there's no need to return
+         * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
+         */
+        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
+        {
+            /* Check if the block allocation should be suppressed. */
+            if (fWrite & VD_WRITE_NO_ALLOC)
+            {
+                *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
+                *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
+
+                if (pcbWriteProcess)
+                    *pcbWriteProcess = cbBuf;
+                rc = VERR_VD_BLOCK_FREE;
+                goto out;
+            }
+
+            size_t  cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
+            uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
+
+            if (!pNewBlock)
+            {
+                rc = VERR_NO_MEMORY;
+                goto out;
+            }
+
+            /*
+             * Write the new block at the current end of the file.
+             */
+            rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
+                                  pNewBlock, cbNewBlock, NULL);
+            AssertRC(rc);
+
+            /*
+             * Set the new end of the file and link the new block into the BAT.
+             */
+            pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
+            pImage->uCurrentEndOfFile += cbNewBlock;
+            RTMemFree(pNewBlock);
+
+            /* Write the updated BAT and the footer to remain in a consistent state. */
+            rc = vhdFlushImage(pImage);
+            AssertRC(rc);
+        }
+
+        /*
+         * Calculate the real offset in the file.
+         */
+        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
+
+        /* Write data. */
+        vhdFileWriteSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
+
+        /* Read in the block's bitmap. */
+        rc = vhdFileReadSync(pImage,
+                             ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                             pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
+                             NULL);
+        if (RT_SUCCESS(rc))
+        {
+            bool fChanged = false;
+
+            /* Set the bits for all sectors having been written. */
+            for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
+            {
+                fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
+                cBATEntryIndex++;
+            }
+
+            if (fChanged)
+            {
+                /* Write the bitmap back. */
+                rc = vhdFileWriteSync(pImage,
+                                      ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                                      pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
+                                      NULL);
+            }
+        }
+    }
+    else
+    {
+        rc = vhdFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbBuf;
+
+    /* Stay on the safe side. Do not run the risk of confusing the higher
+     * level, as that can be pretty lethal to image consistency. */
+    *pcbPreRead = 0;
+    *pcbPostRead = 0;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int vhdFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    rc = vhdFlushImage(pImage);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned vhdGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    unsigned ver = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        ver = 1; /**< @todo use correct version */
+
+    LogFlowFunc(("returns %u\n", ver));
+    return ver;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t vhdGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+        cb = pImage->cbSize;
+
+    LogFlowFunc(("returns %llu\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t vhdGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+        cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pImage->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->LCHSGeometry.cCylinders)
+        {
+            *pLCHSGeometry = pImage->LCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+
+        pImage->LCHSGeometry = *pLCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned vhdGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uImageFlags = pImage->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned vhdGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    rc = vhdFreeImage(pImage, false);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vhdOpenImage(pImage, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int vhdGetComment(void *pBackendData, char *pszComment,
+                         size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int vhdSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+            rc = VERR_NOT_SUPPORTED;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ImageUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            pImage->ImageUuid = *pUuid;
+            /* Update the footer copy. It will get written to disk when the image is closed. */
+            memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
+            /* Update checksum. */
+            pImage->vhdFooterCopy.Checksum = 0;
+            pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
+
+            /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
+            if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
+                pImage->fDynHdrNeedsUpdate = true;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ParentUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (pImage && pImage->pStorage)
+    {
+        if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        {
+            pImage->ParentUuid = *pUuid;
+            pImage->fDynHdrNeedsUpdate = true;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = VERR_NOT_SUPPORTED;
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+            rc = VERR_NOT_SUPPORTED;
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void vhdDump(void *pBackendData)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        vhdMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
+                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
+                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
+                    VHD_SECTOR_SIZE);
+        vhdMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
+        vhdMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
+    }
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetTimestamp */
+static int vhdGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
+{
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        rc = vhdFileGetModificationTime(pImage, pImage->pszFilename, pTimeStamp);
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
+static int vhdGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
+{
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
+static int vhdSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
+{
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+        {
+            pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
+            pImage->fDynHdrNeedsUpdate = true;
+        }
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
+static int vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
+{
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+        *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
+static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
+{
+    int rc = VINF_SUCCESS;
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            rc = VERR_VD_IMAGE_READ_ONLY;
+        else
+        {
+            if (pImage->pszParentFilename)
+                RTStrFree(pImage->pszParentFilename);
+            pImage->pszParentFilename = RTStrDup(pszParentFilename);
+            if (!pImage->pszParentFilename)
+                rc = VERR_NO_MEMORY;
+            else
+                pImage->fDynHdrNeedsUpdate = true;
+        }
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
+static bool vhdIsAsyncIOSupported(void *pBackendData)
+{
+    return true;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
+static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+                        PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
+
+    if (uOffset + cbRead > pImage->cbSize)
+        return VERR_INVALID_PARAMETER;
+
+    /*
+     * If we have a dynamic disk image, we need to find the data block and sector to read.
+     */
+    if (pImage->pBlockAllocationTable)
+    {
+        /*
+         * Get the data block first.
+         */
+        uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
+        uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
+        uint64_t uVhdOffset;
+
+        LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
+        LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
+
+        /*
+         * If the block is not allocated the content of the entry is ~0
+         */
+        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
+        {
+            /* Return block size as read. */
+            *pcbActuallyRead = RT_MIN(cbRead, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
+            return VERR_VD_BLOCK_FREE;
+        }
+
+        uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
+        LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
+
+        /*
+         * Clip read range to remain in this data block.
+         */
+        cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+
+        /* Read in the block's bitmap. */
+        PVDMETAXFER pMetaXfer;
+        rc = vhdFileReadMetaAsync(pImage,
+                                  ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                                  pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
+                                  pIoCtx, &pMetaXfer, NULL, NULL);
+
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t cSectors = 0;
+
+            vhdFileMetaXferRelease(pImage, pMetaXfer);
+            if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+            {
+                cBATEntryIndex++;
+                cSectors = 1;
+
+                /*
+                 * The first sector being read is marked dirty, read as much as we
+                 * can from child. Note that only sectors that are marked dirty
+                 * must be read from child.
+                 */
+                while (   (cSectors < (cbRead / VHD_SECTOR_SIZE))
+                       && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+                {
+                    cBATEntryIndex++;
+                    cSectors++;
+                }
+
+                cbRead = cSectors * VHD_SECTOR_SIZE;
+
+                LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
+                rc = vhdFileReadUserAsync(pImage, uVhdOffset, pIoCtx, cbRead);
+            }
+            else
+            {
+                /*
+                 * The first sector being read is marked clean, so we should read from
+                 * our parent instead, but only as much as there are the following
+                 * clean sectors, because the block may still contain dirty sectors
+                 * further on. We just need to compute the number of clean sectors
+                 * and pass it to our caller along with the notification that they
+                 * should be read from the parent.
+                 */
+                cBATEntryIndex++;
+                cSectors = 1;
+
+                while (   (cSectors < (cbRead / VHD_SECTOR_SIZE))
+                       && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
+                {
+                    cBATEntryIndex++;
+                    cSectors++;
+                }
+
+                cbRead = cSectors * VHD_SECTOR_SIZE;
+                LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
+                rc = VERR_VD_BLOCK_FREE;
+            }
+        }
+        else
+            AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
+    }
+    else
+    {
+        rc = vhdFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
+    }
+
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbRead;
+
+    LogFlowFunc(("returns rc=%Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
+static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                         PVDIOCTX pIoCtx,
+                         size_t *pcbWriteProcess, size_t *pcbPreRead,
+                         size_t *pcbPostRead, unsigned fWrite)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
+             pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
+
+    AssertPtr(pImage);
+    Assert(uOffset % VHD_SECTOR_SIZE == 0);
+    Assert(cbWrite % VHD_SECTOR_SIZE == 0);
+
+    if (pImage->pBlockAllocationTable)
+    {
+        /*
+         * Get the data block first.
+         */
+        uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
+        uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
+        uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
+        uint64_t uVhdOffset;
+
+        /*
+         * Clip write range.
+         */
+        cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
+
+        /*
+         * If the block is not allocated the content of the entry is ~0
+         * and we need to allocate a new block. Note that while blocks are
+         * allocated with a relatively big granularity, each sector has its
+         * own bitmap entry, indicating whether it has been written or not.
+         * So that means for the purposes of the higher level that the
+         * granularity is invisible. This means there's no need to return
+         * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
+         */
+        if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
+        {
+            /* Check if the block allocation should be suppressed. */
+            if (fWrite & VD_WRITE_NO_ALLOC)
+            {
+                *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
+                *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
+
+                if (pcbWriteProcess)
+                    *pcbWriteProcess = cbWrite;
+                return VERR_VD_BLOCK_FREE;
+            }
+
+            PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
+            bool fIoInProgress = false;
+
+            if (!pExpand)
+                return VERR_NO_MEMORY;
+
+            pExpand->cbEofOld = pImage->uCurrentEndOfFile;
+            pExpand->idxBatAllocated = cBlockAllocationTableEntry;
+            pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
+
+            /* Set the bits for all sectors having been written. */
+            for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
+            {
+                /* No need to check for a changed value because this is an initial write. */
+                vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
+                cBATEntryIndex++;
+            }
+
+            do
+            {
+                /*
+                 * Start with the sector bitmap.
+                 */
+                rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
+                                           pExpand->au8Bitmap,
+                                           pImage->cbDataBlockBitmap, pIoCtx,
+                                           vhdAsyncExpansionDataBlockBitmapComplete,
+                                           pExpand);
+                if (RT_SUCCESS(rc))
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    fIoInProgress = true;
+                else
+                {
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    break;
+                }
+
+
+                /*
+                 * Write the new block at the current end of the file.
+                 */
+                rc = vhdFileWriteUserAsync(pImage,
+                                           pImage->uCurrentEndOfFile + pImage->cbDataBlockBitmap,
+                                           pIoCtx, cbWrite,
+                                           vhdAsyncExpansionDataComplete,
+                                           pExpand);
+                if (RT_SUCCESS(rc))
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    fIoInProgress = true;
+                else
+                {
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    break;
+                }
+
+                /*
+                 * Write entry in the BAT.
+                 */
+                rc = vhdFileWriteMetaAsync(pImage,
+                                           pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
+                                           &pExpand->idxBlockBe,
+                                           sizeof(uint32_t), pIoCtx,
+                                           vhdAsyncExpansionBatUpdateComplete,
+                                           pExpand);
+                if (RT_SUCCESS(rc))
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    fIoInProgress = true;
+                else
+                {
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    break;
+                }
+
+                /*
+                 * Set the new end of the file and link the new block into the BAT.
+                 */
+                pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
+                pImage->uCurrentEndOfFile += pImage->cbDataBlockBitmap + pImage->cbDataBlock;
+
+                /* Update the footer. */
+                rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
+                                           &pImage->vhdFooterCopy,
+                                           sizeof(VHDFooter), pIoCtx,
+                                           vhdAsyncExpansionFooterUpdateComplete,
+                                           pExpand);
+                if (RT_SUCCESS(rc))
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
+                else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    fIoInProgress = true;
+                else
+                {
+                    VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
+                    break;
+                }
+
+            } while (0);
+
+            if (!fIoInProgress)
+                vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
+            else
+                rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
+        }
+        else
+        {
+            /*
+             * Calculate the real offset in the file.
+             */
+            uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
+
+            /* Read in the block's bitmap. */
+            PVDMETAXFER pMetaXfer;
+            rc = vhdFileReadMetaAsync(pImage,
+                                      ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                                      pImage->pu8Bitmap,
+                                      pImage->cbDataBlockBitmap, pIoCtx,
+                                      &pMetaXfer, NULL, NULL);
+            if (RT_SUCCESS(rc))
+            {
+                vhdFileMetaXferRelease(pImage, pMetaXfer);
+
+                /* Write data. */
+                rc = vhdFileWriteUserAsync(pImage, uVhdOffset, pIoCtx, cbWrite,
+                                           NULL, NULL);
+                if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                {
+                    bool fChanged = false;
+
+                    /* Set the bits for all sectors having been written. */
+                    for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
+                    {
+                        fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
+                        cBATEntryIndex++;
+                    }
+
+                    /* Only write the bitmap if it was changed. */
+                    if (fChanged)
+                    {
+                        /*
+                         * Write the bitmap back.
+                         *
+                         * @note We don't have a completion callback here because we
+                         * can't do anything if the write fails for some reason.
+                         * The error will propagated to the device/guest
+                         * by the generic VD layer already and we don't need
+                         * to rollback anything here.
+                         */
+                        rc = vhdFileWriteMetaAsync(pImage,
+                                                   ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
+                                                   pImage->pu8Bitmap,
+                                                   pImage->cbDataBlockBitmap,
+                                                   pIoCtx, NULL, NULL);
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        rc = vhdFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbWrite;
+
+    /* Stay on the safe side. Do not run the risk of confusing the higher
+     * level, as that can be pretty lethal to image consistency. */
+    *pcbPreRead = 0;
+    *pcbPostRead = 0;
+
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
+static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+
+    /* No need to write anything here. Data is always updated on a write. */
+    return vhdFileFlushAsync(pImage, pIoCtx, NULL, NULL);
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnResize */
+static int vhdResize(void *pBackendData, uint64_t cbSize,
+                     PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                     unsigned uPercentStart, unsigned uPercentSpan,
+                     PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                     PVDINTERFACE pVDIfsOperation)
+{
+    PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        if (pCbProgress)
+            pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Making the image smaller is not supported at the moment. */
+    if (   cbSize < pImage->cbSize
+        || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        rc = VERR_NOT_SUPPORTED;
+    else if (cbSize > pImage->cbSize)
+    {
+        unsigned cBlocksAllocated = 0;
+        size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap;     /** < Size of a block including the sector bitmap. */
+        uint32_t cBlocksNew = cbSize / pImage->cbDataBlock;                   /** < New number of blocks in the image after the resize */
+        if (cbSize % pImage->cbDataBlock)
+            cBlocksNew++;
+
+        uint32_t cBlocksOld      = pImage->cBlockAllocationTableEntries;      /** < Number of blocks before the resize. */
+        uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE);                         /** < Required space for the block array after the resize. */
+        uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
+        uint64_t offStartDataOld = ~0ULL;
+
+        /* Go through the BAT and find the data start offset. */
+        for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
+        {
+            if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
+            {
+                uint64_t offStartBlock = pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
+                if (offStartBlock < offStartDataOld)
+                    offStartDataOld = offStartBlock;
+                cBlocksAllocated++;
+            }
+        }
+
+        if (   offStartDataOld != offStartDataNew
+            && cBlocksAllocated > 0)
+        {
+            /* Calculate how many sectors nee to be relocated. */
+            uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
+            unsigned cBlocksReloc = cbOverlapping / cbBlock;
+            if (cbOverlapping % cbBlock)
+                cBlocksReloc++;
+
+            cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
+            offStartDataNew = offStartDataOld;
+
+            /* Do the relocation. */
+            LogFlow(("Relocating %u blocks\n", cBlocksReloc));
+
+            /*
+             * Get the blocks we need to relocate first, they are appended to the end
+             * of the image.
+             */
+            void *pvBuf = NULL, *pvZero = NULL;
+            do
+            {
+                /* Allocate data buffer. */
+                pvBuf = RTMemAllocZ(cbBlock);
+                if (!pvBuf)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                /* Allocate buffer for overwriting with zeroes. */
+                pvZero = RTMemAllocZ(cbBlock);
+                if (!pvZero)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
+                for (unsigned i = 0; i < cBlocksReloc; i++)
+                {
+                    uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
+
+                    /* Search the index in the block table. */
+                    for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
+                    {
+                        if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
+                        {
+                            /* Read data and append to the end of the image. */
+                            rc = vhdFileReadSync(pImage, offStartDataNew, pvBuf, cbBlock, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            /* Zero out the old block area. */
+                            rc = vhdFileWriteSync(pImage, offStartDataNew, pvZero, cbBlock, NULL);
+                            if (RT_FAILURE(rc))
+                                break;
+
+                            /* Update block counter. */
+                            pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
+
+                            pImage->uCurrentEndOfFile += cbBlock;
+
+                            /* Continue with the next block. */
+                            break;
+                        }
+                    }
+
+                    if (RT_FAILURE(rc))
+                        break;
+
+                    offStartDataNew += cbBlock;
+                }
+            } while (0);
+
+            if (pvBuf)
+                RTMemFree(pvBuf);
+            if (pvZero)
+                RTMemFree(pvZero);
+        }
+
+        /*
+         * Relocation done, expand the block array and update the header with
+         * the new data.
+         */
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
+            if (paBlocksNew)
+            {
+                pImage->pBlockAllocationTable = paBlocksNew;
+
+                /* Mark the new blocks as unallocated. */
+                for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
+                    pImage->pBlockAllocationTable[idxBlock] = ~0U;
+            }
+            else
+                rc = VERR_NO_MEMORY;
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Write the block array before updating the rest. */
+                rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset, pImage->pBlockAllocationTable,
+                                      cBlocksNew * sizeof(uint32_t), NULL);
+            }
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Update size and new block count. */
+                pImage->cBlockAllocationTableEntries = cBlocksNew;
+                pImage->cbSize = cbSize;
+
+                /* Update geometry. */
+                pImage->PCHSGeometry = *pPCHSGeometry;
+                pImage->LCHSGeometry = *pLCHSGeometry;
+            }
+        }
+
+        /* Update header information in base image file. */
+        pImage->fDynHdrNeedsUpdate = true;
+        vhdFlush(pImage);
+    }
+    /* Same size doesn't change the image at all. */
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_VhdBackend =
+{
+    /* pszBackendName */
+    "VHD",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+    VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
+    VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
+    VD_CAP_ASYNC | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aVhdFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    vhdCheckIfValid,
+    /* pfnOpen */
+    vhdOpen,
+    /* pfnCreate */
+    vhdCreate,
+    /* pfnRename */
+    vhdRename,
+    /* pfnClose */
+    vhdClose,
+    /* pfnRead */
+    vhdRead,
+    /* pfnWrite */
+    vhdWrite,
+    /* pfnFlush */
+    vhdFlush,
+    /* pfnGetVersion */
+    vhdGetVersion,
+    /* pfnGetSize */
+    vhdGetSize,
+    /* pfnGetFileSize */
+    vhdGetFileSize,
+    /* pfnGetPCHSGeometry */
+    vhdGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    vhdSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    vhdGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    vhdSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    vhdGetImageFlags,
+    /* pfnGetOpenFlags */
+    vhdGetOpenFlags,
+    /* pfnSetOpenFlags */
+    vhdSetOpenFlags,
+    /* pfnGetComment */
+    vhdGetComment,
+    /* pfnSetComment */
+    vhdSetComment,
+    /* pfnGetUuid */
+    vhdGetUuid,
+    /* pfnSetUuid */
+    vhdSetUuid,
+    /* pfnGetModificationUuid */
+    vhdGetModificationUuid,
+    /* pfnSetModificationUuid */
+    vhdSetModificationUuid,
+    /* pfnGetParentUuid */
+    vhdGetParentUuid,
+    /* pfnSetParentUuid */
+    vhdSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    vhdGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    vhdSetParentModificationUuid,
+    /* pfnDump */
+    vhdDump,
+    /* pfnGetTimeStamp */
+    vhdGetTimeStamp,
+    /* pfnGetParentTimeStamp */
+    vhdGetParentTimeStamp,
+    /* pfnSetParentTimeStamp */
+    vhdSetParentTimeStamp,
+    /* pfnGetParentFilename */
+    vhdGetParentFilename,
+    /* pfnSetParentFilename */
+    vhdSetParentFilename,
+    /* pfnIsAsyncIOSupported */
+    vhdIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    vhdAsyncRead,
+    /* pfnAsyncWrite */
+    vhdAsyncWrite,
+    /* pfnAsyncFlush */
+    vhdAsyncFlush,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    vhdResize
+};
Index: /trunk/src/VBox/Storage/VMDK.cpp
===================================================================
--- /trunk/src/VBox/Storage/VMDK.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/VMDK.cpp	(revision 33567)
@@ -0,0 +1,7418 @@
+/* $Id$ */
+/** @file
+ * VMDK disk image, core code.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD_VMDK
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/rand.h>
+#include <iprt/zip.h>
+#include <iprt/asm.h>
+
+
+/*******************************************************************************
+*   Constants And Macros, Structures and Typedefs                              *
+*******************************************************************************/
+
+/** Maximum encoded string size (including NUL) we allow for VMDK images.
+ * Deliberately not set high to avoid running out of descriptor space. */
+#define VMDK_ENCODED_COMMENT_MAX 1024
+
+/** VMDK descriptor DDB entry for PCHS cylinders. */
+#define VMDK_DDB_GEO_PCHS_CYLINDERS "ddb.geometry.cylinders"
+
+/** VMDK descriptor DDB entry for PCHS heads. */
+#define VMDK_DDB_GEO_PCHS_HEADS "ddb.geometry.heads"
+
+/** VMDK descriptor DDB entry for PCHS sectors. */
+#define VMDK_DDB_GEO_PCHS_SECTORS "ddb.geometry.sectors"
+
+/** VMDK descriptor DDB entry for LCHS cylinders. */
+#define VMDK_DDB_GEO_LCHS_CYLINDERS "ddb.geometry.biosCylinders"
+
+/** VMDK descriptor DDB entry for LCHS heads. */
+#define VMDK_DDB_GEO_LCHS_HEADS "ddb.geometry.biosHeads"
+
+/** VMDK descriptor DDB entry for LCHS sectors. */
+#define VMDK_DDB_GEO_LCHS_SECTORS "ddb.geometry.biosSectors"
+
+/** VMDK descriptor DDB entry for image UUID. */
+#define VMDK_DDB_IMAGE_UUID "ddb.uuid.image"
+
+/** VMDK descriptor DDB entry for image modification UUID. */
+#define VMDK_DDB_MODIFICATION_UUID "ddb.uuid.modification"
+
+/** VMDK descriptor DDB entry for parent image UUID. */
+#define VMDK_DDB_PARENT_UUID "ddb.uuid.parent"
+
+/** VMDK descriptor DDB entry for parent image modification UUID. */
+#define VMDK_DDB_PARENT_MODIFICATION_UUID "ddb.uuid.parentmodification"
+
+/** No compression for streamOptimized files. */
+#define VMDK_COMPRESSION_NONE 0
+
+/** Deflate compression for streamOptimized files. */
+#define VMDK_COMPRESSION_DEFLATE 1
+
+/** Marker that the actual GD value is stored in the footer. */
+#define VMDK_GD_AT_END 0xffffffffffffffffULL
+
+/** Marker for end-of-stream in streamOptimized images. */
+#define VMDK_MARKER_EOS 0
+
+/** Marker for grain table block in streamOptimized images. */
+#define VMDK_MARKER_GT 1
+
+/** Marker for grain directory block in streamOptimized images. */
+#define VMDK_MARKER_GD 2
+
+/** Marker for footer in streamOptimized images. */
+#define VMDK_MARKER_FOOTER 3
+
+/** Dummy marker for "don't check the marker value". */
+#define VMDK_MARKER_IGNORE 0xffffffffU
+
+/**
+ * Magic number for hosted images created by VMware Workstation 4, VMware
+ * Workstation 5, VMware Server or VMware Player. Not necessarily sparse.
+ */
+#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
+
+/**
+ * VMDK hosted binary extent header. The "Sparse" is a total misnomer, as
+ * this header is also used for monolithic flat images.
+ */
+#pragma pack(1)
+typedef struct SparseExtentHeader
+{
+    uint32_t    magicNumber;
+    uint32_t    version;
+    uint32_t    flags;
+    uint64_t    capacity;
+    uint64_t    grainSize;
+    uint64_t    descriptorOffset;
+    uint64_t    descriptorSize;
+    uint32_t    numGTEsPerGT;
+    uint64_t    rgdOffset;
+    uint64_t    gdOffset;
+    uint64_t    overHead;
+    bool        uncleanShutdown;
+    char        singleEndLineChar;
+    char        nonEndLineChar;
+    char        doubleEndLineChar1;
+    char        doubleEndLineChar2;
+    uint16_t    compressAlgorithm;
+    uint8_t     pad[433];
+} SparseExtentHeader;
+#pragma pack()
+
+/** VMDK capacity for a single chunk when 2G splitting is turned on. Should be
+ * divisible by the default grain size (64K) */
+#define VMDK_2G_SPLIT_SIZE (2047 * 1024 * 1024)
+
+/** VMDK streamOptimized file format marker. The type field may or may not
+ * be actually valid, but there's always data to read there. */
+#pragma pack(1)
+typedef struct VMDKMARKER
+{
+    uint64_t uSector;
+    uint32_t cbSize;
+    uint32_t uType;
+} VMDKMARKER, *PVMDKMARKER;
+#pragma pack()
+
+
+#ifdef VBOX_WITH_VMDK_ESX
+
+/** @todo the ESX code is not tested, not used, and lacks error messages. */
+
+/**
+ * Magic number for images created by VMware GSX Server 3 or ESX Server 3.
+ */
+#define VMDK_ESX_SPARSE_MAGICNUMBER 0x44574f43 /* 'C' 'O' 'W' 'D' */
+
+#pragma pack(1)
+typedef struct COWDisk_Header
+{
+    uint32_t    magicNumber;
+    uint32_t    version;
+    uint32_t    flags;
+    uint32_t    numSectors;
+    uint32_t    grainSize;
+    uint32_t    gdOffset;
+    uint32_t    numGDEntries;
+    uint32_t    freeSector;
+    /* The spec incompletely documents quite a few further fields, but states
+     * that they are unused by the current format. Replace them by padding. */
+    char        reserved1[1604];
+    uint32_t    savedGeneration;
+    char        reserved2[8];
+    uint32_t    uncleanShutdown;
+    char        padding[396];
+} COWDisk_Header;
+#pragma pack()
+#endif /* VBOX_WITH_VMDK_ESX */
+
+
+/** Convert sector number/size to byte offset/size. */
+#define VMDK_SECTOR2BYTE(u) ((uint64_t)(u) << 9)
+
+/** Convert byte offset/size to sector number/size. */
+#define VMDK_BYTE2SECTOR(u) ((u) >> 9)
+
+/**
+ * VMDK extent type.
+ */
+typedef enum VMDKETYPE
+{
+    /** Hosted sparse extent. */
+    VMDKETYPE_HOSTED_SPARSE = 1,
+    /** Flat extent. */
+    VMDKETYPE_FLAT,
+    /** Zero extent. */
+    VMDKETYPE_ZERO,
+    /** VMFS extent, used by ESX. */
+    VMDKETYPE_VMFS
+#ifdef VBOX_WITH_VMDK_ESX
+    ,
+    /** ESX sparse extent. */
+    VMDKETYPE_ESX_SPARSE
+#endif /* VBOX_WITH_VMDK_ESX */
+} VMDKETYPE, *PVMDKETYPE;
+
+/**
+ * VMDK access type for a extent.
+ */
+typedef enum VMDKACCESS
+{
+    /** No access allowed. */
+    VMDKACCESS_NOACCESS = 0,
+    /** Read-only access. */
+    VMDKACCESS_READONLY,
+    /** Read-write access. */
+    VMDKACCESS_READWRITE
+} VMDKACCESS, *PVMDKACCESS;
+
+/** Forward declaration for PVMDKIMAGE. */
+typedef struct VMDKIMAGE *PVMDKIMAGE;
+
+/**
+ * Extents files entry. Used for opening a particular file only once.
+ */
+typedef struct VMDKFILE
+{
+    /** Pointer to filename. Local copy. */
+    const char      *pszFilename;
+    /** File open flags for consistency checking. */
+    unsigned         fOpen;
+    /** Flag whether this file has been opened for async I/O. */
+    bool             fAsyncIO;
+    /** Handle for sync/async file abstraction.*/
+    PVDIOSTORAGE     pStorage;
+    /** Reference counter. */
+    unsigned         uReferences;
+    /** Flag whether the file should be deleted on last close. */
+    bool             fDelete;
+    /** Pointer to the image we belong to (for debugging purposes). */
+    PVMDKIMAGE       pImage;
+    /** Pointer to next file descriptor. */
+    struct VMDKFILE *pNext;
+    /** Pointer to the previous file descriptor. */
+    struct VMDKFILE *pPrev;
+} VMDKFILE, *PVMDKFILE;
+
+/**
+ * VMDK extent data structure.
+ */
+typedef struct VMDKEXTENT
+{
+    /** File handle. */
+    PVMDKFILE    pFile;
+    /** Base name of the image extent. */
+    const char  *pszBasename;
+    /** Full name of the image extent. */
+    const char  *pszFullname;
+    /** Number of sectors in this extent. */
+    uint64_t    cSectors;
+    /** Number of sectors per block (grain in VMDK speak). */
+    uint64_t    cSectorsPerGrain;
+    /** Starting sector number of descriptor. */
+    uint64_t    uDescriptorSector;
+    /** Size of descriptor in sectors. */
+    uint64_t    cDescriptorSectors;
+    /** Starting sector number of grain directory. */
+    uint64_t    uSectorGD;
+    /** Starting sector number of redundant grain directory. */
+    uint64_t    uSectorRGD;
+    /** Total number of metadata sectors. */
+    uint64_t    cOverheadSectors;
+    /** Nominal size (i.e. as described by the descriptor) of this extent. */
+    uint64_t    cNominalSectors;
+    /** Sector offset (i.e. as described by the descriptor) of this extent. */
+    uint64_t    uSectorOffset;
+    /** Number of entries in a grain table. */
+    uint32_t    cGTEntries;
+    /** Number of sectors reachable via a grain directory entry. */
+    uint32_t    cSectorsPerGDE;
+    /** Number of entries in the grain directory. */
+    uint32_t    cGDEntries;
+    /** Pointer to the next free sector. Legacy information. Do not use. */
+    uint32_t    uFreeSector;
+    /** Number of this extent in the list of images. */
+    uint32_t    uExtent;
+    /** Pointer to the descriptor (NULL if no descriptor in this extent). */
+    char        *pDescData;
+    /** Pointer to the grain directory. */
+    uint32_t    *pGD;
+    /** Pointer to the redundant grain directory. */
+    uint32_t    *pRGD;
+    /** VMDK version of this extent. 1=1.0/1.1 */
+    uint32_t    uVersion;
+    /** Type of this extent. */
+    VMDKETYPE   enmType;
+    /** Access to this extent. */
+    VMDKACCESS  enmAccess;
+    /** Flag whether this extent is marked as unclean. */
+    bool        fUncleanShutdown;
+    /** Flag whether the metadata in the extent header needs to be updated. */
+    bool        fMetaDirty;
+    /** Flag whether there is a footer in this extent. */
+    bool        fFooter;
+    /** Compression type for this extent. */
+    uint16_t    uCompression;
+    /** Append position for writing new grain. Only for sparse extents. */
+    uint64_t    uAppendPosition;
+    /** Last grain which was accessed. Only for streamOptimized extents. */
+    uint32_t    uLastGrainAccess;
+    /** Starting sector corresponding to the grain buffer. */
+    uint32_t    uGrainSectorAbs;
+    /** Grain number corresponding to the grain buffer. */
+    uint32_t    uGrain;
+    /** Actual size of the compressed data, only valid for reading. */
+    uint32_t    cbGrainStreamRead;
+    /** Size of compressed grain buffer for streamOptimized extents. */
+    size_t      cbCompGrain;
+    /** Compressed grain buffer for streamOptimized extents, with marker. */
+    void        *pvCompGrain;
+    /** Decompressed grain buffer for streamOptimized extents. */
+    void        *pvGrain;
+    /** Reference to the image in which this extent is used. Do not use this
+     * on a regular basis to avoid passing pImage references to functions
+     * explicitly. */
+    struct VMDKIMAGE *pImage;
+} VMDKEXTENT, *PVMDKEXTENT;
+
+/**
+ * Grain table cache size. Allocated per image.
+ */
+#define VMDK_GT_CACHE_SIZE 256
+
+/**
+ * Grain table block size. Smaller than an actual grain table block to allow
+ * more grain table blocks to be cached without having to allocate excessive
+ * amounts of memory for the cache.
+ */
+#define VMDK_GT_CACHELINE_SIZE 128
+
+
+/**
+ * Maximum number of lines in a descriptor file. Not worth the effort of
+ * making it variable. Descriptor files are generally very short (~20 lines),
+ * with the exception of sparse files split in 2G chunks, which need for the
+ * maximum size (almost 2T) exactly 1025 lines for the disk database.
+ */
+#define VMDK_DESCRIPTOR_LINES_MAX   1100U
+
+/**
+ * Parsed descriptor information. Allows easy access and update of the
+ * descriptor (whether separate file or not). Free form text files suck.
+ */
+typedef struct VMDKDESCRIPTOR
+{
+    /** Line number of first entry of the disk descriptor. */
+    unsigned    uFirstDesc;
+    /** Line number of first entry in the extent description. */
+    unsigned    uFirstExtent;
+    /** Line number of first disk database entry. */
+    unsigned    uFirstDDB;
+    /** Total number of lines. */
+    unsigned    cLines;
+    /** Total amount of memory available for the descriptor. */
+    size_t      cbDescAlloc;
+    /** Set if descriptor has been changed and not yet written to disk. */
+    bool        fDirty;
+    /** Array of pointers to the data in the descriptor. */
+    char        *aLines[VMDK_DESCRIPTOR_LINES_MAX];
+    /** Array of line indices pointing to the next non-comment line. */
+    unsigned    aNextLines[VMDK_DESCRIPTOR_LINES_MAX];
+} VMDKDESCRIPTOR, *PVMDKDESCRIPTOR;
+
+
+/**
+ * Cache entry for translating extent/sector to a sector number in that
+ * extent.
+ */
+typedef struct VMDKGTCACHEENTRY
+{
+    /** Extent number for which this entry is valid. */
+    uint32_t    uExtent;
+    /** GT data block number. */
+    uint64_t    uGTBlock;
+    /** Data part of the cache entry. */
+    uint32_t    aGTData[VMDK_GT_CACHELINE_SIZE];
+} VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY;
+
+/**
+ * Cache data structure for blocks of grain table entries. For now this is a
+ * fixed size direct mapping cache, but this should be adapted to the size of
+ * the sparse image and maybe converted to a set-associative cache. The
+ * implementation below implements a write-through cache with write allocate.
+ */
+typedef struct VMDKGTCACHE
+{
+    /** Cache entries. */
+    VMDKGTCACHEENTRY    aGTCache[VMDK_GT_CACHE_SIZE];
+    /** Number of cache entries (currently unused). */
+    unsigned            cEntries;
+} VMDKGTCACHE, *PVMDKGTCACHE;
+
+/**
+ * Complete VMDK image data structure. Mainly a collection of extents and a few
+ * extra global data fields.
+ */
+typedef struct VMDKIMAGE
+{
+    /** Image name. */
+    const char        *pszFilename;
+    /** Descriptor file if applicable. */
+    PVMDKFILE         pFile;
+    /** I/O interface. */
+    PVDINTERFACE      pInterfaceIO;
+    /** I/O interface callbacks. */
+    PVDINTERFACEIOINT pInterfaceIOCallbacks;
+
+    /** Pointer to the per-disk VD interface list. */
+    PVDINTERFACE      pVDIfsDisk;
+    /** Pointer to the per-image VD interface list. */
+    PVDINTERFACE      pVDIfsImage;
+
+    /** Error interface. */
+    PVDINTERFACE      pInterfaceError;
+    /** Error interface callbacks. */
+    PVDINTERFACEERROR pInterfaceErrorCallbacks;
+
+    /** Pointer to the image extents. */
+    PVMDKEXTENT     pExtents;
+    /** Number of image extents. */
+    unsigned        cExtents;
+    /** Pointer to the files list, for opening a file referenced multiple
+     * times only once (happens mainly with raw partition access). */
+    PVMDKFILE       pFiles;
+
+    /**
+     * Pointer to an array of segment entries for async I/O.
+     * This is an optimization because the task number to submit is not known
+     * and allocating/freeing an array in the read/write functions every time
+     * is too expensive.
+     */
+    PPDMDATASEG     paSegments;
+    /** Entries available in the segments array. */
+    unsigned        cSegments;
+
+    /** Open flags passed by VBoxHD layer. */
+    unsigned        uOpenFlags;
+    /** Image flags defined during creation or determined during open. */
+    unsigned        uImageFlags;
+    /** Total size of the image. */
+    uint64_t        cbSize;
+    /** Physical geometry of this image. */
+    VDGEOMETRY      PCHSGeometry;
+    /** Logical geometry of this image. */
+    VDGEOMETRY      LCHSGeometry;
+    /** Image UUID. */
+    RTUUID          ImageUuid;
+    /** Image modification UUID. */
+    RTUUID          ModificationUuid;
+    /** Parent image UUID. */
+    RTUUID          ParentUuid;
+    /** Parent image modification UUID. */
+    RTUUID          ParentModificationUuid;
+
+    /** Pointer to grain table cache, if this image contains sparse extents. */
+    PVMDKGTCACHE    pGTCache;
+    /** Pointer to the descriptor (NULL if no separate descriptor file). */
+    char            *pDescData;
+    /** Allocation size of the descriptor file. */
+    size_t          cbDescAlloc;
+    /** Parsed descriptor file content. */
+    VMDKDESCRIPTOR  Descriptor;
+} VMDKIMAGE;
+
+
+/** State for the input/output callout of the inflate reader/deflate writer. */
+typedef struct VMDKCOMPRESSIO
+{
+    /* Image this operation relates to. */
+    PVMDKIMAGE pImage;
+    /* Current read position. */
+    ssize_t iOffset;
+    /* Size of the compressed grain buffer (available data). */
+    size_t cbCompGrain;
+    /* Pointer to the compressed grain buffer. */
+    void *pvCompGrain;
+} VMDKCOMPRESSIO;
+
+
+/** Tracks async grain allocation. */
+typedef struct VMDKGRAINALLOCASYNC
+{
+    /** Flag whether the allocation failed. */
+    bool        fIoErr;
+    /** Current number of transfers pending.
+     * If reached 0 and there is an error the old state is restored. */
+    unsigned    cIoXfersPending;
+    /** Sector number */
+    uint64_t    uSector;
+    /** Flag whether the grain table needs to be updated. */
+    bool        fGTUpdateNeeded;
+    /** Extent the allocation happens. */
+    PVMDKEXTENT pExtent;
+    /** Position of the new grain, required for the grain table update. */
+    uint64_t    uGrainOffset;
+    /** Grain table sector. */
+    uint64_t    uGTSector;
+    /** Backup grain table sector. */
+    uint64_t    uRGTSector;
+} VMDKGRAINALLOCASYNC, *PVMDKGRAINALLOCASYNC;
+
+/*******************************************************************************
+*   Static Variables                                                           *
+*******************************************************************************/
+
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION s_aVmdkFileExtensions[] =
+{
+    {"vmdk", VDTYPE_HDD},
+    {NULL, VDTYPE_INVALID}
+};
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent);
+static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                               bool fDelete);
+
+static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
+static int vmdkFlushImage(PVMDKIMAGE pImage);
+static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment);
+static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete);
+
+static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq);
+
+/**
+ * Internal: signal an error to the frontend.
+ */
+DECLINLINE(int) vmdkError(PVMDKIMAGE pImage, int rc, RT_SRC_POS_DECL,
+                          const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
+                                                   pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: signal an informational message to the frontend.
+ */
+DECLINLINE(int) vmdkMessage(PVMDKIMAGE pImage, const char *pszFormat, ...)
+{
+    int rc = VINF_SUCCESS;
+    va_list va;
+    va_start(va, pszFormat);
+    if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
+        rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
+                                                          pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
+/**
+ * Internal: open a file (using a file descriptor cache to ensure each file
+ * is only opened once - anything else can cause locking problems).
+ */
+static int vmdkFileOpen(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile,
+                        const char *pszFilename, uint32_t fOpen, bool fAsyncIO)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKFILE pVmdkFile;
+
+    for (pVmdkFile = pImage->pFiles;
+         pVmdkFile != NULL;
+         pVmdkFile = pVmdkFile->pNext)
+    {
+        if (!strcmp(pszFilename, pVmdkFile->pszFilename))
+        {
+            Assert(fOpen == pVmdkFile->fOpen);
+            pVmdkFile->uReferences++;
+
+            *ppVmdkFile = pVmdkFile;
+
+            return rc;
+        }
+    }
+
+    /* If we get here, there's no matching entry in the cache. */
+    pVmdkFile = (PVMDKFILE)RTMemAllocZ(sizeof(VMDKFILE));
+    if (!VALID_PTR(pVmdkFile))
+    {
+        *ppVmdkFile = NULL;
+        return VERR_NO_MEMORY;
+    }
+
+    pVmdkFile->pszFilename = RTStrDup(pszFilename);
+    if (!VALID_PTR(pVmdkFile->pszFilename))
+    {
+        RTMemFree(pVmdkFile);
+        *ppVmdkFile = NULL;
+        return VERR_NO_MEMORY;
+    }
+    pVmdkFile->fOpen = fOpen;
+    pVmdkFile->fAsyncIO = fAsyncIO;
+
+    rc = pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
+                                                pszFilename, fOpen,
+                                                &pVmdkFile->pStorage);
+    if (RT_SUCCESS(rc))
+    {
+        pVmdkFile->uReferences = 1;
+        pVmdkFile->pImage = pImage;
+        pVmdkFile->pNext = pImage->pFiles;
+        if (pImage->pFiles)
+            pImage->pFiles->pPrev = pVmdkFile;
+        pImage->pFiles = pVmdkFile;
+        *ppVmdkFile = pVmdkFile;
+    }
+    else
+    {
+        RTStrFree((char *)(void *)pVmdkFile->pszFilename);
+        RTMemFree(pVmdkFile);
+        *ppVmdkFile = NULL;
+    }
+
+    return rc;
+}
+
+/**
+ * Internal: close a file, updating the file descriptor cache.
+ */
+static int vmdkFileClose(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKFILE pVmdkFile = *ppVmdkFile;
+
+    AssertPtr(pVmdkFile);
+
+    pVmdkFile->fDelete |= fDelete;
+    Assert(pVmdkFile->uReferences);
+    pVmdkFile->uReferences--;
+    if (pVmdkFile->uReferences == 0)
+    {
+        PVMDKFILE pPrev;
+        PVMDKFILE pNext;
+
+        /* Unchain the element from the list. */
+        pPrev = pVmdkFile->pPrev;
+        pNext = pVmdkFile->pNext;
+
+        if (pNext)
+            pNext->pPrev = pPrev;
+        if (pPrev)
+            pPrev->pNext = pNext;
+        else
+            pImage->pFiles = pNext;
+
+        rc = pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
+                                                     pVmdkFile->pStorage);
+        if (RT_SUCCESS(rc) && pVmdkFile->fDelete)
+            rc = pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
+                                                          pVmdkFile->pszFilename);
+        RTStrFree((char *)(void *)pVmdkFile->pszFilename);
+        RTMemFree(pVmdkFile);
+    }
+
+    *ppVmdkFile = NULL;
+    return rc;
+}
+
+/**
+ * Internal: rename a file (sync)
+ */
+DECLINLINE(int) vmdkFileMove(PVMDKIMAGE pImage, const char *pszSrc,
+                             const char *pszDst, unsigned fMove)
+{
+    return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
+                                                  pszSrc, pszDst, fMove);
+}
+
+/**
+ * Internal: get the size of a file (sync/async)
+ */
+DECLINLINE(int) vmdkFileGetSize(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                uint64_t *pcbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
+                                                     pVmdkFile->pStorage,
+                                                     pcbSize);
+}
+
+/**
+ * Internal: set the size of a file (sync/async)
+ */
+DECLINLINE(int) vmdkFileSetSize(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                uint64_t cbSize)
+{
+    return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
+                                                     pVmdkFile->pStorage,
+                                                     cbSize);
+}
+
+/**
+ * Internal: read from a file (sync)
+ */
+DECLINLINE(int) vmdkFileReadSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                 uint64_t uOffset, void *pvBuf,
+                                 size_t cbToRead, size_t *pcbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
+                                                      pVmdkFile->pStorage, uOffset,
+                                                      pvBuf, cbToRead, pcbRead);
+}
+
+/**
+ * Internal: write to a file (sync)
+ */
+DECLINLINE(int) vmdkFileWriteSync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                  uint64_t uOffset, const void *pvBuf,
+                                  size_t cbToWrite, size_t *pcbWritten)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
+                                                       pVmdkFile->pStorage, uOffset,
+                                                       pvBuf, cbToWrite, pcbWritten);
+}
+
+/**
+ * Internal: flush a file (sync)
+ */
+DECLINLINE(int) vmdkFileFlush(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
+                                                       pVmdkFile->pStorage);
+}
+
+/**
+ * Internal: read user data (async)
+ */
+DECLINLINE(int) vmdkFileReadUserAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                      uint64_t uOffset, PVDIOCTX pIoCtx,
+                                      size_t cbRead)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
+                                                           pVmdkFile->pStorage,
+                                                           uOffset, pIoCtx,
+                                                           cbRead);
+}
+
+/**
+ * Internal: write user data (async)
+ */
+DECLINLINE(int) vmdkFileWriteUserAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                       uint64_t uOffset, PVDIOCTX pIoCtx,
+                                       size_t cbWrite,
+                                       PFNVDXFERCOMPLETED pfnComplete,
+                                       void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
+                                                            pVmdkFile->pStorage,
+                                                            uOffset, pIoCtx,
+                                                            cbWrite,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+/**
+ * Internal: read metadata (async)
+ */
+DECLINLINE(int) vmdkFileReadMetaAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                      uint64_t uOffset, void *pvBuffer,
+                                      size_t cbBuffer, PVDIOCTX pIoCtx,
+                                      PPVDMETAXFER ppMetaXfer,
+                                      PFNVDXFERCOMPLETED pfnComplete,
+                                      void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnReadMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                           pVmdkFile->pStorage,
+                                                           uOffset, pvBuffer,
+                                                           cbBuffer, pIoCtx,
+                                                           ppMetaXfer,
+                                                           pfnComplete,
+                                                           pvCompleteUser);
+}
+
+/**
+ * Internal: write metadata (async)
+ */
+DECLINLINE(int) vmdkFileWriteMetaAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                       uint64_t uOffset, void *pvBuffer,
+                                       size_t cbBuffer, PVDIOCTX pIoCtx,
+                                       PFNVDXFERCOMPLETED pfnComplete,
+                                       void *pvCompleteUser)
+{
+    return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
+                                                            pVmdkFile->pStorage,
+                                                            uOffset, pvBuffer,
+                                                            cbBuffer, pIoCtx,
+                                                            pfnComplete,
+                                                            pvCompleteUser);
+}
+
+/**
+ * Internal: releases a metadata transfer handle (async)
+ */
+DECLINLINE(void) vmdkFileMetaXferRelease(PVMDKIMAGE pImage, PVDMETAXFER pMetaXfer)
+{
+    pImage->pInterfaceIOCallbacks->pfnMetaXferRelease(pImage->pInterfaceIO->pvUser,
+                                                      pMetaXfer);
+}
+
+/**
+ * Internal: flush a file (async)
+ */
+DECLINLINE(int) vmdkFileFlushAsync(PVMDKIMAGE pImage, PVMDKFILE pVmdkFile,
+                                   PVDIOCTX pIoCtx)
+{
+    return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
+                                                        pVmdkFile->pStorage, pIoCtx,
+                                                        NULL, NULL);
+}
+
+/**
+ * Internal: sets the buffer to a specific byte (async)
+ */
+DECLINLINE(int) vmdkFileIoCtxSet(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
+                                 int ch, size_t cbSet)
+{
+    return pImage->pInterfaceIOCallbacks->pfnIoCtxSet(pImage->pInterfaceIO->pvUser,
+                                                      pIoCtx, ch, cbSet);
+}
+
+
+static DECLCALLBACK(int) vmdkFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
+{
+    VMDKCOMPRESSIO *pInflateState = (VMDKCOMPRESSIO *)pvUser;
+    size_t cbInjected = 0;
+
+    Assert(cbBuf);
+    if (pInflateState->iOffset < 0)
+    {
+        *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
+        pvBuf = (uint8_t *)pvBuf + 1;
+        cbBuf--;
+        cbInjected = 1;
+        pInflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
+    }
+    if (!cbBuf)
+    {
+        if (pcbBuf)
+            *pcbBuf = cbInjected;
+        return VINF_SUCCESS;
+    }
+    cbBuf = RT_MIN(cbBuf, pInflateState->cbCompGrain - pInflateState->iOffset);
+    memcpy(pvBuf,
+           (uint8_t *)pInflateState->pvCompGrain + pInflateState->iOffset,
+           cbBuf);
+    pInflateState->iOffset += cbBuf;
+    Assert(pcbBuf);
+    *pcbBuf = cbBuf + cbInjected;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal: read from a file and inflate the compressed data,
+ * distinguishing between async and normal operation
+ */
+DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                    uint64_t uOffset, void *pvBuf,
+                                    size_t cbToRead, const void *pcvMarker,
+                                    uint64_t *puLBA, uint32_t *pcbMarkerData)
+{
+    if (pExtent->pFile->fAsyncIO)
+    {
+        AssertMsgFailed(("TODO\n"));
+        return VERR_NOT_SUPPORTED;
+    }
+    else
+    {
+        int rc;
+        PRTZIPDECOMP pZip = NULL;
+        VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+        size_t cbCompSize, cbActuallyRead;
+
+        if (!pcvMarker)
+        {
+            rc = vmdkFileReadSync(pImage, pExtent->pFile, uOffset, pMarker,
+                                  RT_OFFSETOF(VMDKMARKER, uType), NULL);
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+        else
+            memcpy(pMarker, pcvMarker, RT_OFFSETOF(VMDKMARKER, uType));
+
+        cbCompSize = RT_LE2H_U32(pMarker->cbSize);
+        if (cbCompSize == 0)
+        {
+            AssertMsgFailed(("VMDK: corrupted marker\n"));
+            return VERR_VD_VMDK_INVALID_FORMAT;
+        }
+
+        /* Sanity check - the expansion ratio should be much less than 2. */
+        Assert(cbCompSize < 2 * cbToRead);
+        if (cbCompSize >= 2 * cbToRead)
+            return VERR_VD_VMDK_INVALID_FORMAT;
+
+        /* Compressed grain marker. Data follows immediately. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              uOffset + RT_OFFSETOF(VMDKMARKER, uType),
+                                (uint8_t *)pExtent->pvCompGrain
+                              + RT_OFFSETOF(VMDKMARKER, uType),
+                                RT_ALIGN_Z(  cbCompSize
+                                           + RT_OFFSETOF(VMDKMARKER, uType),
+                                           512)
+                              - RT_OFFSETOF(VMDKMARKER, uType), NULL);
+
+        if (puLBA)
+            *puLBA = RT_LE2H_U64(pMarker->uSector);
+        if (pcbMarkerData)
+            *pcbMarkerData = RT_ALIGN(  cbCompSize
+                                      + RT_OFFSETOF(VMDKMARKER, uType),
+                                      512);
+
+        VMDKCOMPRESSIO InflateState;
+        InflateState.pImage = pImage;
+        InflateState.iOffset = -1;
+        InflateState.cbCompGrain = cbCompSize + RT_OFFSETOF(VMDKMARKER, uType);
+        InflateState.pvCompGrain = pExtent->pvCompGrain;
+
+        rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
+        if (RT_FAILURE(rc))
+            return rc;
+        rc = RTZipDecompress(pZip, pvBuf, cbToRead, &cbActuallyRead);
+        RTZipDecompDestroy(pZip);
+        if (RT_FAILURE(rc))
+            return rc;
+        if (cbActuallyRead != cbToRead)
+            rc = VERR_VD_VMDK_INVALID_FORMAT;
+        return rc;
+    }
+}
+
+static DECLCALLBACK(int) vmdkFileDeflateHelper(void *pvUser, const void *pvBuf, size_t cbBuf)
+{
+    VMDKCOMPRESSIO *pDeflateState = (VMDKCOMPRESSIO *)pvUser;
+
+    Assert(cbBuf);
+    if (pDeflateState->iOffset < 0)
+    {
+        pvBuf = (const uint8_t *)pvBuf + 1;
+        cbBuf--;
+        pDeflateState->iOffset = RT_OFFSETOF(VMDKMARKER, uType);
+    }
+    if (!cbBuf)
+        return VINF_SUCCESS;
+    if (pDeflateState->iOffset + cbBuf > pDeflateState->cbCompGrain)
+        return VERR_BUFFER_OVERFLOW;
+    memcpy((uint8_t *)pDeflateState->pvCompGrain + pDeflateState->iOffset,
+           pvBuf, cbBuf);
+    pDeflateState->iOffset += cbBuf;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal: deflate the uncompressed data and write to a file,
+ * distinguishing between async and normal operation
+ */
+DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                    uint64_t uOffset, const void *pvBuf,
+                                    size_t cbToWrite, uint64_t uLBA,
+                                    uint32_t *pcbMarkerData)
+{
+    if (pExtent->pFile->fAsyncIO)
+    {
+        AssertMsgFailed(("TODO\n"));
+        return VERR_NOT_SUPPORTED;
+    }
+    else
+    {
+        int rc;
+        PRTZIPCOMP pZip = NULL;
+        VMDKCOMPRESSIO DeflateState;
+
+        DeflateState.pImage = pImage;
+        DeflateState.iOffset = -1;
+        DeflateState.cbCompGrain = pExtent->cbCompGrain;
+        DeflateState.pvCompGrain = pExtent->pvCompGrain;
+
+        rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper,
+                             RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
+        if (RT_FAILURE(rc))
+            return rc;
+        rc = RTZipCompress(pZip, pvBuf, cbToWrite);
+        if (RT_SUCCESS(rc))
+            rc = RTZipCompFinish(pZip);
+        RTZipCompDestroy(pZip);
+        if (RT_SUCCESS(rc))
+        {
+            Assert(   DeflateState.iOffset > 0
+                   && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
+
+            /* pad with zeroes to get to a full sector size */
+            uint32_t uSize = DeflateState.iOffset;
+            if (uSize % 512)
+            {
+                uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
+                memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
+                       uSizeAlign - uSize);
+                uSize = uSizeAlign;
+            }
+
+            if (pcbMarkerData)
+                *pcbMarkerData = uSize;
+
+            /* Compressed grain marker. Data follows immediately. */
+            VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
+            pMarker->uSector = RT_H2LE_U64(uLBA);
+            pMarker->cbSize = RT_H2LE_U32(  DeflateState.iOffset
+                                          - RT_OFFSETOF(VMDKMARKER, uType));
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOffset, pMarker,
+                                   uSize, NULL);
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+        return rc;
+    }
+}
+
+
+/**
+ * Internal: check if all files are closed, prevent leaking resources.
+ */
+static int vmdkFileCheckAllClose(PVMDKIMAGE pImage)
+{
+    int rc = VINF_SUCCESS, rc2;
+    PVMDKFILE pVmdkFile;
+
+    Assert(pImage->pFiles == NULL);
+    for (pVmdkFile = pImage->pFiles;
+         pVmdkFile != NULL;
+         pVmdkFile = pVmdkFile->pNext)
+    {
+        LogRel(("VMDK: leaking reference to file \"%s\"\n",
+                pVmdkFile->pszFilename));
+        pImage->pFiles = pVmdkFile->pNext;
+
+        rc2 = vmdkFileClose(pImage, &pVmdkFile, pVmdkFile->fDelete);
+
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+    return rc;
+}
+
+/**
+ * Internal: truncate a string (at a UTF8 code point boundary) and encode the
+ * critical non-ASCII characters.
+ */
+static char *vmdkEncodeString(const char *psz)
+{
+    char szEnc[VMDK_ENCODED_COMMENT_MAX + 3];
+    char *pszDst = szEnc;
+
+    AssertPtr(psz);
+
+    for (; *psz; psz = RTStrNextCp(psz))
+    {
+        char *pszDstPrev = pszDst;
+        RTUNICP Cp = RTStrGetCp(psz);
+        if (Cp == '\\')
+        {
+            pszDst = RTStrPutCp(pszDst, Cp);
+            pszDst = RTStrPutCp(pszDst, Cp);
+        }
+        else if (Cp == '\n')
+        {
+            pszDst = RTStrPutCp(pszDst, '\\');
+            pszDst = RTStrPutCp(pszDst, 'n');
+        }
+        else if (Cp == '\r')
+        {
+            pszDst = RTStrPutCp(pszDst, '\\');
+            pszDst = RTStrPutCp(pszDst, 'r');
+        }
+        else
+            pszDst = RTStrPutCp(pszDst, Cp);
+        if (pszDst - szEnc >= VMDK_ENCODED_COMMENT_MAX - 1)
+        {
+            pszDst = pszDstPrev;
+            break;
+        }
+    }
+    *pszDst = '\0';
+    return RTStrDup(szEnc);
+}
+
+/**
+ * Internal: decode a string and store it into the specified string.
+ */
+static int vmdkDecodeString(const char *pszEncoded, char *psz, size_t cb)
+{
+    int rc = VINF_SUCCESS;
+    char szBuf[4];
+
+    if (!cb)
+        return VERR_BUFFER_OVERFLOW;
+
+    AssertPtr(psz);
+
+    for (; *pszEncoded; pszEncoded = RTStrNextCp(pszEncoded))
+    {
+        char *pszDst = szBuf;
+        RTUNICP Cp = RTStrGetCp(pszEncoded);
+        if (Cp == '\\')
+        {
+            pszEncoded = RTStrNextCp(pszEncoded);
+            RTUNICP CpQ = RTStrGetCp(pszEncoded);
+            if (CpQ == 'n')
+                RTStrPutCp(pszDst, '\n');
+            else if (CpQ == 'r')
+                RTStrPutCp(pszDst, '\r');
+            else if (CpQ == '\0')
+            {
+                rc = VERR_VD_VMDK_INVALID_HEADER;
+                break;
+            }
+            else
+                RTStrPutCp(pszDst, CpQ);
+        }
+        else
+            pszDst = RTStrPutCp(pszDst, Cp);
+
+        /* Need to leave space for terminating NUL. */
+        if ((size_t)(pszDst - szBuf) + 1 >= cb)
+        {
+            rc = VERR_BUFFER_OVERFLOW;
+            break;
+        }
+        memcpy(psz, szBuf, pszDst - szBuf);
+        psz += pszDst - szBuf;
+    }
+    *psz = '\0';
+    return rc;
+}
+
+/**
+ * Internal: free all buffers associated with grain directories.
+ */
+static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)
+{
+    if (pExtent->pGD)
+    {
+        RTMemFree(pExtent->pGD);
+        pExtent->pGD = NULL;
+    }
+    if (pExtent->pRGD)
+    {
+        RTMemFree(pExtent->pRGD);
+        pExtent->pRGD = NULL;
+    }
+}
+
+/**
+ * Internal: allocate the compressed/uncompressed buffers for streamOptimized
+ * images.
+ */
+static int vmdkAllocStreamBuffers(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
+{
+    int rc = VINF_SUCCESS;
+
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        /* streamOptimized extents need a compressed grain buffer, which must
+         * be big enough to hold uncompressible data (which needs ~8 bytes
+         * more than the uncompressed data), the marker and padding. */
+        pExtent->cbCompGrain = RT_ALIGN_Z(  VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
+                                          + 8 + sizeof(VMDKMARKER), 512);
+        pExtent->pvCompGrain = RTMemAlloc(pExtent->cbCompGrain);
+        if (!pExtent->pvCompGrain)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        /* streamOptimized extents need a decompressed grain buffer. */
+        pExtent->pvGrain = RTMemAlloc(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
+        if (!pExtent->pvGrain)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeStreamBuffers(pExtent);
+    return rc;
+}
+
+/**
+ * Internal: allocate all buffers associated with grain directories.
+ */
+static int vmdkAllocGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
+{
+    int rc = VINF_SUCCESS;
+    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
+    uint32_t *pGD = NULL, *pRGD = NULL;
+
+    pGD = (uint32_t *)RTMemAllocZ(cbGD);
+    if (!pGD)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pExtent->pGD = pGD;
+
+    if (pExtent->uSectorRGD)
+    {
+        pRGD = (uint32_t *)RTMemAllocZ(cbGD);
+        if (!pRGD)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+        pExtent->pRGD = pRGD;
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeGrainDirectory(pExtent);
+    return rc;
+}
+
+static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
+{
+    int rc = VINF_SUCCESS;
+    unsigned i;
+    uint32_t *pGDTmp, *pRGDTmp;
+    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
+
+    if (pExtent->enmType != VMDKETYPE_HOSTED_SPARSE)
+        goto out;
+
+    if (   pExtent->uSectorGD == VMDK_GD_AT_END
+        || pExtent->uSectorRGD == VMDK_GD_AT_END)
+    {
+        rc = VERR_INTERNAL_ERROR;
+        goto out;
+    }
+
+    rc = vmdkAllocGrainDirectory(pImage, pExtent);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* The VMDK 1.1 spec seems to talk about compressed grain directories,
+     * but in reality they are not compressed. */
+    rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                          VMDK_SECTOR2BYTE(pExtent->uSectorGD),
+                          pExtent->pGD, cbGD, NULL);
+    AssertRC(rc);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not read grain directory in '%s': %Rrc"), pExtent->pszFullname);
+        goto out;
+    }
+    for (i = 0, pGDTmp = pExtent->pGD; i < pExtent->cGDEntries; i++, pGDTmp++)
+        *pGDTmp = RT_LE2H_U32(*pGDTmp);
+
+    if (pExtent->uSectorRGD)
+    {
+        /* The VMDK 1.1 spec seems to talk about compressed grain directories,
+         * but in reality they are not compressed. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
+                              pExtent->pRGD, cbGD, NULL);
+        AssertRC(rc);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not read redundant grain directory in '%s'"), pExtent->pszFullname);
+            goto out;
+        }
+        for (i = 0, pRGDTmp = pExtent->pRGD; i < pExtent->cGDEntries; i++, pRGDTmp++)
+            *pRGDTmp = RT_LE2H_U32(*pRGDTmp);
+
+        /* Check grain table and redundant grain table for consistency. */
+        size_t cbGT = pExtent->cGTEntries * sizeof(uint32_t);
+        uint32_t *pTmpGT1 = (uint32_t *)RTMemTmpAlloc(cbGT);
+        if (!pTmpGT1)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+        uint32_t *pTmpGT2 = (uint32_t *)RTMemTmpAlloc(cbGT);
+        if (!pTmpGT2)
+        {
+            RTMemTmpFree(pTmpGT1);
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        for (i = 0, pGDTmp = pExtent->pGD, pRGDTmp = pExtent->pRGD;
+             i < pExtent->cGDEntries;
+             i++, pGDTmp++, pRGDTmp++)
+        {
+            /* If no grain table is allocated skip the entry. */
+            if (*pGDTmp == 0 && *pRGDTmp == 0)
+                continue;
+
+            if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
+            {
+                /* Just one grain directory entry refers to a not yet allocated
+                 * grain table or both grain directory copies refer to the same
+                 * grain table. Not allowed. */
+                RTMemTmpFree(pTmpGT1);
+                RTMemTmpFree(pTmpGT2);
+                rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);
+                goto out;
+            }
+            /* The VMDK 1.1 spec seems to talk about compressed grain tables,
+             * but in reality they are not compressed. */
+            rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                  VMDK_SECTOR2BYTE(*pGDTmp),
+                                  pTmpGT1, cbGT, NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);
+                RTMemTmpFree(pTmpGT1);
+                RTMemTmpFree(pTmpGT2);
+                goto out;
+            }
+            /* The VMDK 1.1 spec seems to talk about compressed grain tables,
+             * but in reality they are not compressed. */
+            rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                  VMDK_SECTOR2BYTE(*pRGDTmp),
+                                  pTmpGT2, cbGT, NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading backup grain table in '%s'"), pExtent->pszFullname);
+                RTMemTmpFree(pTmpGT1);
+                RTMemTmpFree(pTmpGT2);
+                goto out;
+            }
+            if (memcmp(pTmpGT1, pTmpGT2, cbGT))
+            {
+                RTMemTmpFree(pTmpGT1);
+                RTMemTmpFree(pTmpGT2);
+                rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistency between grain table and backup grain table in '%s'"), pExtent->pszFullname);
+                goto out;
+            }
+        }
+
+        /** @todo figure out what to do for unclean VMDKs. */
+        RTMemTmpFree(pTmpGT1);
+        RTMemTmpFree(pTmpGT2);
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeGrainDirectory(pExtent);
+    return rc;
+}
+
+static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                    uint64_t uStartSector, bool fPreAlloc)
+{
+    int rc = VINF_SUCCESS;
+    unsigned i;
+    size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
+    size_t cbGDRounded = RT_ALIGN_64(pExtent->cGDEntries * sizeof(uint32_t), 512);
+    size_t cbGTRounded;
+    uint64_t cbOverhead;
+
+    if (fPreAlloc)
+    {
+        cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
+        cbOverhead =   VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded
+                     + cbGTRounded;
+    }
+    else
+    {
+        /* Use a dummy start sector for layout computation. */
+        if (uStartSector == VMDK_GD_AT_END)
+            uStartSector = 1;
+        cbGTRounded = 0;
+        cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded;
+    }
+
+    /* For streamOptimized extents there is only one grain directory,
+     * and for all others take redundant grain directory into account. */
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        cbOverhead = RT_ALIGN_64(cbOverhead,
+                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
+    }
+    else
+    {
+        cbOverhead += cbGDRounded + cbGTRounded;
+        cbOverhead = RT_ALIGN_64(cbOverhead,
+                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
+        rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead);
+    }
+    if (RT_FAILURE(rc))
+        goto out;
+    pExtent->uAppendPosition = cbOverhead;
+    pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);
+
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        pExtent->uSectorRGD = 0;
+        pExtent->uSectorGD = uStartSector;
+    }
+    else
+    {
+        pExtent->uSectorRGD = uStartSector;
+        pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded);
+    }
+
+    rc = vmdkAllocStreamBuffers(pImage, pExtent);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = vmdkAllocGrainDirectory(pImage, pExtent);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    if (fPreAlloc)
+    {
+        uint32_t uGTSectorLE;
+        uint64_t uOffsetSectors;
+
+        if (pExtent->pRGD)
+        {
+            uOffsetSectors = pExtent->uSectorRGD + VMDK_BYTE2SECTOR(cbGDRounded);
+            for (i = 0; i < pExtent->cGDEntries; i++)
+            {
+                pExtent->pRGD[i] = uOffsetSectors;
+                uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
+                /* Write the redundant grain directory entry to disk. */
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                       VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE),
+                                       &uGTSectorLE, sizeof(uGTSectorLE), NULL);
+                if (RT_FAILURE(rc))
+                {
+                    rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);
+                    goto out;
+                }
+                uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
+            }
+        }
+
+        uOffsetSectors = pExtent->uSectorGD + VMDK_BYTE2SECTOR(cbGDRounded);
+        for (i = 0; i < pExtent->cGDEntries; i++)
+        {
+            pExtent->pGD[i] = uOffsetSectors;
+            uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
+            /* Write the grain directory entry to disk. */
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE),
+                                   &uGTSectorLE, sizeof(uGTSectorLE), NULL);
+            if (RT_FAILURE(rc))
+            {
+                rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);
+                goto out;
+            }
+            uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
+        }
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeGrainDirectory(pExtent);
+    return rc;
+}
+
+static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr,
+                             char **ppszUnquoted, char **ppszNext)
+{
+    char *pszQ;
+    char *pszUnquoted;
+
+    /* Skip over whitespace. */
+    while (*pszStr == ' ' || *pszStr == '\t')
+        pszStr++;
+
+    if (*pszStr != '"')
+    {
+        pszQ = (char *)pszStr;
+        while (*pszQ && *pszQ != ' ' && *pszQ != '\t')
+            pszQ++;
+    }
+    else
+    {
+        pszStr++;
+        pszQ = (char *)strchr(pszStr, '"');
+        if (pszQ == NULL)
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);
+    }
+
+    pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);
+    if (!pszUnquoted)
+        return VERR_NO_MEMORY;
+    memcpy(pszUnquoted, pszStr, pszQ - pszStr);
+    pszUnquoted[pszQ - pszStr] = '\0';
+    *ppszUnquoted = pszUnquoted;
+    if (ppszNext)
+        *ppszNext = pszQ + 1;
+    return VINF_SUCCESS;
+}
+
+static int vmdkDescInitStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                           const char *pszLine)
+{
+    char *pEnd = pDescriptor->aLines[pDescriptor->cLines];
+    ssize_t cbDiff = strlen(pszLine) + 1;
+
+    if (    pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1
+        &&  pEnd - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
+        return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
+
+    memcpy(pEnd, pszLine, cbDiff);
+    pDescriptor->cLines++;
+    pDescriptor->aLines[pDescriptor->cLines] = pEnd + cbDiff;
+    pDescriptor->fDirty = true;
+
+    return VINF_SUCCESS;
+}
+
+static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,
+                           const char *pszKey, const char **ppszValue)
+{
+    size_t cbKey = strlen(pszKey);
+    const char *pszValue;
+
+    while (uStart != 0)
+    {
+        if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
+        {
+            /* Key matches, check for a '=' (preceded by whitespace). */
+            pszValue = pDescriptor->aLines[uStart] + cbKey;
+            while (*pszValue == ' ' || *pszValue == '\t')
+                pszValue++;
+            if (*pszValue == '=')
+            {
+                *ppszValue = pszValue + 1;
+                break;
+            }
+        }
+        uStart = pDescriptor->aNextLines[uStart];
+    }
+    return !!uStart;
+}
+
+static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                          unsigned uStart,
+                          const char *pszKey, const char *pszValue)
+{
+    char *pszTmp;
+    size_t cbKey = strlen(pszKey);
+    unsigned uLast = 0;
+
+    while (uStart != 0)
+    {
+        if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
+        {
+            /* Key matches, check for a '=' (preceded by whitespace). */
+            pszTmp = pDescriptor->aLines[uStart] + cbKey;
+            while (*pszTmp == ' ' || *pszTmp == '\t')
+                pszTmp++;
+            if (*pszTmp == '=')
+            {
+                pszTmp++;
+                while (*pszTmp == ' ' || *pszTmp == '\t')
+                    pszTmp++;
+                break;
+            }
+        }
+        if (!pDescriptor->aNextLines[uStart])
+            uLast = uStart;
+        uStart = pDescriptor->aNextLines[uStart];
+    }
+    if (uStart)
+    {
+        if (pszValue)
+        {
+            /* Key already exists, replace existing value. */
+            size_t cbOldVal = strlen(pszTmp);
+            size_t cbNewVal = strlen(pszValue);
+            ssize_t cbDiff = cbNewVal - cbOldVal;
+            /* Check for buffer overflow. */
+            if (    pDescriptor->aLines[pDescriptor->cLines]
+                -   pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
+                return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
+
+            memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,
+                    pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);
+            memcpy(pszTmp, pszValue, cbNewVal + 1);
+            for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
+                pDescriptor->aLines[i] += cbDiff;
+        }
+        else
+        {
+            memmove(pDescriptor->aLines[uStart], pDescriptor->aLines[uStart+1],
+                    pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uStart+1] + 1);
+            for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
+            {
+                pDescriptor->aLines[i-1] = pDescriptor->aLines[i];
+                if (pDescriptor->aNextLines[i])
+                    pDescriptor->aNextLines[i-1] = pDescriptor->aNextLines[i] - 1;
+                else
+                    pDescriptor->aNextLines[i-1] = 0;
+            }
+            pDescriptor->cLines--;
+            /* Adjust starting line numbers of following descriptor sections. */
+            if (uStart < pDescriptor->uFirstExtent)
+                pDescriptor->uFirstExtent--;
+            if (uStart < pDescriptor->uFirstDDB)
+                pDescriptor->uFirstDDB--;
+        }
+    }
+    else
+    {
+        /* Key doesn't exist, append after the last entry in this category. */
+        if (!pszValue)
+        {
+            /* Key doesn't exist, and it should be removed. Simply a no-op. */
+            return VINF_SUCCESS;
+        }
+        cbKey = strlen(pszKey);
+        size_t cbValue = strlen(pszValue);
+        ssize_t cbDiff = cbKey + 1 + cbValue + 1;
+        /* Check for buffer overflow. */
+        if (   (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
+            || (  pDescriptor->aLines[pDescriptor->cLines]
+                - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
+            return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
+        for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
+        {
+            pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
+            if (pDescriptor->aNextLines[i - 1])
+                pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
+            else
+                pDescriptor->aNextLines[i] = 0;
+        }
+        uStart = uLast + 1;
+        pDescriptor->aNextLines[uLast] = uStart;
+        pDescriptor->aNextLines[uStart] = 0;
+        pDescriptor->cLines++;
+        pszTmp = pDescriptor->aLines[uStart];
+        memmove(pszTmp + cbDiff, pszTmp,
+                pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
+        memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);
+        pDescriptor->aLines[uStart][cbKey] = '=';
+        memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);
+        for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
+            pDescriptor->aLines[i] += cbDiff;
+
+        /* Adjust starting line numbers of following descriptor sections. */
+        if (uStart <= pDescriptor->uFirstExtent)
+            pDescriptor->uFirstExtent++;
+        if (uStart <= pDescriptor->uFirstDDB)
+            pDescriptor->uFirstDDB++;
+    }
+    pDescriptor->fDirty = true;
+    return VINF_SUCCESS;
+}
+
+static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,
+                              uint32_t *puValue)
+{
+    const char *pszValue;
+
+    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
+                        &pszValue))
+        return VERR_VD_VMDK_VALUE_NOT_FOUND;
+    return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);
+}
+
+static int vmdkDescBaseGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                              const char *pszKey, const char **ppszValue)
+{
+    const char *pszValue;
+    char *pszValueUnquoted;
+
+    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
+                        &pszValue))
+        return VERR_VD_VMDK_VALUE_NOT_FOUND;
+    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    *ppszValue = pszValueUnquoted;
+    return rc;
+}
+
+static int vmdkDescBaseSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                              const char *pszKey, const char *pszValue)
+{
+    char *pszValueQuoted;
+
+    RTStrAPrintf(&pszValueQuoted, "\"%s\"", pszValue);
+    if (!pszValueQuoted)
+        return VERR_NO_STR_MEMORY;
+    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, pszKey,
+                            pszValueQuoted);
+    RTStrFree(pszValueQuoted);
+    return rc;
+}
+
+static void vmdkDescExtRemoveDummy(PVMDKIMAGE pImage,
+                                   PVMDKDESCRIPTOR pDescriptor)
+{
+    unsigned uEntry = pDescriptor->uFirstExtent;
+    ssize_t cbDiff;
+
+    if (!uEntry)
+        return;
+
+    cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;
+    /* Move everything including \0 in the entry marking the end of buffer. */
+    memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],
+            pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);
+    for (unsigned i = uEntry + 1; i <= pDescriptor->cLines; i++)
+    {
+        pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;
+        if (pDescriptor->aNextLines[i])
+            pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;
+        else
+            pDescriptor->aNextLines[i - 1] = 0;
+    }
+    pDescriptor->cLines--;
+    if (pDescriptor->uFirstDDB)
+        pDescriptor->uFirstDDB--;
+
+    return;
+}
+
+static int vmdkDescExtInsert(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                             VMDKACCESS enmAccess, uint64_t cNominalSectors,
+                             VMDKETYPE enmType, const char *pszBasename,
+                             uint64_t uSectorOffset)
+{
+    static const char *apszAccess[] = { "NOACCESS", "RDONLY", "RW" };
+    static const char *apszType[] = { "", "SPARSE", "FLAT", "ZERO", "VMFS" };
+    char *pszTmp;
+    unsigned uStart = pDescriptor->uFirstExtent, uLast = 0;
+    char szExt[1024];
+    ssize_t cbDiff;
+
+    Assert((unsigned)enmAccess < RT_ELEMENTS(apszAccess));
+    Assert((unsigned)enmType < RT_ELEMENTS(apszType));
+
+    /* Find last entry in extent description. */
+    while (uStart)
+    {
+        if (!pDescriptor->aNextLines[uStart])
+            uLast = uStart;
+        uStart = pDescriptor->aNextLines[uStart];
+    }
+
+    if (enmType == VMDKETYPE_ZERO)
+    {
+        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s ", apszAccess[enmAccess],
+                    cNominalSectors, apszType[enmType]);
+    }
+    else if (enmType == VMDKETYPE_FLAT)
+    {
+        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\" %llu",
+                    apszAccess[enmAccess], cNominalSectors,
+                    apszType[enmType], pszBasename, uSectorOffset);
+    }
+    else
+    {
+        RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\"",
+                    apszAccess[enmAccess], cNominalSectors,
+                    apszType[enmType], pszBasename);
+    }
+    cbDiff = strlen(szExt) + 1;
+
+    /* Check for buffer overflow. */
+    if (   (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
+        || (  pDescriptor->aLines[pDescriptor->cLines]
+            - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
+        return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
+
+    for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
+    {
+        pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
+        if (pDescriptor->aNextLines[i - 1])
+            pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
+        else
+            pDescriptor->aNextLines[i] = 0;
+    }
+    uStart = uLast + 1;
+    pDescriptor->aNextLines[uLast] = uStart;
+    pDescriptor->aNextLines[uStart] = 0;
+    pDescriptor->cLines++;
+    pszTmp = pDescriptor->aLines[uStart];
+    memmove(pszTmp + cbDiff, pszTmp,
+            pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
+    memcpy(pDescriptor->aLines[uStart], szExt, cbDiff);
+    for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
+        pDescriptor->aLines[i] += cbDiff;
+
+    /* Adjust starting line numbers of following descriptor sections. */
+    if (uStart <= pDescriptor->uFirstDDB)
+        pDescriptor->uFirstDDB++;
+
+    pDescriptor->fDirty = true;
+    return VINF_SUCCESS;
+}
+
+static int vmdkDescDDBGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                             const char *pszKey, const char **ppszValue)
+{
+    const char *pszValue;
+    char *pszValueUnquoted;
+
+    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                        &pszValue))
+        return VERR_VD_VMDK_VALUE_NOT_FOUND;
+    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    *ppszValue = pszValueUnquoted;
+    return rc;
+}
+
+static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                             const char *pszKey, uint32_t *puValue)
+{
+    const char *pszValue;
+    char *pszValueUnquoted;
+
+    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                        &pszValue))
+        return VERR_VD_VMDK_VALUE_NOT_FOUND;
+    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);
+    RTMemTmpFree(pszValueUnquoted);
+    return rc;
+}
+
+static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                              const char *pszKey, PRTUUID pUuid)
+{
+    const char *pszValue;
+    char *pszValueUnquoted;
+
+    if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                        &pszValue))
+        return VERR_VD_VMDK_VALUE_NOT_FOUND;
+    int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = RTUuidFromStr(pUuid, pszValueUnquoted);
+    RTMemTmpFree(pszValueUnquoted);
+    return rc;
+}
+
+static int vmdkDescDDBSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                             const char *pszKey, const char *pszVal)
+{
+    int rc;
+    char *pszValQuoted;
+
+    if (pszVal)
+    {
+        RTStrAPrintf(&pszValQuoted, "\"%s\"", pszVal);
+        if (!pszValQuoted)
+            return VERR_NO_STR_MEMORY;
+    }
+    else
+        pszValQuoted = NULL;
+    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                        pszValQuoted);
+    if (pszValQuoted)
+        RTStrFree(pszValQuoted);
+    return rc;
+}
+
+static int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                              const char *pszKey, PCRTUUID pUuid)
+{
+    char *pszUuid;
+
+    RTStrAPrintf(&pszUuid, "\"%RTuuid\"", pUuid);
+    if (!pszUuid)
+        return VERR_NO_STR_MEMORY;
+    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                            pszUuid);
+    RTStrFree(pszUuid);
+    return rc;
+}
+
+static int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
+                             const char *pszKey, uint32_t uValue)
+{
+    char *pszValue;
+
+    RTStrAPrintf(&pszValue, "\"%d\"", uValue);
+    if (!pszValue)
+        return VERR_NO_STR_MEMORY;
+    int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
+                            pszValue);
+    RTStrFree(pszValue);
+    return rc;
+}
+
+static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData,
+                                    size_t cbDescData,
+                                    PVMDKDESCRIPTOR pDescriptor)
+{
+    int rc = VINF_SUCCESS;
+    unsigned cLine = 0, uLastNonEmptyLine = 0;
+    char *pTmp = pDescData;
+
+    pDescriptor->cbDescAlloc = cbDescData;
+    while (*pTmp != '\0')
+    {
+        pDescriptor->aLines[cLine++] = pTmp;
+        if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)
+        {
+            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+
+        while (*pTmp != '\0' && *pTmp != '\n')
+        {
+            if (*pTmp == '\r')
+            {
+                if (*(pTmp + 1) != '\n')
+                {
+                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor in '%s'"), pImage->pszFilename);
+                    goto out;
+                }
+                else
+                {
+                    /* Get rid of CR character. */
+                    *pTmp = '\0';
+                }
+            }
+            pTmp++;
+        }
+        /* Get rid of LF character. */
+        if (*pTmp == '\n')
+        {
+            *pTmp = '\0';
+            pTmp++;
+        }
+    }
+    pDescriptor->cLines = cLine;
+    /* Pointer right after the end of the used part of the buffer. */
+    pDescriptor->aLines[cLine] = pTmp;
+
+    if (    strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile")
+        &&  strcmp(pDescriptor->aLines[0], "# Disk Descriptor File"))
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Initialize those, because we need to be able to reopen an image. */
+    pDescriptor->uFirstDesc = 0;
+    pDescriptor->uFirstExtent = 0;
+    pDescriptor->uFirstDDB = 0;
+    for (unsigned i = 0; i < cLine; i++)
+    {
+        if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
+        {
+            if (    !strncmp(pDescriptor->aLines[i], "RW", 2)
+                ||  !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
+                ||  !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
+            {
+                /* An extent descriptor. */
+                if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
+                {
+                    /* Incorrect ordering of entries. */
+                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
+                    goto out;
+                }
+                if (!pDescriptor->uFirstExtent)
+                {
+                    pDescriptor->uFirstExtent = i;
+                    uLastNonEmptyLine = 0;
+                }
+            }
+            else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
+            {
+                /* A disk database entry. */
+                if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
+                {
+                    /* Incorrect ordering of entries. */
+                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
+                    goto out;
+                }
+                if (!pDescriptor->uFirstDDB)
+                {
+                    pDescriptor->uFirstDDB = i;
+                    uLastNonEmptyLine = 0;
+                }
+            }
+            else
+            {
+                /* A normal entry. */
+                if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
+                {
+                    /* Incorrect ordering of entries. */
+                    rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
+                    goto out;
+                }
+                if (!pDescriptor->uFirstDesc)
+                {
+                    pDescriptor->uFirstDesc = i;
+                    uLastNonEmptyLine = 0;
+                }
+            }
+            if (uLastNonEmptyLine)
+                pDescriptor->aNextLines[uLastNonEmptyLine] = i;
+            uLastNonEmptyLine = i;
+        }
+    }
+
+out:
+    return rc;
+}
+
+static int vmdkDescSetPCHSGeometry(PVMDKIMAGE pImage,
+                                   PCVDGEOMETRY pPCHSGeometry)
+{
+    int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_CYLINDERS,
+                           pPCHSGeometry->cCylinders);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_HEADS,
+                           pPCHSGeometry->cHeads);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_SECTORS,
+                           pPCHSGeometry->cSectors);
+    return rc;
+}
+
+static int vmdkDescSetLCHSGeometry(PVMDKIMAGE pImage,
+                                   PCVDGEOMETRY pLCHSGeometry)
+{
+    int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_CYLINDERS,
+                           pLCHSGeometry->cCylinders);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_HEADS,
+
+                           pLCHSGeometry->cHeads);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_SECTORS,
+                           pLCHSGeometry->cSectors);
+    return rc;
+}
+
+static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData,
+                                size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
+{
+    int rc;
+
+    pDescriptor->uFirstDesc = 0;
+    pDescriptor->uFirstExtent = 0;
+    pDescriptor->uFirstDDB = 0;
+    pDescriptor->cLines = 0;
+    pDescriptor->cbDescAlloc = cbDescData;
+    pDescriptor->fDirty = false;
+    pDescriptor->aLines[pDescriptor->cLines] = pDescData;
+    memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));
+
+    rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");
+    if (RT_FAILURE(rc))
+        goto out;
+    pDescriptor->uFirstDesc = pDescriptor->cLines - 1;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");
+    if (RT_FAILURE(rc))
+        goto out;
+    pDescriptor->uFirstExtent = pDescriptor->cLines - 1;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "");
+    if (RT_FAILURE(rc))
+        goto out;
+    /* The trailing space is created by VMware, too. */
+    rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "");
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");
+    if (RT_FAILURE(rc))
+        goto out;
+    pDescriptor->uFirstDDB = pDescriptor->cLines - 1;
+
+    /* Now that the framework is in place, use the normal functions to insert
+     * the remaining keys. */
+    char szBuf[9];
+    RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());
+    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
+                        "CID", szBuf);
+    if (RT_FAILURE(rc))
+        goto out;
+    rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
+                        "parentCID", "ffffffff");
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");
+    if (RT_FAILURE(rc))
+        goto out;
+
+out:
+    return rc;
+}
+
+static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData,
+                               size_t cbDescData)
+{
+    int rc;
+    unsigned cExtents;
+    unsigned uLine;
+    unsigned i;
+
+    rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData,
+                                  &pImage->Descriptor);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* Check version, must be 1. */
+    uint32_t uVersion;
+    rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);
+    if (uVersion != 1)
+        return vmdkError(pImage, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);
+
+    /* Get image creation type and determine image flags. */
+    const char *pszCreateType = NULL;   /* initialized to make gcc shut up */
+    rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",
+                            &pszCreateType);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);
+    if (    !strcmp(pszCreateType, "twoGbMaxExtentSparse")
+        ||  !strcmp(pszCreateType, "twoGbMaxExtentFlat"))
+        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
+    else if (   !strcmp(pszCreateType, "partitionedDevice")
+             || !strcmp(pszCreateType, "fullDevice"))
+        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_RAWDISK;
+    else if (!strcmp(pszCreateType, "streamOptimized"))
+        pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
+    else if (!strcmp(pszCreateType, "vmfs"))
+        pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_ESX;
+    RTStrFree((char *)(void *)pszCreateType);
+
+    /* Count the number of extent config entries. */
+    for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
+         uLine != 0;
+         uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
+        /* nothing */;
+
+    if (!pImage->pDescData && cExtents != 1)
+    {
+        /* Monolithic image, must have only one extent (already opened). */
+        return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent in '%s'"), pImage->pszFilename);
+    }
+
+    if (pImage->pDescData)
+    {
+        /* Non-monolithic image, extents need to be allocated. */
+        rc = vmdkCreateExtents(pImage, cExtents);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    for (i = 0, uLine = pImage->Descriptor.uFirstExtent;
+         i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
+    {
+        char *pszLine = pImage->Descriptor.aLines[uLine];
+
+        /* Access type of the extent. */
+        if (!strncmp(pszLine, "RW", 2))
+        {
+            pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
+            pszLine += 2;
+        }
+        else if (!strncmp(pszLine, "RDONLY", 6))
+        {
+            pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
+            pszLine += 6;
+        }
+        else if (!strncmp(pszLine, "NOACCESS", 8))
+        {
+            pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
+            pszLine += 8;
+        }
+        else
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+        if (*pszLine++ != ' ')
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+
+        /* Nominal size of the extent. */
+        rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
+                             &pImage->pExtents[i].cNominalSectors);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+        if (*pszLine++ != ' ')
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+
+        /* Type of the extent. */
+#ifdef VBOX_WITH_VMDK_ESX
+        /** @todo Add the ESX extent types. Not necessary for now because
+         * the ESX extent types are only used inside an ESX server. They are
+         * automatically converted if the VMDK is exported. */
+#endif /* VBOX_WITH_VMDK_ESX */
+        if (!strncmp(pszLine, "SPARSE", 6))
+        {
+            pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
+            pszLine += 6;
+        }
+        else if (!strncmp(pszLine, "FLAT", 4))
+        {
+            pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
+            pszLine += 4;
+        }
+        else if (!strncmp(pszLine, "ZERO", 4))
+        {
+            pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
+            pszLine += 4;
+        }
+        else if (!strncmp(pszLine, "VMFS", 4))
+        {
+            pImage->pExtents[i].enmType = VMDKETYPE_VMFS;
+            pszLine += 4;
+        }
+        else
+            return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+
+        if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
+        {
+            /* This one has no basename or offset. */
+            if (*pszLine == ' ')
+                pszLine++;
+            if (*pszLine != '\0')
+                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+            pImage->pExtents[i].pszBasename = NULL;
+        }
+        else
+        {
+            /* All other extent types have basename and optional offset. */
+            if (*pszLine++ != ' ')
+                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+
+            /* Basename of the image. Surrounded by quotes. */
+            char *pszBasename;
+            rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);
+            if (RT_FAILURE(rc))
+                return rc;
+            pImage->pExtents[i].pszBasename = pszBasename;
+            if (*pszLine == ' ')
+            {
+                pszLine++;
+                if (*pszLine != '\0')
+                {
+                    /* Optional offset in extent specified. */
+                    rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
+                                         &pImage->pExtents[i].uSectorOffset);
+                    if (RT_FAILURE(rc))
+                        return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+                }
+            }
+
+            if (*pszLine != '\0')
+                return vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
+        }
+    }
+
+    /* Determine PCHS geometry (autogenerate if necessary). */
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_CYLINDERS,
+                           &pImage->PCHSGeometry.cCylinders);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->PCHSGeometry.cCylinders = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_HEADS,
+                           &pImage->PCHSGeometry.cHeads);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->PCHSGeometry.cHeads = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_PCHS_SECTORS,
+                           &pImage->PCHSGeometry.cSectors);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->PCHSGeometry.cSectors = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    if (    pImage->PCHSGeometry.cCylinders == 0
+        ||  pImage->PCHSGeometry.cHeads == 0
+        ||  pImage->PCHSGeometry.cHeads > 16
+        ||  pImage->PCHSGeometry.cSectors == 0
+        ||  pImage->PCHSGeometry.cSectors > 63)
+    {
+        /* Mark PCHS geometry as not yet valid (can't do the calculation here
+         * as the total image size isn't known yet). */
+        pImage->PCHSGeometry.cCylinders = 0;
+        pImage->PCHSGeometry.cHeads = 16;
+        pImage->PCHSGeometry.cSectors = 63;
+    }
+
+    /* Determine LCHS geometry (set to 0 if not specified). */
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_CYLINDERS,
+                           &pImage->LCHSGeometry.cCylinders);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->LCHSGeometry.cCylinders = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_HEADS,
+                           &pImage->LCHSGeometry.cHeads);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->LCHSGeometry.cHeads = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
+                           VMDK_DDB_GEO_LCHS_SECTORS,
+                           &pImage->LCHSGeometry.cSectors);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+        pImage->LCHSGeometry.cSectors = 0;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
+    if (    pImage->LCHSGeometry.cCylinders == 0
+        ||  pImage->LCHSGeometry.cHeads == 0
+        ||  pImage->LCHSGeometry.cSectors == 0)
+    {
+        pImage->LCHSGeometry.cCylinders = 0;
+        pImage->LCHSGeometry.cHeads = 0;
+        pImage->LCHSGeometry.cSectors = 0;
+    }
+
+    /* Get image UUID. */
+    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID,
+                            &pImage->ImageUuid);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+    {
+        /* Image without UUID. Probably created by VMware and not yet used
+         * by VirtualBox. Can only be added for images opened in read/write
+         * mode, so don't bother producing a sensible UUID otherwise. */
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            RTUuidClear(&pImage->ImageUuid);
+        else
+        {
+            rc = RTUuidCreate(&pImage->ImageUuid);
+            if (RT_FAILURE(rc))
+                return rc;
+            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                    VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
+        }
+    }
+    else if (RT_FAILURE(rc))
+        return rc;
+
+    /* Get image modification UUID. */
+    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_MODIFICATION_UUID,
+                            &pImage->ModificationUuid);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+    {
+        /* Image without UUID. Probably created by VMware and not yet used
+         * by VirtualBox. Can only be added for images opened in read/write
+         * mode, so don't bother producing a sensible UUID otherwise. */
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            RTUuidClear(&pImage->ModificationUuid);
+        else
+        {
+            rc = RTUuidCreate(&pImage->ModificationUuid);
+            if (RT_FAILURE(rc))
+                return rc;
+            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                    VMDK_DDB_MODIFICATION_UUID,
+                                    &pImage->ModificationUuid);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);
+        }
+    }
+    else if (RT_FAILURE(rc))
+        return rc;
+
+    /* Get UUID of parent image. */
+    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID,
+                            &pImage->ParentUuid);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+    {
+        /* Image without UUID. Probably created by VMware and not yet used
+         * by VirtualBox. Can only be added for images opened in read/write
+         * mode, so don't bother producing a sensible UUID otherwise. */
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            RTUuidClear(&pImage->ParentUuid);
+        else
+        {
+            rc = RTUuidClear(&pImage->ParentUuid);
+            if (RT_FAILURE(rc))
+                return rc;
+            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                    VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);
+        }
+    }
+    else if (RT_FAILURE(rc))
+        return rc;
+
+    /* Get parent image modification UUID. */
+    rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_PARENT_MODIFICATION_UUID,
+                            &pImage->ParentModificationUuid);
+    if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+    {
+        /* Image without UUID. Probably created by VMware and not yet used
+         * by VirtualBox. Can only be added for images opened in read/write
+         * mode, so don't bother producing a sensible UUID otherwise. */
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            RTUuidClear(&pImage->ParentModificationUuid);
+        else
+        {
+            rc = RTUuidCreate(&pImage->ParentModificationUuid);
+            if (RT_FAILURE(rc))
+                return rc;
+            rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                    VMDK_DDB_PARENT_MODIFICATION_UUID,
+                                    &pImage->ParentModificationUuid);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in descriptor in '%s'"), pImage->pszFilename);
+        }
+    }
+    else if (RT_FAILURE(rc))
+        return rc;
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal : Prepares the descriptor to write to the image.
+ */
+static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
+                                 void **ppvData, size_t *pcbData)
+{
+    int rc = VINF_SUCCESS;
+
+    /*
+     * Allocate temporary descriptor buffer.
+     * In case there is no limit allocate a default
+     * and increase if required.
+     */
+    size_t cbDescriptor = cbLimit ? cbLimit : 4 * _1K;
+    char *pszDescriptor = (char *)RTMemAllocZ(cbDescriptor);
+    unsigned offDescriptor = 0;
+
+    if (!pszDescriptor)
+        return VERR_NO_MEMORY;
+
+    for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
+    {
+        const char *psz = pImage->Descriptor.aLines[i];
+        size_t cb = strlen(psz);
+
+        /*
+         * Increase the descriptor if there is no limit and
+         * there is not enough room left for this line.
+         */
+        if (offDescriptor + cb + 1 > cbDescriptor)
+        {
+            if (cbLimit)
+            {
+                rc = vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);
+                break;
+            }
+            else
+            {
+                char *pszDescriptorNew = NULL;
+                LogFlow(("Increasing descriptor cache\n"));
+
+                pszDescriptorNew = (char *)RTMemRealloc(pszDescriptor, cbDescriptor + cb + 4 * _1K);
+                if (!pszDescriptorNew)
+                {
+                    RTMemFree(pszDescriptor);
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+                pszDescriptor = pszDescriptorNew;
+                cbDescriptor += cb + 4 * _1K;
+            }
+        }
+
+        if (cb > 0)
+        {
+            memcpy(pszDescriptor + offDescriptor, psz, cb);
+            offDescriptor += cb;
+        }
+
+        memcpy(pszDescriptor + offDescriptor, "\n", 1);
+        offDescriptor++;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        *ppvData = pszDescriptor;
+        *pcbData = offDescriptor;
+    }
+
+    return rc;
+}
+
+/**
+ * Internal: write/update the descriptor part of the image.
+ */
+static int vmdkWriteDescriptor(PVMDKIMAGE pImage)
+{
+    int rc = VINF_SUCCESS;
+    uint64_t cbLimit;
+    uint64_t uOffset;
+    PVMDKFILE pDescFile;
+    void *pvDescriptor;
+    size_t cbDescriptor;
+
+    if (pImage->pDescData)
+    {
+        /* Separate descriptor file. */
+        uOffset = 0;
+        cbLimit = 0;
+        pDescFile = pImage->pFile;
+    }
+    else
+    {
+        /* Embedded descriptor file. */
+        uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
+        cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
+        pDescFile = pImage->pExtents[0].pFile;
+    }
+    /* Bail out if there is no file to write to. */
+    if (pDescFile == NULL)
+        return VERR_INVALID_PARAMETER;
+
+    rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
+    if (RT_SUCCESS(rc))
+    {
+        rc = vmdkFileWriteSync(pImage, pDescFile, uOffset, pvDescriptor, cbLimit ? cbLimit : cbDescriptor, NULL);
+        if (RT_FAILURE(rc))
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
+    }
+
+    if (RT_SUCCESS(rc) && !cbLimit)
+    {
+        rc = vmdkFileSetSize(pImage, pDescFile, cbDescriptor);
+        if (RT_FAILURE(rc))
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
+    }
+
+    if (RT_SUCCESS(rc))
+        pImage->Descriptor.fDirty = false;
+
+    RTMemFree(pvDescriptor);
+    return rc;
+}
+
+/**
+ * Internal: write/update the descriptor part of the image - async version.
+ */
+static int vmdkWriteDescriptorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
+{
+    int rc = VINF_SUCCESS;
+    uint64_t cbLimit;
+    uint64_t uOffset;
+    PVMDKFILE pDescFile;
+    void *pvDescriptor;
+    size_t cbDescriptor;
+
+    if (pImage->pDescData)
+    {
+        /* Separate descriptor file. */
+        uOffset = 0;
+        cbLimit = 0;
+        pDescFile = pImage->pFile;
+    }
+    else
+    {
+        /* Embedded descriptor file. */
+        uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
+        cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
+        pDescFile = pImage->pExtents[0].pFile;
+    }
+    /* Bail out if there is no file to write to. */
+    if (pDescFile == NULL)
+        return VERR_INVALID_PARAMETER;
+
+    rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
+    if (RT_SUCCESS(rc))
+    {
+        rc = vmdkFileWriteMetaAsync(pImage, pDescFile, uOffset, pvDescriptor, cbLimit ? cbLimit : cbDescriptor, pIoCtx, NULL, NULL);
+        if (   RT_FAILURE(rc)
+            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
+    }
+
+    if (RT_SUCCESS(rc) && !cbLimit)
+    {
+        rc = vmdkFileSetSize(pImage, pDescFile, cbDescriptor);
+        if (RT_FAILURE(rc))
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
+    }
+
+    if (RT_SUCCESS(rc))
+        pImage->Descriptor.fDirty = false;
+
+    RTMemFree(pvDescriptor);
+    return rc;
+
+}
+
+/**
+ * Internal: validate the consistency check values in a binary header.
+ */
+static int vmdkValidateHeader(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, const SparseExtentHeader *pHeader)
+{
+    int rc = VINF_SUCCESS;
+    if (RT_LE2H_U32(pHeader->magicNumber) != VMDK_SPARSE_MAGICNUMBER)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic in sparse extent header in '%s'"), pExtent->pszFullname);
+        return rc;
+    }
+    if (RT_LE2H_U32(pHeader->version) != 1 && RT_LE2H_U32(pHeader->version) != 3)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: incorrect version in sparse extent header in '%s', not a VMDK 1.0/1.1 conforming file"), pExtent->pszFullname);
+        return rc;
+    }
+    if (    (RT_LE2H_U32(pHeader->flags) & 1)
+        &&  (   pHeader->singleEndLineChar != '\n'
+             || pHeader->nonEndLineChar != ' '
+             || pHeader->doubleEndLineChar1 != '\r'
+             || pHeader->doubleEndLineChar2 != '\n') )
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);
+        return rc;
+    }
+    return rc;
+}
+
+/**
+ * Internal: read metadata belonging to an extent with binary header, i.e.
+ * as found in monolithic files.
+ */
+static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                    bool fMagicAlreadyRead)
+{
+    SparseExtentHeader Header;
+    uint64_t cSectorsPerGDE;
+    uint64_t cbFile = 0;
+    int rc;
+
+    if (!fMagicAlreadyRead)
+        rc = vmdkFileReadSync(pImage, pExtent->pFile, 0, &Header,
+                              sizeof(Header), NULL);
+    else
+    {
+        Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              RT_OFFSETOF(SparseExtentHeader, version),
+                              &Header.version,
+                                sizeof(Header)
+                              - RT_OFFSETOF(SparseExtentHeader, version),
+                              NULL);
+    }
+    AssertRC(rc);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+    rc = vmdkValidateHeader(pImage, pExtent, &Header);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    if (    RT_LE2H_U32(Header.flags & RT_BIT(17))
+        &&  RT_LE2H_U64(Header.gdOffset) == VMDK_GD_AT_END)
+        pExtent->fFooter = true;
+
+    if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        || (   pExtent->fFooter
+            && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbFile);
+        AssertRC(rc);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname);
+            goto out;
+        }
+    }
+
+    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        pExtent->uAppendPosition = RT_ALIGN_64(cbFile, 512);
+
+    if (   pExtent->fFooter
+        && (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        /* Read the footer, which comes before the end-of-stream marker. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              cbFile - 2*512, &Header,
+                              sizeof(Header), NULL);
+        AssertRC(rc);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent footer in '%s'"), pExtent->pszFullname);
+            goto out;
+        }
+        rc = vmdkValidateHeader(pImage, pExtent, &Header);
+        if (RT_FAILURE(rc))
+            goto out;
+        /* Prohibit any writes to this extent. */
+        pExtent->uAppendPosition = 0;
+    }
+
+    pExtent->uVersion = RT_LE2H_U32(Header.version);
+    pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; /* Just dummy value, changed later. */
+    pExtent->cSectors = RT_LE2H_U64(Header.capacity);
+    pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
+    pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
+    pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
+    if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+    pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
+    if (RT_LE2H_U32(Header.flags) & RT_BIT(1))
+    {
+        pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
+        pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
+    }
+    else
+    {
+        pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
+        pExtent->uSectorRGD = 0;
+    }
+    if (   (   pExtent->uSectorGD == VMDK_GD_AT_END
+            || pExtent->uSectorRGD == VMDK_GD_AT_END)
+        && (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot resolve grain directory offset in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+    pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);
+    pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
+    pExtent->uCompression = RT_LE2H_U16(Header.compressAlgorithm);
+    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
+    if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+    pExtent->cSectorsPerGDE = cSectorsPerGDE;
+    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
+
+    /* Fix up the number of descriptor sectors, as some flat images have
+     * really just one, and this causes failures when inserting the UUID
+     * values and other extra information. */
+    if (pExtent->cDescriptorSectors != 0 && pExtent->cDescriptorSectors < 4)
+    {
+        /* Do it the easy way - just fix it for flat images which have no
+         * other complicated metadata which needs space too. */
+        if (    pExtent->uDescriptorSector + 4 < pExtent->cOverheadSectors
+            &&  pExtent->cGTEntries * pExtent->cGDEntries == 0)
+            pExtent->cDescriptorSectors = 4;
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeExtentData(pImage, pExtent, false);
+
+    return rc;
+}
+
+/**
+ * Internal: read additional metadata belonging to an extent. For those
+ * extents which have no additional metadata just verify the information.
+ */
+static int vmdkReadMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
+{
+    int rc = VINF_SUCCESS;
+
+/* disabled the check as there are too many truncated vmdk images out there */
+#ifdef VBOX_WITH_VMDK_STRICT_SIZE_CHECK
+    uint64_t cbExtentSize;
+    /* The image must be a multiple of a sector in size and contain the data
+     * area (flat images only). If not, it means the image is at least
+     * truncated, or even seriously garbled. */
+    rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+    if (    cbExtentSize != RT_ALIGN_64(cbExtentSize, 512)
+        &&  (pExtent->enmType != VMDKETYPE_FLAT || pExtent->cNominalSectors + pExtent->uSectorOffset > VMDK_BYTE2SECTOR(cbExtentSize)))
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: file size is not a multiple of 512 in '%s', file is truncated or otherwise garbled"), pExtent->pszFullname);
+        goto out;
+    }
+#endif /* VBOX_WITH_VMDK_STRICT_SIZE_CHECK */
+    if (pExtent->enmType != VMDKETYPE_HOSTED_SPARSE)
+        goto out;
+
+    /* The spec says that this must be a power of two and greater than 8,
+     * but probably they meant not less than 8. */
+    if (    (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
+        ||  pExtent->cSectorsPerGrain < 8)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
+        goto out;
+    }
+
+    /* This code requires that a grain table must hold a power of two multiple
+     * of the number of entries per GT cache entry. */
+    if (    (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
+        ||  pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
+    {
+        rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);
+        goto out;
+    }
+
+    rc = vmdkAllocStreamBuffers(pImage, pExtent);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Prohibit any writes to this streamOptimized extent. */
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        pExtent->uAppendPosition = 0;
+
+    if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
+        rc = vmdkReadGrainDirectory(pImage, pExtent);
+    else
+    {
+        pExtent->uGrainSectorAbs = pExtent->cOverheadSectors;
+        pExtent->cbGrainStreamRead = 0;
+    }
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeExtentData(pImage, pExtent, false);
+
+    return rc;
+}
+
+/**
+ * Internal: write/update the metadata for a sparse extent.
+ */
+static int vmdkWriteMetaSparseExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                     uint64_t uOffset)
+{
+    SparseExtentHeader Header;
+
+    memset(&Header, '\0', sizeof(Header));
+    Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
+    Header.version = RT_H2LE_U32(pExtent->uVersion);
+    Header.flags = RT_H2LE_U32(RT_BIT(0));
+    if (pExtent->pRGD)
+        Header.flags |= RT_H2LE_U32(RT_BIT(1));
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
+    Header.capacity = RT_H2LE_U64(pExtent->cSectors);
+    Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
+    Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
+    Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
+    Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
+    if (pExtent->fFooter && uOffset == 0)
+    {
+        if (pExtent->pRGD)
+        {
+            Assert(pExtent->uSectorRGD);
+            Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+        }
+        else
+        {
+            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+        }
+    }
+    else
+    {
+        if (pExtent->pRGD)
+        {
+            Assert(pExtent->uSectorRGD);
+            Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
+            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
+        }
+        else
+        {
+            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
+        }
+    }
+    Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
+    Header.uncleanShutdown = pExtent->fUncleanShutdown;
+    Header.singleEndLineChar = '\n';
+    Header.nonEndLineChar = ' ';
+    Header.doubleEndLineChar1 = '\r';
+    Header.doubleEndLineChar2 = '\n';
+    Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
+
+    int rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOffset, &Header, sizeof(Header), NULL);
+    AssertRC(rc);
+    if (RT_FAILURE(rc))
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
+    return rc;
+}
+
+/**
+ * Internal: write/update the metadata for a sparse extent - async version.
+ */
+static int vmdkWriteMetaSparseExtentAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                          uint64_t uOffset, PVDIOCTX pIoCtx)
+{
+    SparseExtentHeader Header;
+
+    memset(&Header, '\0', sizeof(Header));
+    Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
+    Header.version = RT_H2LE_U32(pExtent->uVersion);
+    Header.flags = RT_H2LE_U32(RT_BIT(0));
+    if (pExtent->pRGD)
+        Header.flags |= RT_H2LE_U32(RT_BIT(1));
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
+    Header.capacity = RT_H2LE_U64(pExtent->cSectors);
+    Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
+    Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
+    Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
+    Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
+    if (pExtent->fFooter && uOffset == 0)
+    {
+        if (pExtent->pRGD)
+        {
+            Assert(pExtent->uSectorRGD);
+            Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+        }
+        else
+        {
+            Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
+        }
+    }
+    else
+    {
+        if (pExtent->pRGD)
+        {
+            Assert(pExtent->uSectorRGD);
+            Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
+            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
+        }
+        else
+        {
+            Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
+        }
+    }
+    Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
+    Header.uncleanShutdown = pExtent->fUncleanShutdown;
+    Header.singleEndLineChar = '\n';
+    Header.nonEndLineChar = ' ';
+    Header.doubleEndLineChar1 = '\r';
+    Header.doubleEndLineChar2 = '\n';
+    Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
+
+    int rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                    uOffset, &Header, sizeof(Header),
+                                    pIoCtx, NULL, NULL);
+    if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
+    return rc;
+}
+
+#ifdef VBOX_WITH_VMDK_ESX
+/**
+ * Internal: unused code to read the metadata of a sparse ESX extent.
+ *
+ * Such extents never leave ESX server, so this isn't ever used.
+ */
+static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
+{
+    COWDisk_Header Header;
+    uint64_t cSectorsPerGDE;
+
+    int rc = vmdkFileReadSync(pImage, pExtent->pFile, 0, &Header, sizeof(Header), NULL);
+    AssertRC(rc);
+    if (RT_FAILURE(rc))
+        goto out;
+    if (    RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER
+        ||  RT_LE2H_U32(Header.version) != 1
+        ||  RT_LE2H_U32(Header.flags) != 3)
+    {
+        rc = VERR_VD_VMDK_INVALID_HEADER;
+        goto out;
+    }
+    pExtent->enmType = VMDKETYPE_ESX_SPARSE;
+    pExtent->cSectors = RT_LE2H_U32(Header.numSectors);
+    pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);
+    /* The spec says that this must be between 1 sector and 1MB. This code
+     * assumes it's a power of two, so check that requirement, too. */
+    if (    (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
+        ||  pExtent->cSectorsPerGrain == 0
+        ||  pExtent->cSectorsPerGrain > 2048)
+    {
+        rc = VERR_VD_VMDK_INVALID_HEADER;
+        goto out;
+    }
+    pExtent->uDescriptorSector = 0;
+    pExtent->cDescriptorSectors = 0;
+    pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);
+    pExtent->uSectorRGD = 0;
+    pExtent->cOverheadSectors = 0;
+    pExtent->cGTEntries = 4096;
+    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
+    if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
+    {
+        rc = VERR_VD_VMDK_INVALID_HEADER;
+        goto out;
+    }
+    pExtent->cSectorsPerGDE = cSectorsPerGDE;
+    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
+    if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))
+    {
+        /* Inconsistency detected. Computed number of GD entries doesn't match
+         * stored value. Better be safe than sorry. */
+        rc = VERR_VD_VMDK_INVALID_HEADER;
+        goto out;
+    }
+    pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);
+    pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
+
+    rc = vmdkReadGrainDirectory(pImage, pExtent);
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeExtentData(pImage, pExtent, false);
+
+    return rc;
+}
+#endif /* VBOX_WITH_VMDK_ESX */
+
+/**
+ * Internal: free the buffers used for streamOptimized images.
+ */
+static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent)
+{
+    if (pExtent->pvCompGrain)
+    {
+        RTMemFree(pExtent->pvCompGrain);
+        pExtent->pvCompGrain = NULL;
+    }
+    if (pExtent->pvGrain)
+    {
+        RTMemFree(pExtent->pvGrain);
+        pExtent->pvGrain = NULL;
+    }
+}
+
+/**
+ * Internal: free the memory used by the extent data structure, optionally
+ * deleting the referenced files.
+ */
+static void vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                               bool fDelete)
+{
+    vmdkFreeGrainDirectory(pExtent);
+    if (pExtent->pDescData)
+    {
+        RTMemFree(pExtent->pDescData);
+        pExtent->pDescData = NULL;
+    }
+    if (pExtent->pFile != NULL)
+    {
+        /* Do not delete raw extents, these have full and base names equal. */
+        vmdkFileClose(pImage, &pExtent->pFile,
+                         fDelete
+                      && pExtent->pszFullname
+                      && strcmp(pExtent->pszFullname, pExtent->pszBasename));
+    }
+    if (pExtent->pszBasename)
+    {
+        RTMemTmpFree((void *)pExtent->pszBasename);
+        pExtent->pszBasename = NULL;
+    }
+    if (pExtent->pszFullname)
+    {
+        RTStrFree((char *)(void *)pExtent->pszFullname);
+        pExtent->pszFullname = NULL;
+    }
+    vmdkFreeStreamBuffers(pExtent);
+}
+
+/**
+ * Internal: allocate grain table cache if necessary for this image.
+ */
+static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)
+{
+    PVMDKEXTENT pExtent;
+
+    /* Allocate grain table cache if any sparse extent is present. */
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (    pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
+#ifdef VBOX_WITH_VMDK_ESX
+            ||  pExtent->enmType == VMDKETYPE_ESX_SPARSE
+#endif /* VBOX_WITH_VMDK_ESX */
+           )
+        {
+            /* Allocate grain table cache. */
+            pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
+            if (!pImage->pGTCache)
+                return VERR_NO_MEMORY;
+            for (unsigned j = 0; j < VMDK_GT_CACHE_SIZE; j++)
+            {
+                PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[j];
+                pGCE->uExtent = UINT32_MAX;
+            }
+            pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
+            break;
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal: allocate the given number of extents.
+ */
+static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
+    if (pExtents)
+    {
+        for (unsigned i = 0; i < cExtents; i++)
+        {
+            pExtents[i].pFile = NULL;
+            pExtents[i].pszBasename = NULL;
+            pExtents[i].pszFullname = NULL;
+            pExtents[i].pGD = NULL;
+            pExtents[i].pRGD = NULL;
+            pExtents[i].pDescData = NULL;
+            pExtents[i].uVersion = 1;
+            pExtents[i].uCompression = VMDK_COMPRESSION_NONE;
+            pExtents[i].uExtent = i;
+            pExtents[i].pImage = pImage;
+        }
+        pImage->pExtents = pExtents;
+        pImage->cExtents = cExtents;
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Internal: Open an image, constructing all necessary data structures.
+ */
+static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
+{
+    int rc;
+    uint32_t u32Magic;
+    PVMDKFILE pFile;
+    PVMDKEXTENT pExtent;
+
+    pImage->uOpenFlags = uOpenFlags;
+
+    /* Try to get error interface. */
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    /*
+     * Open the image.
+     * We don't have to check for asynchronous access because
+     * we only support raw access and the opened file is a description
+     * file were no data is stored.
+     */
+
+    rc = vmdkFileOpen(pImage, &pFile, pImage->pszFilename,
+                      VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
+                      false /* fAsyncIO */);
+    if (RT_FAILURE(rc))
+    {
+        /* Do NOT signal an appropriate error here, as the VD layer has the
+         * choice of retrying the open if it failed. */
+        goto out;
+    }
+    pImage->pFile = pFile;
+
+    /* Read magic (if present). */
+    rc = vmdkFileReadSync(pImage, pFile, 0, &u32Magic, sizeof(u32Magic), NULL);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    /* Handle the file according to its magic number. */
+    if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
+    {
+        /* It's a hosted single-extent image. */
+        rc = vmdkCreateExtents(pImage, 1);
+        if (RT_FAILURE(rc))
+            goto out;
+        /* The opened file is passed to the extent. No separate descriptor
+         * file, so no need to keep anything open for the image. */
+        pExtent = &pImage->pExtents[0];
+        pExtent->pFile = pFile;
+        pImage->pFile = NULL;
+        pExtent->pszFullname = RTPathAbsDup(pImage->pszFilename);
+        if (!pExtent->pszFullname)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+        rc = vmdkReadBinaryMetaExtent(pImage, pExtent, true /* fMagicAlreadyRead */);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        /* As we're dealing with a monolithic image here, there must
+         * be a descriptor embedded in the image file. */
+        if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)
+        {
+            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+        /* HACK: extend the descriptor if it is unusually small and it fits in
+         * the unused space after the image header. Allows opening VMDK files
+         * with extremely small descriptor in read/write mode. */
+        if (    !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            &&  pExtent->cDescriptorSectors < 3
+            &&  (int64_t)pExtent->uSectorGD - pExtent->uDescriptorSector >= 4
+            &&  (!pExtent->uSectorRGD || (int64_t)pExtent->uSectorRGD - pExtent->uDescriptorSector >= 4))
+        {
+            pExtent->cDescriptorSectors = 4;
+            pExtent->fMetaDirty = true;
+        }
+        /* Read the descriptor from the extent. */
+        pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
+        if (!pExtent->pDescData)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
+                              pExtent->pDescData,
+                              VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
+        AssertRC(rc);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname);
+            goto out;
+        }
+
+        rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
+                                 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
+        if (RT_FAILURE(rc))
+            goto out;
+
+        rc = vmdkReadMetaExtent(pImage, pExtent);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        /* Mark the extent as unclean if opened in read-write mode. */
+        if (   !(uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+        {
+            pExtent->fUncleanShutdown = true;
+            pExtent->fMetaDirty = true;
+        }
+    }
+    else
+    {
+        /* Allocate at least 10K, and make sure that there is 5K free space
+         * in case new entries need to be added to the descriptor. Never
+         * allocate more than 128K, because that's no valid descriptor file
+         * and will result in the correct "truncated read" error handling. */
+        uint64_t cbFileSize;
+        rc = vmdkFileGetSize(pImage, pFile, &cbFileSize);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        uint64_t cbSize = cbFileSize;
+        if (cbSize % VMDK_SECTOR2BYTE(10))
+            cbSize += VMDK_SECTOR2BYTE(20) - cbSize % VMDK_SECTOR2BYTE(10);
+        else
+            cbSize += VMDK_SECTOR2BYTE(10);
+        cbSize = RT_MIN(cbSize, _128K);
+        pImage->cbDescAlloc = RT_MAX(VMDK_SECTOR2BYTE(20), cbSize);
+        pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
+        if (!pImage->pDescData)
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+
+        size_t cbRead;
+        rc = vmdkFileReadSync(pImage, pImage->pFile, 0, pImage->pDescData,
+                              RT_MIN(pImage->cbDescAlloc, cbFileSize),
+                              &cbRead);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+        if (cbRead == pImage->cbDescAlloc)
+        {
+            /* Likely the read is truncated. Better fail a bit too early
+             * (normally the descriptor is much smaller than our buffer). */
+            rc = vmdkError(pImage, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+
+        rc = vmdkParseDescriptor(pImage, pImage->pDescData,
+                                 pImage->cbDescAlloc);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        /*
+         * We have to check for the asynchronous open flag. The
+         * extents are parsed and the type of all are known now.
+         * Check if every extent is either FLAT or ZERO.
+         */
+        if (uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)
+        {
+            unsigned cFlatExtents = 0;
+
+            for (unsigned i = 0; i < pImage->cExtents; i++)
+            {
+                pExtent = &pImage->pExtents[i];
+
+                if ((    pExtent->enmType != VMDKETYPE_FLAT
+                     &&  pExtent->enmType != VMDKETYPE_ZERO
+                     &&  pExtent->enmType != VMDKETYPE_VMFS)
+                    || ((pImage->pExtents[i].enmType == VMDKETYPE_FLAT) && (cFlatExtents > 0)))
+                {
+                    /*
+                     * Opened image contains at least one none flat or zero extent.
+                     * Return error but don't set error message as the caller
+                     * has the chance to open in non async I/O mode.
+                     */
+                    rc = VERR_NOT_SUPPORTED;
+                    goto out;
+                }
+                if (pExtent->enmType == VMDKETYPE_FLAT)
+                    cFlatExtents++;
+            }
+        }
+
+        for (unsigned i = 0; i < pImage->cExtents; i++)
+        {
+            pExtent = &pImage->pExtents[i];
+
+            if (pExtent->pszBasename)
+            {
+                /* Hack to figure out whether the specified name in the
+                 * extent descriptor is absolute. Doesn't always work, but
+                 * should be good enough for now. */
+                char *pszFullname;
+                /** @todo implement proper path absolute check. */
+                if (pExtent->pszBasename[0] == RTPATH_SLASH)
+                {
+                    pszFullname = RTStrDup(pExtent->pszBasename);
+                    if (!pszFullname)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        goto out;
+                    }
+                }
+                else
+                {
+                    char *pszDirname = RTStrDup(pImage->pszFilename);
+                    if (!pszDirname)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        goto out;
+                    }
+                    RTPathStripFilename(pszDirname);
+                    pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
+                    RTStrFree(pszDirname);
+                    if (!pszFullname)
+                    {
+                        rc = VERR_NO_STR_MEMORY;
+                        goto out;
+                    }
+                }
+                pExtent->pszFullname = pszFullname;
+            }
+            else
+                pExtent->pszFullname = NULL;
+
+            switch (pExtent->enmType)
+            {
+                case VMDKETYPE_HOSTED_SPARSE:
+                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                                      VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                                 false /* fCreate */),
+                                      false /* fAsyncIO */);
+                    if (RT_FAILURE(rc))
+                    {
+                        /* Do NOT signal an appropriate error here, as the VD
+                         * layer has the choice of retrying the open if it
+                         * failed. */
+                        goto out;
+                    }
+                    rc = vmdkReadBinaryMetaExtent(pImage, pExtent,
+                                                  false /* fMagicAlreadyRead */);
+                    if (RT_FAILURE(rc))
+                        goto out;
+                    rc = vmdkReadMetaExtent(pImage, pExtent);
+                    if (RT_FAILURE(rc))
+                        goto out;
+
+                    /* Mark extent as unclean if opened in read-write mode. */
+                    if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
+                    {
+                        pExtent->fUncleanShutdown = true;
+                        pExtent->fMetaDirty = true;
+                    }
+                    break;
+                case VMDKETYPE_VMFS:
+                case VMDKETYPE_FLAT:
+                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                                      VDOpenFlagsToFileOpenFlags(uOpenFlags,
+                                                                 false /* fCreate */),
+                                      true /* fAsyncIO */);
+                    if (RT_FAILURE(rc))
+                    {
+                        /* Do NOT signal an appropriate error here, as the VD
+                         * layer has the choice of retrying the open if it
+                         * failed. */
+                        goto out;
+                    }
+                    break;
+                case VMDKETYPE_ZERO:
+                    /* Nothing to do. */
+                    break;
+                default:
+                    AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
+            }
+        }
+    }
+
+    /* Make sure this is not reached accidentally with an error status. */
+    AssertRC(rc);
+
+    /* Determine PCHS geometry if not set. */
+    if (pImage->PCHSGeometry.cCylinders == 0)
+    {
+        uint64_t cCylinders =   VMDK_BYTE2SECTOR(pImage->cbSize)
+                              / pImage->PCHSGeometry.cHeads
+                              / pImage->PCHSGeometry.cSectors;
+        pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383);
+        if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+            && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+        {
+            rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry);
+            AssertRC(rc);
+        }
+    }
+
+    /* Update the image metadata now in case has changed. */
+    rc = vmdkFlushImage(pImage);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Figure out a few per-image constants from the extents. */
+    pImage->cbSize = 0;
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (    pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
+#ifdef VBOX_WITH_VMDK_ESX
+            ||  pExtent->enmType == VMDKETYPE_ESX_SPARSE
+#endif /* VBOX_WITH_VMDK_ESX */
+           )
+        {
+            /* Here used to be a check whether the nominal size of an extent
+             * is a multiple of the grain size. The spec says that this is
+             * always the case, but unfortunately some files out there in the
+             * wild violate the spec (e.g. ReactOS 0.3.1). */
+        }
+        pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
+    }
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (    pImage->pExtents[i].enmType == VMDKETYPE_FLAT
+            ||  pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
+        {
+            pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+            break;
+        }
+    }
+
+    if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
+        rc = vmdkAllocateGrainTableCache(pImage);
+
+out:
+    if (RT_FAILURE(rc))
+        vmdkFreeImage(pImage, false);
+    return rc;
+}
+
+/**
+ * Internal: create VMDK images for raw disk/partition access.
+ */
+static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
+                              uint64_t cbSize)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKEXTENT pExtent;
+
+    if (pRaw->fRawDisk)
+    {
+        /* Full raw disk access. This requires setting up a descriptor
+         * file and open the (flat) raw disk. */
+        rc = vmdkCreateExtents(pImage, 1);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
+        pExtent = &pImage->pExtents[0];
+        /* Create raw disk descriptor file. */
+        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
+                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                     true /* fCreate */),
+                          false /* fAsyncIO */);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
+
+        /* Set up basename for extent description. Cannot use StrDup. */
+        size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
+        char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
+        if (!pszBasename)
+            return VERR_NO_MEMORY;
+        memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
+        pExtent->pszBasename = pszBasename;
+        /* For raw disks the full name is identical to the base name. */
+        pExtent->pszFullname = RTStrDup(pszBasename);
+        if (!pExtent->pszFullname)
+            return VERR_NO_MEMORY;
+        pExtent->enmType = VMDKETYPE_FLAT;
+        pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
+        pExtent->uSectorOffset = 0;
+        pExtent->enmAccess = VMDKACCESS_READWRITE;
+        pExtent->fMetaDirty = false;
+
+        /* Open flat image, the raw disk. */
+        rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                                     false /* fCreate */),
+                          false /* fAsyncIO */);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
+    }
+    else
+    {
+        /* Raw partition access. This requires setting up a descriptor
+         * file, write the partition information to a flat extent and
+         * open all the (flat) raw disk partitions. */
+
+        /* First pass over the partition data areas to determine how many
+         * extents we need. One data area can require up to 2 extents, as
+         * it might be necessary to skip over unpartitioned space. */
+        unsigned cExtents = 0;
+        uint64_t uStart = 0;
+        for (unsigned i = 0; i < pRaw->cPartDescs; i++)
+        {
+            PVBOXHDDRAWPARTDESC pPart = &pRaw->pPartDescs[i];
+            if (uStart > pPart->uStart)
+                return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: incorrect partition data area ordering set up by the caller in '%s'"), pImage->pszFilename);
+
+            if (uStart < pPart->uStart)
+                cExtents++;
+            uStart = pPart->uStart + pPart->cbData;
+            cExtents++;
+        }
+        /* Another extent for filling up the rest of the image. */
+        if (uStart != cbSize)
+            cExtents++;
+
+        rc = vmdkCreateExtents(pImage, cExtents);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
+
+        /* Create raw partition descriptor file. */
+        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
+                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                     true /* fCreate */),
+                          false /* fAsyncIO */);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
+
+        /* Create base filename for the partition table extent. */
+        /** @todo remove fixed buffer without creating memory leaks. */
+        char pszPartition[1024];
+        const char *pszBase = RTPathFilename(pImage->pszFilename);
+        const char *pszExt = RTPathExt(pszBase);
+        if (pszExt == NULL)
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename);
+        char *pszBaseBase = RTStrDup(pszBase);
+        if (!pszBaseBase)
+            return VERR_NO_MEMORY;
+        RTPathStripExt(pszBaseBase);
+        RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s",
+                    pszBaseBase, pszExt);
+        RTStrFree(pszBaseBase);
+
+        /* Second pass over the partitions, now define all extents. */
+        uint64_t uPartOffset = 0;
+        cExtents = 0;
+        uStart = 0;
+        for (unsigned i = 0; i < pRaw->cPartDescs; i++)
+        {
+            PVBOXHDDRAWPARTDESC pPart = &pRaw->pPartDescs[i];
+            pExtent = &pImage->pExtents[cExtents++];
+
+            if (uStart < pPart->uStart)
+            {
+                pExtent->pszBasename = NULL;
+                pExtent->pszFullname = NULL;
+                pExtent->enmType = VMDKETYPE_ZERO;
+                pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uStart - uStart);
+                pExtent->uSectorOffset = 0;
+                pExtent->enmAccess = VMDKACCESS_READWRITE;
+                pExtent->fMetaDirty = false;
+                /* go to next extent */
+                pExtent = &pImage->pExtents[cExtents++];
+            }
+            uStart = pPart->uStart + pPart->cbData;
+
+            if (pPart->pvPartitionData)
+            {
+                /* Set up basename for extent description. Can't use StrDup. */
+                size_t cbBasename = strlen(pszPartition) + 1;
+                char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
+                if (!pszBasename)
+                    return VERR_NO_MEMORY;
+                memcpy(pszBasename, pszPartition, cbBasename);
+                pExtent->pszBasename = pszBasename;
+
+                /* Set up full name for partition extent. */
+                char *pszDirname = RTStrDup(pImage->pszFilename);
+                if (!pszDirname)
+                    return VERR_NO_STR_MEMORY;
+                RTPathStripFilename(pszDirname);
+                char *pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
+                RTStrFree(pszDirname);
+                if (!pszDirname)
+                    return VERR_NO_STR_MEMORY;
+                pExtent->pszFullname = pszFullname;
+                pExtent->enmType = VMDKETYPE_FLAT;
+                pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
+                pExtent->uSectorOffset = uPartOffset;
+                pExtent->enmAccess = VMDKACCESS_READWRITE;
+                pExtent->fMetaDirty = false;
+
+                /* Create partition table flat image. */
+                rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                                  VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                             true /* fCreate */),
+                                  false /* fAsyncIO */);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                       VMDK_SECTOR2BYTE(uPartOffset),
+                                       pPart->pvPartitionData,
+                                       pPart->cbData, NULL);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
+                uPartOffset += VMDK_BYTE2SECTOR(pPart->cbData);
+            }
+            else
+            {
+                if (pPart->pszRawDevice)
+                {
+                    /* Set up basename for extent descr. Can't use StrDup. */
+                    size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
+                    char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
+                    if (!pszBasename)
+                        return VERR_NO_MEMORY;
+                    memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
+                    pExtent->pszBasename = pszBasename;
+                    /* For raw disks full name is identical to base name. */
+                    pExtent->pszFullname = RTStrDup(pszBasename);
+                    if (!pExtent->pszFullname)
+                        return VERR_NO_MEMORY;
+                    pExtent->enmType = VMDKETYPE_FLAT;
+                    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
+                    pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uStartOffset);
+                    pExtent->enmAccess = VMDKACCESS_READWRITE;
+                    pExtent->fMetaDirty = false;
+
+                    /* Open flat image, the raw partition. */
+                    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                                      VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
+                                                                 false /* fCreate */),
+                                      false /* fAsyncIO */);
+                    if (RT_FAILURE(rc))
+                        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
+                }
+                else
+                {
+                    pExtent->pszBasename = NULL;
+                    pExtent->pszFullname = NULL;
+                    pExtent->enmType = VMDKETYPE_ZERO;
+                    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
+                    pExtent->uSectorOffset = 0;
+                    pExtent->enmAccess = VMDKACCESS_READWRITE;
+                    pExtent->fMetaDirty = false;
+                }
+            }
+        }
+        /* Another extent for filling up the rest of the image. */
+        if (uStart != cbSize)
+        {
+            pExtent = &pImage->pExtents[cExtents++];
+            pExtent->pszBasename = NULL;
+            pExtent->pszFullname = NULL;
+            pExtent->enmType = VMDKETYPE_ZERO;
+            pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
+            pExtent->uSectorOffset = 0;
+            pExtent->enmAccess = VMDKACCESS_READWRITE;
+            pExtent->fMetaDirty = false;
+        }
+    }
+
+    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
+                            pRaw->fRawDisk ?
+                            "fullDevice" : "partitionedDevice");
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
+    return rc;
+}
+
+/**
+ * Internal: create a regular (i.e. file-backed) VMDK image.
+ */
+static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
+                                  unsigned uImageFlags,
+                                  PFNVDPROGRESS pfnProgress, void *pvUser,
+                                  unsigned uPercentStart, unsigned uPercentSpan)
+{
+    int rc = VINF_SUCCESS;
+    unsigned cExtents = 1;
+    uint64_t cbOffset = 0;
+    uint64_t cbRemaining = cbSize;
+
+    if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
+    {
+        cExtents = cbSize / VMDK_2G_SPLIT_SIZE;
+        /* Do proper extent computation: need one smaller extent if the total
+         * size isn't evenly divisible by the split size. */
+        if (cbSize % VMDK_2G_SPLIT_SIZE)
+            cExtents++;
+    }
+    rc = vmdkCreateExtents(pImage, cExtents);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
+
+    /* Basename strings needed for constructing the extent names. */
+    char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
+    AssertPtr(pszBasenameSubstr);
+    size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
+
+    /* Create separate descriptor file if necessary. */
+    if (cExtents != 1 || (uImageFlags & VD_IMAGE_FLAGS_FIXED))
+    {
+        rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
+                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                     true /* fCreate */),
+                          false /* fAsyncIO */);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
+    }
+    else
+        pImage->pFile = NULL;
+
+    /* Set up all extents. */
+    for (unsigned i = 0; i < cExtents; i++)
+    {
+        PVMDKEXTENT pExtent = &pImage->pExtents[i];
+        uint64_t cbExtent = cbRemaining;
+
+        /* Set up fullname/basename for extent description. Cannot use StrDup
+         * for basename, as it is not guaranteed that the memory can be freed
+         * with RTMemTmpFree, which must be used as in other code paths
+         * StrDup is not usable. */
+        if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        {
+            char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
+            if (!pszBasename)
+                return VERR_NO_MEMORY;
+            memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
+            pExtent->pszBasename = pszBasename;
+        }
+        else
+        {
+            char *pszBasenameExt = RTPathExt(pszBasenameSubstr);
+            char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
+            RTPathStripExt(pszBasenameBase);
+            char *pszTmp;
+            size_t cbTmp;
+            if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+            {
+                if (cExtents == 1)
+                    RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase,
+                                 pszBasenameExt);
+                else
+                    RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
+                                 i+1, pszBasenameExt);
+            }
+            else
+                RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1,
+                             pszBasenameExt);
+            RTStrFree(pszBasenameBase);
+            if (!pszTmp)
+                return VERR_NO_STR_MEMORY;
+            cbTmp = strlen(pszTmp) + 1;
+            char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
+            if (!pszBasename)
+                return VERR_NO_MEMORY;
+            memcpy(pszBasename, pszTmp, cbTmp);
+            RTStrFree(pszTmp);
+            pExtent->pszBasename = pszBasename;
+            if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
+                cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE);
+        }
+        char *pszBasedirectory = RTStrDup(pImage->pszFilename);
+        if (!pszBasedirectory)
+            return VERR_NO_STR_MEMORY;
+        RTPathStripFilename(pszBasedirectory);
+        char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
+        RTStrFree(pszBasedirectory);
+        if (!pszFullname)
+            return VERR_NO_STR_MEMORY;
+        pExtent->pszFullname = pszFullname;
+
+        /* Create file for extent. */
+        rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                          VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                     true /* fCreate */),
+                          false /* fAsyncIO */);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
+        if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        {
+            rc = vmdkFileSetSize(pImage, pExtent->pFile, cbExtent);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
+
+            /* Fill image with zeroes. We do this for every fixed-size image since on some systems
+             * (for example Windows Vista), it takes ages to write a block near the end of a sparse
+             * file and the guest could complain about an ATA timeout. */
+
+            /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
+             *        Currently supported file systems are ext4 and ocfs2. */
+
+            /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
+            const size_t cbBuf = 128 * _1K;
+            void *pvBuf = RTMemTmpAllocZ(cbBuf);
+            if (!pvBuf)
+                return VERR_NO_MEMORY;
+
+            uint64_t uOff = 0;
+            /* Write data to all image blocks. */
+            while (uOff < cbExtent)
+            {
+                unsigned cbChunk = (unsigned)RT_MIN(cbExtent, cbBuf);
+
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uOff, pvBuf, cbChunk, NULL);
+                if (RT_FAILURE(rc))
+                {
+                    RTMemFree(pvBuf);
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: writing block failed for '%s'"), pImage->pszFilename);
+                }
+
+                uOff += cbChunk;
+
+                if (pfnProgress)
+                {
+                    rc = pfnProgress(pvUser,
+                                     uPercentStart + uOff * uPercentSpan / cbExtent);
+                    if (RT_FAILURE(rc))
+                    {
+                        RTMemFree(pvBuf);
+                        return rc;
+                    }
+                }
+            }
+            RTMemTmpFree(pvBuf);
+        }
+
+        /* Place descriptor file information (where integrated). */
+        if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        {
+            pExtent->uDescriptorSector = 1;
+            pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
+            /* The descriptor is part of the (only) extent. */
+            pExtent->pDescData = pImage->pDescData;
+            pImage->pDescData = NULL;
+        }
+
+        if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        {
+            uint64_t cSectorsPerGDE, cSectorsPerGD;
+            pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
+            pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, _64K));
+            pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
+            pExtent->cGTEntries = 512;
+            cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
+            pExtent->cSectorsPerGDE = cSectorsPerGDE;
+            pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
+            cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
+            if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+            {
+                /* The spec says version is 1 for all VMDKs, but the vast
+                 * majority of streamOptimized VMDKs actually contain
+                 * version 3 - so go with the majority. Both are accepted. */
+                pExtent->uVersion = 3;
+                pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
+            }
+        }
+        else
+        {
+            if (uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
+                pExtent->enmType = VMDKETYPE_VMFS;
+            else
+                pExtent->enmType = VMDKETYPE_FLAT;
+        }
+
+        pExtent->enmAccess = VMDKACCESS_READWRITE;
+        pExtent->fUncleanShutdown = true;
+        pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent);
+        pExtent->uSectorOffset = 0;
+        pExtent->fMetaDirty = true;
+
+        if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+        {
+            /* fPreAlloc should never be false because VMware can't use such images. */
+            rc = vmdkCreateGrainDirectory(pImage, pExtent,
+                                          RT_MAX(  pExtent->uDescriptorSector
+                                                 + pExtent->cDescriptorSectors,
+                                                 1),
+                                          true /* fPreAlloc */);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
+        }
+
+        if (RT_SUCCESS(rc) && pfnProgress)
+            pfnProgress(pvUser, uPercentStart + i * uPercentSpan / cExtents);
+
+        cbRemaining -= cbExtent;
+        cbOffset += cbExtent;
+    }
+
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
+    {
+        /* VirtualBox doesn't care, but VMWare ESX freaks out if the wrong
+         * controller type is set in an image. */
+        rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, "ddb.adapterType", "lsilogic");
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set controller type to lsilogic in '%s'"), pImage->pszFilename);
+    }
+
+    const char *pszDescType = NULL;
+    if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+    {
+        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
+            pszDescType = "vmfs";
+        else
+            pszDescType =   (cExtents == 1)
+                          ? "monolithicFlat" : "twoGbMaxExtentFlat";
+    }
+    else
+    {
+        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+            pszDescType = "streamOptimized";
+        else
+        {
+            pszDescType =   (cExtents == 1)
+                          ? "monolithicSparse" : "twoGbMaxExtentSparse";
+        }
+    }
+    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
+                            pszDescType);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
+    return rc;
+}
+
+/**
+ * Internal: Create a real stream optimized VMDK using only linear writes.
+ */
+static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize,
+                                 unsigned uImageFlags,
+                                 PFNVDPROGRESS pfnProgress, void *pvUser,
+                                 unsigned uPercentStart, unsigned uPercentSpan)
+{
+    int rc;
+
+    rc = vmdkCreateExtents(pImage, 1);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
+
+    /* Basename strings needed for constructing the extent names. */
+    const char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
+    AssertPtr(pszBasenameSubstr);
+    size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
+
+    /* No separate descriptor file. */
+    pImage->pFile = NULL;
+
+    /* Set up all extents. */
+    PVMDKEXTENT pExtent = &pImage->pExtents[0];
+
+    /* Set up fullname/basename for extent description. Cannot use StrDup
+     * for basename, as it is not guaranteed that the memory can be freed
+     * with RTMemTmpFree, which must be used as in other code paths
+     * StrDup is not usable. */
+    char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
+    if (!pszBasename)
+        return VERR_NO_MEMORY;
+    memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
+    pExtent->pszBasename = pszBasename;
+
+    char *pszBasedirectory = RTStrDup(pImage->pszFilename);
+    RTPathStripFilename(pszBasedirectory);
+    char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
+    RTStrFree(pszBasedirectory);
+    if (!pszFullname)
+        return VERR_NO_STR_MEMORY;
+    pExtent->pszFullname = pszFullname;
+
+    /* Create file for extent. Make it write only, no reading allowed. */
+    rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
+                        VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
+                                                   true /* fCreate */)
+                      & ~RTFILE_O_READ,
+                      false /* fAsyncIO */);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
+
+    /* Place descriptor file information. */
+    pExtent->uDescriptorSector = 1;
+    pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
+    /* The descriptor is part of the (only) extent. */
+    pExtent->pDescData = pImage->pDescData;
+    pImage->pDescData = NULL;
+
+    uint64_t cSectorsPerGDE, cSectorsPerGD;
+    pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
+    pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));
+    pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
+    pExtent->cGTEntries = 512;
+    cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
+    pExtent->cSectorsPerGDE = cSectorsPerGDE;
+    pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
+    cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
+
+    /* The spec says version is 1 for all VMDKs, but the vast
+     * majority of streamOptimized VMDKs actually contain
+     * version 3 - so go with the majority. Both are accepted. */
+    pExtent->uVersion = 3;
+    pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
+    pExtent->fFooter = true;
+
+    pExtent->enmAccess = VMDKACCESS_READONLY;
+    pExtent->fUncleanShutdown = false;
+    pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
+    pExtent->uSectorOffset = 0;
+    pExtent->fMetaDirty = true;
+
+    /* Create grain directory, without preallocating it straight away. It will
+     * be constructed on the fly when writing out the data and written when
+     * closing the image. The end effect is that the full grain directory is
+     * allocated, which is a requirement of the VMDK specs. */
+    rc = vmdkCreateGrainDirectory(pImage, pExtent, VMDK_GD_AT_END,
+                                  false /* fPreAlloc */);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
+
+    rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
+                            "streamOptimized");
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
+
+    return rc;
+}
+
+/**
+ * Internal: The actual code for creating any VMDK variant currently in
+ * existence on hosted environments.
+ */
+static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
+                           unsigned uImageFlags, const char *pszComment,
+                           PCVDGEOMETRY pPCHSGeometry,
+                           PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
+                           PFNVDPROGRESS pfnProgress, void *pvUser,
+                           unsigned uPercentStart, unsigned uPercentSpan)
+{
+    int rc;
+
+    pImage->uImageFlags = uImageFlags;
+
+    /* Try to get error interface. */
+    pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
+    if (pImage->pInterfaceError)
+        pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
+
+    /* Get I/O interface. */
+    pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
+    AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
+    pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
+    AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
+
+    rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,
+                              &pImage->Descriptor);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    if (    (uImageFlags & VD_IMAGE_FLAGS_FIXED)
+        &&  (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
+    {
+        /* Raw disk image (includes raw partition). */
+        const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment;
+        /* As the comment is misused, zap it so that no garbage comment
+         * is set below. */
+        pszComment = NULL;
+        rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
+    }
+    else
+    {
+        if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        {
+            /* Stream optimized sparse image (monolithic). */
+            rc = vmdkCreateStreamImage(pImage, cbSize, uImageFlags,
+                                       pfnProgress, pvUser, uPercentStart,
+                                       uPercentSpan * 95 / 100);
+        }
+        else
+        {
+            /* Regular fixed or sparse image (monolithic or split). */
+            rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags,
+                                        pfnProgress, pvUser, uPercentStart,
+                                        uPercentSpan * 95 / 100);
+        }
+    }
+
+    if (RT_FAILURE(rc))
+        goto out;
+
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
+
+    pImage->cbSize = cbSize;
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        PVMDKEXTENT pExtent = &pImage->pExtents[i];
+
+        rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
+                               pExtent->cNominalSectors, pExtent->enmType,
+                               pExtent->pszBasename, pExtent->uSectorOffset);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+    }
+    vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
+
+    if (    pPCHSGeometry->cCylinders != 0
+        &&  pPCHSGeometry->cHeads != 0
+        &&  pPCHSGeometry->cSectors != 0)
+    {
+        rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
+        if (RT_FAILURE(rc))
+            goto out;
+    }
+    if (    pLCHSGeometry->cCylinders != 0
+        &&  pLCHSGeometry->cHeads != 0
+        &&  pLCHSGeometry->cSectors != 0)
+    {
+        rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
+        if (RT_FAILURE(rc))
+            goto out;
+    }
+
+    pImage->LCHSGeometry = *pLCHSGeometry;
+    pImage->PCHSGeometry = *pPCHSGeometry;
+
+    pImage->ImageUuid = *pUuid;
+    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    RTUuidClear(&pImage->ParentUuid);
+    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    RTUuidClear(&pImage->ModificationUuid);
+    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_MODIFICATION_UUID,
+                            &pImage->ModificationUuid);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+    RTUuidClear(&pImage->ParentModificationUuid);
+    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                            VMDK_DDB_PARENT_MODIFICATION_UUID,
+                            &pImage->ParentModificationUuid);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    rc = vmdkAllocateGrainTableCache(pImage);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    rc = vmdkSetImageComment(pImage, pszComment);
+    if (RT_FAILURE(rc))
+    {
+        rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);
+        goto out;
+    }
+
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan * 99 / 100);
+
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        /* streamOptimized is a bit special, we cannot trigger the flush
+         * until all data has been written. So we write the necessary
+         * information explicitly. */
+        pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(  pImage->Descriptor.aLines[pImage->Descriptor.cLines]
+                                                                              - pImage->Descriptor.aLines[0], 512));
+        rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+
+        rc = vmdkWriteDescriptor(pImage);
+        if (RT_FAILURE(rc))
+        {
+            rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);
+            goto out;
+        }
+    }
+    else
+        rc = vmdkFlushImage(pImage);
+
+out:
+    if (RT_SUCCESS(rc) && pfnProgress)
+        pfnProgress(pvUser, uPercentStart + uPercentSpan);
+
+    if (RT_FAILURE(rc))
+        vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
+    return rc;
+}
+
+/**
+ * Internal: Update image comment.
+ */
+static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
+{
+    char *pszCommentEncoded;
+    if (pszComment)
+    {
+        pszCommentEncoded = vmdkEncodeString(pszComment);
+        if (!pszCommentEncoded)
+            return VERR_NO_MEMORY;
+    }
+    else
+        pszCommentEncoded = NULL;
+    int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
+                               "ddb.comment", pszCommentEncoded);
+    if (pszComment)
+        RTStrFree(pszCommentEncoded);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal. Clear the grain table buffer for real stream optimized writing.
+ */
+static void vmdkStreamClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
+{
+    uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
+    for (uint32_t i = 0; i < cCacheLines; i++)
+        memset(&pImage->pGTCache->aGTCache[i].aGTData[0], '\0',
+               VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
+}
+
+/**
+ * Internal. Flush the grain table buffer for real stream optimized writing.
+ */
+static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                             uint32_t uGDEntry)
+{
+    int rc = VINF_SUCCESS;
+    uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
+
+    /* VMware does not write out completely empty grain tables in the case
+     * of streamOptimized images, which according to my interpretation of
+     * the VMDK 1.1 spec is bending the rules. Since they do it and we can
+     * handle it without problems do it the same way and save some bytes. */
+    bool fAllZero = true;
+    for (uint32_t i = 0; i < cCacheLines; i++)
+    {
+        /* Convert the grain table to little endian in place, as it will not
+         * be used at all after this function has been called. */
+        uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
+        for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
+            if (*pGTTmp)
+            {
+                fAllZero = false;
+                break;
+            }
+        if (!fAllZero)
+            break;
+    }
+    if (fAllZero)
+        return VINF_SUCCESS;
+
+    uint64_t uFileOffset = pExtent->uAppendPosition;
+    if (!uFileOffset)
+        return VERR_INTERNAL_ERROR;
+    /* Align to sector, as the previous write could have been any size. */
+    uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+
+    /* Grain table marker. */
+    uint8_t aMarker[512];
+    PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
+    memset(pMarker, '\0', sizeof(aMarker));
+    pMarker->uSector = RT_H2LE_U64(VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t)));
+    pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT);
+    rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                           aMarker, sizeof(aMarker), NULL);
+    AssertRC(rc);
+    uFileOffset += 512;
+
+    if (!pExtent->pGD || pExtent->pGD[uGDEntry])
+        return VERR_INTERNAL_ERROR;
+
+    pExtent->pGD[uGDEntry] = VMDK_BYTE2SECTOR(uFileOffset);
+
+    for (uint32_t i = 0; i < cCacheLines; i++)
+    {
+        /* Convert the grain table to little endian in place, as it will not
+         * be used at all after this function has been called. */
+        uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
+        for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
+            *pGTTmp = RT_H2LE_U32(*pGTTmp);
+
+        rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                               &pImage->pGTCache->aGTCache[i].aGTData[0],
+                               VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t),
+                               NULL);
+        uFileOffset += VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t);
+        if (RT_FAILURE(rc))
+            break;
+    }
+    Assert(!(uFileOffset % 512));
+    pExtent->uAppendPosition = RT_ALIGN_64(uFileOffset, 512);
+    return rc;
+}
+
+/**
+ * Internal. Free all allocated space for representing an image, and optionally
+ * delete the image from disk.
+ */
+static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
+{
+    int rc = VINF_SUCCESS;
+
+    /* Freeing a never allocated image (e.g. because the open failed) is
+     * not signalled as an error. After all nothing bad happens. */
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+            {
+                /* Check if all extents are clean. */
+                for (unsigned i = 0; i < pImage->cExtents; i++)
+                {
+                    Assert(!pImage->pExtents[i].fUncleanShutdown);
+                }
+            }
+            else
+            {
+                /* Mark all extents as clean. */
+                for (unsigned i = 0; i < pImage->cExtents; i++)
+                {
+                    if (   (   pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
+#ifdef VBOX_WITH_VMDK_ESX
+                            || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
+#endif /* VBOX_WITH_VMDK_ESX */
+                           )
+                        && pImage->pExtents[i].fUncleanShutdown)
+                    {
+                        pImage->pExtents[i].fUncleanShutdown = false;
+                        pImage->pExtents[i].fMetaDirty = true;
+                    }
+
+                    /* From now on it's not safe to append any more data. */
+                    pImage->pExtents[i].uAppendPosition = 0;
+                }
+            }
+        }
+
+        if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        {
+            /* No need to write any pending data if the file will be deleted
+             * or if the new file wasn't successfully created. */
+            if (   !fDelete && pImage->pExtents
+                && pImage->pExtents[0].cGTEntries
+                && pImage->pExtents[0].uAppendPosition)
+            {
+                PVMDKEXTENT pExtent = &pImage->pExtents[0];
+                uint32_t uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
+                if (uLastGDEntry != pExtent->cGDEntries - 1)
+                {
+                    rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
+                    AssertRC(rc);
+                    vmdkStreamClearGT(pImage, pExtent);
+                    for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++)
+                    {
+                        rc = vmdkStreamFlushGT(pImage, pExtent, i);
+                        AssertRC(rc);
+                    }
+                }
+
+                uint64_t uFileOffset = pExtent->uAppendPosition;
+                if (!uFileOffset)
+                    return VERR_INTERNAL_ERROR;
+                uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+
+                /* From now on it's not safe to append any more data. */
+                pExtent->uAppendPosition = 0;
+
+                /* Grain directory marker. */
+                uint8_t aMarker[512];
+                PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
+                memset(pMarker, '\0', sizeof(aMarker));
+                pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64(pExtent->cGDEntries * sizeof(uint32_t)), 512));
+                pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                                       aMarker, sizeof(aMarker), NULL);
+                AssertRC(rc);
+                uFileOffset += 512;
+
+                /* Write grain directory in little endian style. The array will
+                 * not be used after this, so convert in place. */
+                uint32_t *pGDTmp = pExtent->pGD;
+                for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++)
+                    *pGDTmp = RT_H2LE_U32(*pGDTmp);
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                                       pExtent->pGD,
+                                       pExtent->cGDEntries * sizeof(uint32_t),
+                                       NULL);
+                AssertRC(rc);
+
+                pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);
+                pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset);
+                uFileOffset = RT_ALIGN_64(  uFileOffset
+                                          + pExtent->cGDEntries * sizeof(uint32_t),
+                                          512);
+
+                /* Footer marker. */
+                memset(pMarker, '\0', sizeof(aMarker));
+                pMarker->uSector = VMDK_BYTE2SECTOR(512);
+                pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                                       aMarker, sizeof(aMarker), NULL);
+                AssertRC(rc);
+
+                uFileOffset += 512;
+                rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
+                AssertRC(rc);
+
+                uFileOffset += 512;
+                /* End-of-stream marker. */
+                memset(pMarker, '\0', sizeof(aMarker));
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                                       aMarker, sizeof(aMarker), NULL);
+                AssertRC(rc);
+            }
+        }
+        else
+            vmdkFlushImage(pImage);
+
+        if (pImage->pExtents != NULL)
+        {
+            for (unsigned i = 0 ; i < pImage->cExtents; i++)
+                vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
+            RTMemFree(pImage->pExtents);
+            pImage->pExtents = NULL;
+        }
+        pImage->cExtents = 0;
+        if (pImage->pFile != NULL)
+            vmdkFileClose(pImage, &pImage->pFile, fDelete);
+        vmdkFileCheckAllClose(pImage);
+
+        if (pImage->pGTCache)
+        {
+            RTMemFree(pImage->pGTCache);
+            pImage->pGTCache = NULL;
+        }
+        if (pImage->pDescData)
+        {
+            RTMemFree(pImage->pDescData);
+            pImage->pDescData = NULL;
+        }
+    }
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Internal. Flush image data (and metadata) to disk.
+ */
+static int vmdkFlushImage(PVMDKIMAGE pImage)
+{
+    PVMDKEXTENT pExtent;
+    int rc = VINF_SUCCESS;
+
+    /* Update descriptor if changed. */
+    if (pImage->Descriptor.fDirty)
+    {
+        rc = vmdkWriteDescriptor(pImage);
+        if (RT_FAILURE(rc))
+            goto out;
+    }
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
+        {
+            switch (pExtent->enmType)
+            {
+                case VMDKETYPE_HOSTED_SPARSE:
+                    if (!pExtent->fFooter)
+                    {
+                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0);
+                        if (RT_FAILURE(rc))
+                            goto out;
+                    }
+                    else
+                    {
+                        uint64_t uFileOffset = pExtent->uAppendPosition;
+                        /* Simply skip writing anything if the streamOptimized
+                         * image hasn't been just created. */
+                        if (!uFileOffset)
+                            break;
+                        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent,
+                                                       uFileOffset);
+                        if (RT_FAILURE(rc))
+                            goto out;
+                    }
+                    break;
+#ifdef VBOX_WITH_VMDK_ESX
+                case VMDKETYPE_ESX_SPARSE:
+                    /** @todo update the header. */
+                    break;
+#endif /* VBOX_WITH_VMDK_ESX */
+                case VMDKETYPE_VMFS:
+                case VMDKETYPE_FLAT:
+                    /* Nothing to do. */
+                    break;
+                case VMDKETYPE_ZERO:
+                default:
+                    AssertMsgFailed(("extent with type %d marked as dirty\n",
+                                     pExtent->enmType));
+                    break;
+            }
+        }
+        switch (pExtent->enmType)
+        {
+            case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+            case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            case VMDKETYPE_VMFS:
+            case VMDKETYPE_FLAT:
+                /** @todo implement proper path absolute check. */
+                if (   pExtent->pFile != NULL
+                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                    && !(pExtent->pszBasename[0] == RTPATH_SLASH))
+                    rc = vmdkFileFlush(pImage, pExtent->pFile);
+                break;
+            case VMDKETYPE_ZERO:
+                /* No need to do anything for this extent. */
+                break;
+            default:
+                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
+                break;
+        }
+    }
+
+out:
+    return rc;
+}
+
+/**
+ * Internal. Flush image data (and metadata) to disk - async version.
+ */
+static int vmdkFlushImageAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
+{
+    PVMDKEXTENT pExtent;
+    int rc = VINF_SUCCESS;
+
+    /* Update descriptor if changed. */
+    if (pImage->Descriptor.fDirty)
+    {
+        rc = vmdkWriteDescriptorAsync(pImage, pIoCtx);
+        if (   RT_FAILURE(rc)
+            && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+            goto out;
+    }
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
+        {
+            switch (pExtent->enmType)
+            {
+                case VMDKETYPE_HOSTED_SPARSE:
+                    AssertMsgFailed(("Async I/O not supported for sparse images\n"));
+                    break;
+#ifdef VBOX_WITH_VMDK_ESX
+                case VMDKETYPE_ESX_SPARSE:
+                    /** @todo update the header. */
+                    break;
+#endif /* VBOX_WITH_VMDK_ESX */
+                case VMDKETYPE_VMFS:
+                case VMDKETYPE_FLAT:
+                    /* Nothing to do. */
+                    break;
+                case VMDKETYPE_ZERO:
+                default:
+                    AssertMsgFailed(("extent with type %d marked as dirty\n",
+                                     pExtent->enmType));
+                    break;
+            }
+        }
+        switch (pExtent->enmType)
+        {
+            case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+            case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            case VMDKETYPE_VMFS:
+            case VMDKETYPE_FLAT:
+                /** @todo implement proper path absolute check. */
+                if (   pExtent->pFile != NULL
+                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                    && !(pExtent->pszBasename[0] == RTPATH_SLASH))
+                    rc = vmdkFileFlushAsync(pImage, pExtent->pFile, pIoCtx);
+                break;
+            case VMDKETYPE_ZERO:
+                /* No need to do anything for this extent. */
+                break;
+            default:
+                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
+                break;
+        }
+    }
+
+out:
+    return rc;
+}
+
+/**
+ * Internal. Find extent corresponding to the sector number in the disk.
+ */
+static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector,
+                          PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
+{
+    PVMDKEXTENT pExtent = NULL;
+    int rc = VINF_SUCCESS;
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        if (offSector < pImage->pExtents[i].cNominalSectors)
+        {
+            pExtent = &pImage->pExtents[i];
+            *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
+            break;
+        }
+        offSector -= pImage->pExtents[i].cNominalSectors;
+    }
+
+    if (pExtent)
+        *ppExtent = pExtent;
+    else
+        rc = VERR_IO_SECTOR_NOT_FOUND;
+
+    return rc;
+}
+
+/**
+ * Internal. Hash function for placing the grain table hash entries.
+ */
+static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
+                                unsigned uExtent)
+{
+    /** @todo this hash function is quite simple, maybe use a better one which
+     * scrambles the bits better. */
+    return (uSector + uExtent) % pCache->cEntries;
+}
+
+/**
+ * Internal. Get sector number in the extent file from the relative sector
+ * number in the extent.
+ */
+static int vmdkGetSector(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                         uint64_t uSector, uint64_t *puExtentSector)
+{
+    PVMDKGTCACHE pCache = pImage->pGTCache;
+    uint64_t uGDIndex, uGTSector, uGTBlock;
+    uint32_t uGTHash, uGTBlockIndex;
+    PVMDKGTCACHEENTRY pGTCacheEntry;
+    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
+    int rc;
+
+    /* For newly created and readonly/sequentially opened streamOptimized
+     * images this must be a no-op, as the grain directory is not there. */
+    if (   (   pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
+            && pExtent->uAppendPosition)
+        || (   pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
+            && pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY
+            && pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
+    {
+        *puExtentSector = 0;
+        return VINF_SUCCESS;
+    }
+
+    uGDIndex = uSector / pExtent->cSectorsPerGDE;
+    if (uGDIndex >= pExtent->cGDEntries)
+        return VERR_OUT_OF_RANGE;
+    uGTSector = pExtent->pGD[uGDIndex];
+    if (!uGTSector)
+    {
+        /* There is no grain table referenced by this grain directory
+         * entry. So there is absolutely no data in this area. */
+        *puExtentSector = 0;
+        return VINF_SUCCESS;
+    }
+
+    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
+    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
+    pGTCacheEntry = &pCache->aGTCache[uGTHash];
+    if (    pGTCacheEntry->uExtent != pExtent->uExtent
+        ||  pGTCacheEntry->uGTBlock != uGTBlock)
+    {
+        /* Cache miss, fetch data from disk. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                              aGTDataTmp, sizeof(aGTDataTmp), NULL);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname);
+        pGTCacheEntry->uExtent = pExtent->uExtent;
+        pGTCacheEntry->uGTBlock = uGTBlock;
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
+    }
+    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
+    uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
+    if (uGrainSector)
+        *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
+    else
+        *puExtentSector = 0;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal. Get sector number in the extent file from the relative sector
+ * number in the extent - version for async access.
+ */
+static int vmdkGetSectorAsync(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
+                              PVMDKEXTENT pExtent, uint64_t uSector,
+                              uint64_t *puExtentSector)
+{
+    PVMDKGTCACHE pCache = pImage->pGTCache;
+    uint64_t uGDIndex, uGTSector, uGTBlock;
+    uint32_t uGTHash, uGTBlockIndex;
+    PVMDKGTCACHEENTRY pGTCacheEntry;
+    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
+    int rc;
+
+    uGDIndex = uSector / pExtent->cSectorsPerGDE;
+    if (uGDIndex >= pExtent->cGDEntries)
+        return VERR_OUT_OF_RANGE;
+    uGTSector = pExtent->pGD[uGDIndex];
+    if (!uGTSector)
+    {
+        /* There is no grain table referenced by this grain directory
+         * entry. So there is absolutely no data in this area. */
+        *puExtentSector = 0;
+        return VINF_SUCCESS;
+    }
+
+    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
+    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
+    pGTCacheEntry = &pCache->aGTCache[uGTHash];
+    if (    pGTCacheEntry->uExtent != pExtent->uExtent
+        ||  pGTCacheEntry->uGTBlock != uGTBlock)
+    {
+        /* Cache miss, fetch data from disk. */
+        PVDMETAXFER pMetaXfer;
+        rc = vmdkFileReadMetaAsync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                                   aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
+        if (RT_FAILURE(rc))
+            return rc;
+        /* We can release the metadata transfer immediately. */
+        vmdkFileMetaXferRelease(pImage, pMetaXfer);
+        pGTCacheEntry->uExtent = pExtent->uExtent;
+        pGTCacheEntry->uGTBlock = uGTBlock;
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
+    }
+    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
+    uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
+    if (uGrainSector)
+        *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
+    else
+        *puExtentSector = 0;
+    return VINF_SUCCESS;
+}
+
+/**
+ * Internal. Allocates a new grain table (if necessary), writes the grain
+ * and updates the grain table. The cache is also updated by this operation.
+ * This is separate from vmdkGetSector, because that should be as fast as
+ * possible. Most code from vmdkGetSector also appears here.
+ */
+static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                          uint64_t uSector, const void *pvBuf,
+                          uint64_t cbWrite)
+{
+    PVMDKGTCACHE pCache = pImage->pGTCache;
+    uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
+    uint64_t uFileOffset;
+    uint32_t uGTHash, uGTBlockIndex;
+    PVMDKGTCACHEENTRY pGTCacheEntry;
+    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
+    int rc;
+
+    uGDIndex = uSector / pExtent->cSectorsPerGDE;
+    if (uGDIndex >= pExtent->cGDEntries)
+        return VERR_OUT_OF_RANGE;
+    uGTSector = pExtent->pGD[uGDIndex];
+    if (pExtent->pRGD)
+        uRGTSector = pExtent->pRGD[uGDIndex];
+    else
+        uRGTSector = 0; /**< avoid compiler warning */
+    if (!uGTSector)
+    {
+        /* There is no grain table referenced by this grain directory
+         * entry. So there is absolutely no data in this area. Allocate
+         * a new grain table and put the reference to it in the GDs. */
+        uFileOffset = pExtent->uAppendPosition;
+        if (!uFileOffset)
+            return VERR_INTERNAL_ERROR;
+        Assert(!(uFileOffset % 512));
+        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+        uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
+
+        pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
+
+        /* Normally the grain table is preallocated for hosted sparse extents
+         * that support more than 32 bit sector numbers. So this shouldn't
+         * ever happen on a valid extent. */
+        if (uGTSector > UINT32_MAX)
+            return VERR_VD_VMDK_INVALID_HEADER;
+
+        /* Write grain table by writing the required number of grain table
+         * cache chunks. Avoids dynamic memory allocation, but is a bit
+         * slower. But as this is a pretty infrequently occurring case it
+         * should be acceptable. */
+        memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
+        for (unsigned i = 0;
+             i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
+             i++)
+        {
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp),
+                                   aGTDataTmp, sizeof(aGTDataTmp), NULL);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
+        }
+        pExtent->uAppendPosition = RT_ALIGN_64(  pExtent->uAppendPosition
+                                               + pExtent->cGTEntries * sizeof(uint32_t),
+                                               512);
+
+        if (pExtent->pRGD)
+        {
+            AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
+            uFileOffset = pExtent->uAppendPosition;
+            if (!uFileOffset)
+                return VERR_INTERNAL_ERROR;
+            Assert(!(uFileOffset % 512));
+            uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
+
+            pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t);
+
+            /* Normally the redundant grain table is preallocated for hosted
+             * sparse extents that support more than 32 bit sector numbers. So
+             * this shouldn't ever happen on a valid extent. */
+            if (uRGTSector > UINT32_MAX)
+                return VERR_VD_VMDK_INVALID_HEADER;
+
+            /* Write backup grain table by writing the required number of grain
+             * table cache chunks. Avoids dynamic memory allocation, but is a
+             * bit slower. But as this is a pretty infrequently occurring case
+             * it should be acceptable. */
+            for (unsigned i = 0;
+                 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
+                 i++)
+            {
+                rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                       VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp),
+                                       aGTDataTmp, sizeof(aGTDataTmp), NULL);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
+            }
+
+            pExtent->uAppendPosition =   pExtent->uAppendPosition
+                                       + pExtent->cGTEntries * sizeof(uint32_t);
+        }
+
+        /* Update the grain directory on disk (doing it before writing the
+         * grain table will result in a garbled extent if the operation is
+         * aborted for some reason. Otherwise the worst that can happen is
+         * some unused sectors in the extent. */
+        uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
+        rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                               VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
+                               &uGTSectorLE, sizeof(uGTSectorLE), NULL);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
+        if (pExtent->pRGD)
+        {
+            uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE),
+                                   &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
+            if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
+        }
+
+        /* As the final step update the in-memory copy of the GDs. */
+        pExtent->pGD[uGDIndex] = uGTSector;
+        if (pExtent->pRGD)
+            pExtent->pRGD[uGDIndex] = uRGTSector;
+    }
+
+    uFileOffset = pExtent->uAppendPosition;
+    if (!uFileOffset)
+        return VERR_INTERNAL_ERROR;
+    Assert(!(uFileOffset % 512));
+
+    /* Write the data. Always a full grain, or we're in big trouble. */
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
+            return vmdkError(pImage, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
+
+        /* Invalidate cache, just in case some code incorrectly allows mixing
+         * of reads and writes. Normally shouldn't be needed. */
+        pExtent->uGrainSectorAbs = 0;
+
+        /* Write compressed data block and the markers. */
+        uint32_t cbGrain = 0;
+        rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
+                                 pvBuf, cbWrite, uSector, &cbGrain);
+        if (RT_FAILURE(rc))
+        {
+            AssertRC(rc);
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
+        }
+        pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
+        pExtent->uAppendPosition += cbGrain;
+    }
+    else
+    {
+        rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,
+                               pvBuf, cbWrite, NULL);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
+        pExtent->uAppendPosition += cbWrite;
+    }
+
+    /* Update the grain table (and the cache). */
+    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
+    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
+    pGTCacheEntry = &pCache->aGTCache[uGTHash];
+    if (    pGTCacheEntry->uExtent != pExtent->uExtent
+        ||  pGTCacheEntry->uGTBlock != uGTBlock)
+    {
+        /* Cache miss, fetch data from disk. */
+        rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                              VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                              aGTDataTmp, sizeof(aGTDataTmp), NULL);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
+        pGTCacheEntry->uExtent = pExtent->uExtent;
+        pGTCacheEntry->uGTBlock = uGTBlock;
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
+    }
+    else
+    {
+        /* Cache hit. Convert grain table block back to disk format, otherwise
+         * the code below will write garbage for all but the updated entry. */
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
+    }
+    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
+    aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(uFileOffset));
+    pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(uFileOffset);
+    /* Update grain table on disk. */
+    rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                           VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                           aGTDataTmp, sizeof(aGTDataTmp), NULL);
+    if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
+    if (pExtent->pRGD)
+    {
+        /* Update backup grain table on disk. */
+        rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                               VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                               aGTDataTmp, sizeof(aGTDataTmp), NULL);
+        if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
+    }
+#ifdef VBOX_WITH_VMDK_ESX
+    if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
+    {
+        pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
+        pExtent->fMetaDirty = true;
+    }
+#endif /* VBOX_WITH_VMDK_ESX */
+    return rc;
+}
+
+/**
+ * Internal. Writes the grain and also if necessary the grain tables.
+ * Uses the grain table cache as a true grain table.
+ */
+static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                uint64_t uSector, const void *pvBuf,
+                                uint64_t cbWrite)
+{
+    uint32_t uGrain;
+    uint32_t uGDEntry, uLastGDEntry;
+    uint32_t cbGrain = 0;
+    uint32_t uCacheLine, uCacheEntry;
+    const void *pData = pvBuf;
+    int rc;
+
+    /* Very strict requirements: always write at least one full grain, with
+     * proper alignment. Everything else would require reading of already
+     * written data, which we don't support for obvious reasons. The only
+     * exception is the last grain, and only if the image size specifies
+     * that only some portion holds data. In any case the write must be
+     * within the image limits, no "overshoot" allowed. */
+    if (   cbWrite == 0
+        || (   cbWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
+            && pExtent->cNominalSectors - uSector >= pExtent->cSectorsPerGrain)
+        || uSector % pExtent->cSectorsPerGrain
+        || uSector + VMDK_BYTE2SECTOR(cbWrite) > pExtent->cNominalSectors)
+        return VERR_INVALID_PARAMETER;
+
+    /* Clip write range to at most the rest of the grain. */
+    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain));
+
+    /* Do not allow to go back. */
+    uGrain = uSector / pExtent->cSectorsPerGrain;
+    uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
+    uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE;
+    uGDEntry = uGrain / pExtent->cGTEntries;
+    uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
+    if (uGrain < pExtent->uLastGrainAccess)
+        return VERR_VD_VMDK_INVALID_WRITE;
+
+    /* Zero byte write optimization. Since we don't tell VBoxHDD that we need
+     * to allocate something, we also need to detect the situation ourself. */
+    if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
+        && ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbWrite * 8) == -1)
+        return VINF_SUCCESS;
+
+    if (uGDEntry != uLastGDEntry)
+    {
+        rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
+        if (RT_FAILURE(rc))
+            return rc;
+        vmdkStreamClearGT(pImage, pExtent);
+        for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++)
+        {
+            rc = vmdkStreamFlushGT(pImage, pExtent, i);
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+    }
+
+    uint64_t uFileOffset;
+    uFileOffset = pExtent->uAppendPosition;
+    if (!uFileOffset)
+        return VERR_INTERNAL_ERROR;
+    /* Align to sector, as the previous write could have been any size. */
+    uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+
+    /* Paranoia check: extent type, grain table buffer presence and
+     * grain table buffer space. Also grain table entry must be clear. */
+    if (   pExtent->enmType != VMDKETYPE_HOSTED_SPARSE
+        || !pImage->pGTCache
+        || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE
+        || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry])
+        return VERR_INTERNAL_ERROR;
+
+    /* Update grain table entry. */
+    pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset);
+
+    if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
+    {
+        memcpy(pExtent->pvGrain, pvBuf, cbWrite);
+        memset((char *)pExtent->pvGrain + cbWrite, '\0',
+               VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite);
+        pData = pExtent->pvGrain;
+    }
+    rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
+                             VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                             uSector, &cbGrain);
+    if (RT_FAILURE(rc))
+    {
+        pExtent->uGrainSectorAbs = 0;
+        AssertRC(rc);
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
+    }
+    pExtent->uLastGrainAccess = uGrain;
+    pExtent->uAppendPosition += cbGrain;
+
+    return rc;
+}
+
+/**
+ * Internal: Updates the grain table during a async grain allocation.
+ */
+static int vmdkAllocGrainAsyncGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                       PVDIOCTX pIoCtx,
+                                       PVMDKGRAINALLOCASYNC pGrainAlloc)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKGTCACHE pCache = pImage->pGTCache;
+    uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
+    uint32_t uGTHash, uGTBlockIndex;
+    uint64_t uGTSector, uRGTSector, uGTBlock;
+    uint64_t uSector = pGrainAlloc->uSector;
+    PVMDKGTCACHEENTRY pGTCacheEntry;
+
+    LogFlowFunc(("pImage=%#p pExtent=%#p pCache=%#p pIoCtx=%#p pGrainAlloc=%#p\n",
+                 pImage, pExtent, pCache, pIoCtx, pGrainAlloc));
+
+    uGTSector = pGrainAlloc->uGTSector;
+    uRGTSector = pGrainAlloc->uRGTSector;
+    LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
+
+    /* Update the grain table (and the cache). */
+    uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
+    uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
+    pGTCacheEntry = &pCache->aGTCache[uGTHash];
+    if (    pGTCacheEntry->uExtent != pExtent->uExtent
+        ||  pGTCacheEntry->uGTBlock != uGTBlock)
+    {
+        /* Cache miss, fetch data from disk. */
+        LogFlow(("Cache miss, fetch data from disk\n"));
+        PVDMETAXFER pMetaXfer = NULL;
+        rc = vmdkFileReadMetaAsync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                                   aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+                                   &pMetaXfer, vmdkAllocGrainAsyncComplete, pGrainAlloc);
+        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        {
+            pGrainAlloc->cIoXfersPending++;
+            pGrainAlloc->fGTUpdateNeeded = true;
+            /* Leave early, we will be called  again after the read completed. */
+            LogFlowFunc(("Metadata read in progress, leaving\n"));
+            return rc;
+        }
+        else if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
+        vmdkFileMetaXferRelease(pImage, pMetaXfer);
+        pGTCacheEntry->uExtent = pExtent->uExtent;
+        pGTCacheEntry->uGTBlock = uGTBlock;
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
+    }
+    else
+    {
+        /* Cache hit. Convert grain table block back to disk format, otherwise
+         * the code below will write garbage for all but the updated entry. */
+        for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
+            aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
+    }
+    pGrainAlloc->fGTUpdateNeeded = false;
+    uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
+    aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset));
+    pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset);
+    /* Update grain table on disk. */
+    rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                                aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+                                vmdkAllocGrainAsyncComplete, pGrainAlloc);
+    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        pGrainAlloc->cIoXfersPending++;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
+    if (pExtent->pRGD)
+    {
+        /* Update backup grain table on disk. */
+        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                    VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
+                                    aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
+                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
+        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+            pGrainAlloc->cIoXfersPending++;
+        else if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
+    }
+#ifdef VBOX_WITH_VMDK_ESX
+    if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
+    {
+        pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
+        pExtent->fMetaDirty = true;
+    }
+#endif /* VBOX_WITH_VMDK_ESX */
+
+    LogFlowFunc(("leaving rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+/**
+ * Internal - complete the grain allocation by updating disk grain table if required.
+ */
+static int vmdkAllocGrainAsyncComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    int rc = VINF_SUCCESS;
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKGRAINALLOCASYNC pGrainAlloc = (PVMDKGRAINALLOCASYNC)pvUser;
+    PVMDKEXTENT pExtent = pGrainAlloc->pExtent;
+
+    LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc\n",
+                 pBackendData, pIoCtx, pvUser, rcReq));
+
+    pGrainAlloc->cIoXfersPending--;
+    if (!pGrainAlloc->cIoXfersPending && pGrainAlloc->fGTUpdateNeeded)
+        rc = vmdkAllocGrainAsyncGTUpdate(pImage, pGrainAlloc->pExtent,
+                                         pIoCtx, pGrainAlloc);
+
+    if (!pGrainAlloc->cIoXfersPending)
+    {
+        /* Grain allocation completed. */
+        RTMemFree(pGrainAlloc);
+    }
+
+    LogFlowFunc(("Leaving rc=%Rrc\n", rc));
+    return rc;
+}
+
+/**
+ * Internal. Allocates a new grain table (if necessary) - async version.
+ */
+static int vmdkAllocGrainAsync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                               PVDIOCTX pIoCtx, uint64_t uSector,
+                               uint64_t cbWrite)
+{
+    PVMDKGTCACHE pCache = pImage->pGTCache;
+    uint64_t uGDIndex, uGTSector, uRGTSector;
+    uint64_t uFileOffset;
+    PVMDKGRAINALLOCASYNC pGrainAlloc = NULL;
+    int rc;
+
+    LogFlowFunc(("pCache=%#p pExtent=%#p pIoCtx=%#p uSector=%llu cbWrite=%llu\n",
+                 pCache, pExtent, pIoCtx, uSector, cbWrite));
+
+    AssertReturn(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), VERR_NOT_SUPPORTED);
+
+    pGrainAlloc = (PVMDKGRAINALLOCASYNC)RTMemAllocZ(sizeof(VMDKGRAINALLOCASYNC));
+    if (!pGrainAlloc)
+        return VERR_NO_MEMORY;
+
+    pGrainAlloc->pExtent = pExtent;
+    pGrainAlloc->uSector = uSector;
+
+    uGDIndex = uSector / pExtent->cSectorsPerGDE;
+    if (uGDIndex >= pExtent->cGDEntries)
+    {
+        RTMemFree(pGrainAlloc);
+        return VERR_OUT_OF_RANGE;
+    }
+    uGTSector = pExtent->pGD[uGDIndex];
+    if (pExtent->pRGD)
+        uRGTSector = pExtent->pRGD[uGDIndex];
+    else
+        uRGTSector = 0; /**< avoid compiler warning */
+    if (!uGTSector)
+    {
+        LogFlow(("Allocating new grain table\n"));
+
+        /* There is no grain table referenced by this grain directory
+         * entry. So there is absolutely no data in this area. Allocate
+         * a new grain table and put the reference to it in the GDs. */
+        uFileOffset = pExtent->uAppendPosition;
+        if (!uFileOffset)
+            return VERR_INTERNAL_ERROR;
+        Assert(!(uFileOffset % 512));
+
+        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+        uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
+
+        /* Normally the grain table is preallocated for hosted sparse extents
+         * that support more than 32 bit sector numbers. So this shouldn't
+         * ever happen on a valid extent. */
+        if (uGTSector > UINT32_MAX)
+            return VERR_VD_VMDK_INVALID_HEADER;
+
+        /* Write grain table by writing the required number of grain table
+         * cache chunks. Allocate memory dynamically here or we flood the
+         * metadata cache with very small entries. */
+        size_t cbGTDataTmp = pExtent->cGTEntries * sizeof(uint32_t);
+        uint32_t *paGTDataTmp = (uint32_t *)RTMemTmpAllocZ(cbGTDataTmp);
+
+        if (!paGTDataTmp)
+            return VERR_NO_MEMORY;
+
+        memset(paGTDataTmp, '\0', cbGTDataTmp);
+        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                    VMDK_SECTOR2BYTE(uGTSector),
+                                    paGTDataTmp, cbGTDataTmp, pIoCtx,
+                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
+        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+            pGrainAlloc->cIoXfersPending++;
+        else if (RT_FAILURE(rc))
+        {
+            RTMemTmpFree(paGTDataTmp);
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
+        }
+        pExtent->uAppendPosition = RT_ALIGN_64(  pExtent->uAppendPosition
+                                               + cbGTDataTmp, 512);
+
+        if (pExtent->pRGD)
+        {
+            AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
+            uFileOffset = pExtent->uAppendPosition;
+            if (!uFileOffset)
+                return VERR_INTERNAL_ERROR;
+            Assert(!(uFileOffset % 512));
+            uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
+
+            /* Normally the redundant grain table is preallocated for hosted
+             * sparse extents that support more than 32 bit sector numbers. So
+             * this shouldn't ever happen on a valid extent. */
+            if (uRGTSector > UINT32_MAX)
+            {
+                RTMemTmpFree(paGTDataTmp);
+                return VERR_VD_VMDK_INVALID_HEADER;
+            }
+
+            /* Write grain table by writing the required number of grain table
+             * cache chunks. Allocate memory dynamically here or we flood the
+             * metadata cache with very small entries. */
+            rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                        VMDK_SECTOR2BYTE(uRGTSector),
+                                        paGTDataTmp, cbGTDataTmp, pIoCtx,
+                                        vmdkAllocGrainAsyncComplete, pGrainAlloc);
+            if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                pGrainAlloc->cIoXfersPending++;
+            else if (RT_FAILURE(rc))
+            {
+                RTMemTmpFree(paGTDataTmp);
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
+            }
+
+            pExtent->uAppendPosition = pExtent->uAppendPosition + cbGTDataTmp;
+        }
+
+        RTMemTmpFree(paGTDataTmp);
+
+        /* Update the grain directory on disk (doing it before writing the
+         * grain table will result in a garbled extent if the operation is
+         * aborted for some reason. Otherwise the worst that can happen is
+         * some unused sectors in the extent. */
+        uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
+        rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                    VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
+                                    &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
+                                    vmdkAllocGrainAsyncComplete, pGrainAlloc);
+        if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+            pGrainAlloc->cIoXfersPending++;
+        else if (RT_FAILURE(rc))
+            return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
+        if (pExtent->pRGD)
+        {
+            uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
+            rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile,
+                                        VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
+                                        &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
+                                        vmdkAllocGrainAsyncComplete, pGrainAlloc);
+            if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                pGrainAlloc->cIoXfersPending++;
+            else if (RT_FAILURE(rc))
+                return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
+        }
+
+        /* As the final step update the in-memory copy of the GDs. */
+        pExtent->pGD[uGDIndex] = uGTSector;
+        if (pExtent->pRGD)
+            pExtent->pRGD[uGDIndex] = uRGTSector;
+    }
+
+    LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
+    pGrainAlloc->uGTSector = uGTSector;
+    pGrainAlloc->uRGTSector = uRGTSector;
+
+    uFileOffset = pExtent->uAppendPosition;
+    if (!uFileOffset)
+        return VERR_INTERNAL_ERROR;
+    Assert(!(uFileOffset % 512));
+
+    pGrainAlloc->uGrainOffset = uFileOffset;
+
+    /* Write the data. Always a full grain, or we're in big trouble. */
+    rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
+                                uFileOffset, pIoCtx, cbWrite,
+                                vmdkAllocGrainAsyncComplete, pGrainAlloc);
+    if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+        pGrainAlloc->cIoXfersPending++;
+    else if (RT_FAILURE(rc))
+        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
+
+    rc = vmdkAllocGrainAsyncGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
+
+    if (!pGrainAlloc->cIoXfersPending)
+    {
+        /* Grain allocation completed. */
+        RTMemFree(pGrainAlloc);
+    }
+
+    LogFlowFunc(("leaving rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+/**
+ * Internal. Reads the contents by sequentially going over the compressed
+ * grains (hoping that they are in sequence).
+ */
+static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
+                                    uint64_t uSector, void *pvBuf,
+                                    uint64_t cbRead)
+{
+    int rc;
+
+    /* Do not allow to go back. */
+    uint32_t uGrain = uSector / pExtent->cSectorsPerGrain;
+    if (uGrain < pExtent->uLastGrainAccess)
+        return VERR_VD_VMDK_INVALID_STATE;
+    pExtent->uLastGrainAccess = uGrain;
+
+    /* After a previous error do not attempt to recover, as it would need
+     * seeking (in the general case backwards which is forbidden). */
+    if (!pExtent->uGrainSectorAbs)
+        return VERR_VD_VMDK_INVALID_STATE;
+
+    /* Check if we need to read something from the image or if what we have
+     * in the buffer is good to fulfill the request. */
+    if (!pExtent->cbGrainStreamRead || uGrain > pExtent->uGrain)
+    {
+        uint32_t uGrainSectorAbs =   pExtent->uGrainSectorAbs
+                                   + VMDK_BYTE2SECTOR(pExtent->cbGrainStreamRead);
+
+        /* Get the marker from the next data block - and skip everything which
+         * is not a compressed grain. If it's a compressed grain which is for
+         * the requested sector (or after), read it. */
+        VMDKMARKER Marker;
+        do
+        {
+            RT_ZERO(Marker);
+            rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                  VMDK_SECTOR2BYTE(uGrainSectorAbs),
+                                  &Marker, RT_OFFSETOF(VMDKMARKER, uType),
+                                  NULL);
+            if (RT_FAILURE(rc))
+                return rc;
+            Marker.uSector = RT_LE2H_U64(Marker.uSector);
+            Marker.cbSize = RT_LE2H_U32(Marker.cbSize);
+
+            if (Marker.cbSize == 0)
+            {
+                /* A marker for something else than a compressed grain. */
+                rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                        VMDK_SECTOR2BYTE(uGrainSectorAbs)
+                                      + RT_OFFSETOF(VMDKMARKER, uType),
+                                      &Marker.uType, sizeof(Marker.uType),
+                                      NULL);
+                if (RT_FAILURE(rc))
+                    return rc;
+                Marker.uType = RT_LE2H_U32(Marker.uType);
+                switch (Marker.uType)
+                {
+                    case VMDK_MARKER_EOS:
+                        uGrainSectorAbs++;
+                        /* Read (or mostly skip) to the end of file. Uses the
+                         * Marker (LBA sector) as it is unused anyway. This
+                         * makes sure that really everything is read in the
+                         * success case. If this read fails it means the image
+                         * is truncated, but this is harmless so ignore. */
+                        vmdkFileReadSync(pImage, pExtent->pFile,
+                                           VMDK_SECTOR2BYTE(uGrainSectorAbs)
+                                         + 511,
+                                         &Marker.uSector, 1, NULL);
+                        break;
+                    case VMDK_MARKER_GT:
+                        uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
+                        break;
+                    case VMDK_MARKER_GD:
+                        uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(RT_ALIGN(pExtent->cGDEntries * sizeof(uint32_t), 512));
+                        break;
+                    case VMDK_MARKER_FOOTER:
+                        uGrainSectorAbs += 2;
+                        break;
+                    default:
+                        AssertMsgFailed(("VMDK: corrupted marker, type=%#x\n", Marker.uType));
+                        pExtent->uGrainSectorAbs = 0;
+                        return VERR_VD_VMDK_INVALID_STATE;
+                }
+                pExtent->cbGrainStreamRead = 0;
+            }
+            else
+            {
+                /* A compressed grain marker. If it is at/after what we're
+                 * interested in read and decompress data. */
+                if (uSector > Marker.uSector + pExtent->cSectorsPerGrain)
+                {
+                    uGrainSectorAbs += VMDK_BYTE2SECTOR(RT_ALIGN(Marker.cbSize + RT_OFFSETOF(VMDKMARKER, uType), 512));
+                    continue;
+                }
+                uint64_t uLBA = 0;
+                uint32_t cbGrainStreamRead = 0;
+                rc = vmdkFileInflateSync(pImage, pExtent,
+                                         VMDK_SECTOR2BYTE(uGrainSectorAbs),
+                                         pExtent->pvGrain,
+                                         VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                         &Marker, &uLBA, &cbGrainStreamRead);
+                if (RT_FAILURE(rc))
+                {
+                    pExtent->uGrainSectorAbs = 0;
+                    return rc;
+                }
+                if (   pExtent->uGrain
+                    && uLBA / pExtent->cSectorsPerGrain <= pExtent->uGrain)
+                {
+                    pExtent->uGrainSectorAbs = 0;
+                    return VERR_VD_VMDK_INVALID_STATE;
+                }
+                pExtent->uGrain = uLBA / pExtent->cSectorsPerGrain;
+                pExtent->cbGrainStreamRead = cbGrainStreamRead;
+                break;
+            }
+        } while (Marker.uType != VMDK_MARKER_EOS);
+
+        pExtent->uGrainSectorAbs = uGrainSectorAbs;
+
+        if (!pExtent->cbGrainStreamRead && Marker.uType == VMDK_MARKER_EOS)
+        {
+            pExtent->uGrain = UINT32_MAX;
+            /* Must set a non-zero value for pExtent->cbGrainStreamRead or
+             * the next read would try to get more data, and we're at EOF. */
+            pExtent->cbGrainStreamRead = 1;
+        }
+    }
+
+    if (pExtent->uGrain > uSector / pExtent->cSectorsPerGrain)
+    {
+        /* The next data block we have is not for this area, so just return
+         * that there is no data. */
+        return VERR_VD_BLOCK_FREE;
+    }
+
+    uint32_t uSectorInGrain = uSector % pExtent->cSectorsPerGrain;
+    memcpy(pvBuf,
+           (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
+           cbRead);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Replaces a fragment of a string with the specified string.
+ *
+ * @returns Pointer to the allocated UTF-8 string.
+ * @param   pszWhere        UTF-8 string to search in.
+ * @param   pszWhat         UTF-8 string to search for.
+ * @param   pszByWhat       UTF-8 string to replace the found string with.
+ */
+static char *vmdkStrReplace(const char *pszWhere, const char *pszWhat,
+                            const char *pszByWhat)
+{
+    AssertPtr(pszWhere);
+    AssertPtr(pszWhat);
+    AssertPtr(pszByWhat);
+    const char *pszFoundStr = strstr(pszWhere, pszWhat);
+    if (!pszFoundStr)
+        return NULL;
+    size_t cFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
+    char *pszNewStr = (char *)RTMemAlloc(cFinal);
+    if (pszNewStr)
+    {
+        char *pszTmp = pszNewStr;
+        memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
+        pszTmp += pszFoundStr - pszWhere;
+        memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
+        pszTmp += strlen(pszByWhat);
+        strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
+    }
+    return pszNewStr;
+}
+
+
+/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
+static int vmdkCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
+                            PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
+{
+    LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
+                 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
+    int rc = VINF_SUCCESS;
+    PVMDKIMAGE pImage;
+
+    if (   !pszFilename
+        || !*pszFilename
+        || strchr(pszFilename, '"'))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pFile = NULL;
+    pImage->pExtents = NULL;
+    pImage->pFiles = NULL;
+    pImage->pGTCache = NULL;
+    pImage->pDescData = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+    /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
+     * much as possible in vmdkOpenImage. */
+    rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
+    vmdkFreeImage(pImage, false);
+    RTMemFree(pImage);
+
+    if (RT_SUCCESS(rc))
+        *penmType = VDTYPE_HDD;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnOpen */
+static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
+                    PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                    VDTYPE enmType, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
+    int rc;
+    PVMDKIMAGE pImage;
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename
+        || strchr(pszFilename, '"'))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pFile = NULL;
+    pImage->pExtents = NULL;
+    pImage->pFiles = NULL;
+    pImage->pGTCache = NULL;
+    pImage->pDescData = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+
+    rc = vmdkOpenImage(pImage, uOpenFlags);
+    if (RT_SUCCESS(rc))
+        *ppBackendData = pImage;
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnCreate */
+static int vmdkCreate(const char *pszFilename, uint64_t cbSize,
+                      unsigned uImageFlags, const char *pszComment,
+                      PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
+                      PCRTUUID pUuid, unsigned uOpenFlags,
+                      unsigned uPercentStart, unsigned uPercentSpan,
+                      PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+                      PVDINTERFACE pVDIfsOperation, void **ppBackendData)
+{
+    LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
+    int rc;
+    PVMDKIMAGE pImage;
+
+    PFNVDPROGRESS pfnProgress = NULL;
+    void *pvUser = NULL;
+    PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
+                                              VDINTERFACETYPE_PROGRESS);
+    PVDINTERFACEPROGRESS pCbProgress = NULL;
+    if (pIfProgress)
+    {
+        pCbProgress = VDGetInterfaceProgress(pIfProgress);
+        pfnProgress = pCbProgress->pfnProgress;
+        pvUser = pIfProgress->pvUser;
+    }
+
+    /* Check open flags. All valid flags are supported. */
+    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Check size. Maximum 2TB-64K for sparse images, otherwise unlimited. */
+    if (    !cbSize
+        ||  (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 2 - _64K))
+    {
+        rc = VERR_VD_INVALID_SIZE;
+        goto out;
+    }
+
+    /* Check remaining arguments. */
+    if (   !VALID_PTR(pszFilename)
+        || !*pszFilename
+        || strchr(pszFilename, '"')
+        || !VALID_PTR(pPCHSGeometry)
+        || !VALID_PTR(pLCHSGeometry)
+#ifndef VBOX_WITH_VMDK_ESX
+        || (    uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX
+            &&  !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
+#endif
+        || (   (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+            && (uImageFlags & ~(VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_IMAGE_FLAGS_DIFF))))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
+    if (!pImage)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+    pImage->pszFilename = pszFilename;
+    pImage->pFile = NULL;
+    pImage->pExtents = NULL;
+    pImage->pFiles = NULL;
+    pImage->pGTCache = NULL;
+    pImage->pDescData = NULL;
+    pImage->pVDIfsDisk = pVDIfsDisk;
+    pImage->pVDIfsImage = pVDIfsImage;
+    /* Descriptors for split images can be pretty large, especially if the
+     * filename is long. So prepare for the worst, and allocate quite some
+     * memory for the descriptor in this case. */
+    if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
+        pImage->cbDescAlloc = VMDK_SECTOR2BYTE(200);
+    else
+        pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
+    pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
+    if (!pImage->pDescData)
+    {
+        RTMemFree(pImage);
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    rc = vmdkCreateImage(pImage, cbSize, uImageFlags, pszComment,
+                         pPCHSGeometry, pLCHSGeometry, pUuid,
+                         pfnProgress, pvUser, uPercentStart, uPercentSpan);
+    if (RT_SUCCESS(rc))
+    {
+        /* So far the image is opened in read/write mode. Make sure the
+         * image is opened in read-only mode if the caller requested that. */
+        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            vmdkFreeImage(pImage, false);
+            rc = vmdkOpenImage(pImage, uOpenFlags);
+            if (RT_FAILURE(rc))
+                goto out;
+        }
+        *ppBackendData = pImage;
+    }
+    else
+    {
+        RTMemFree(pImage->pDescData);
+        RTMemFree(pImage);
+    }
+
+out:
+    LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRename */
+static int vmdkRename(void *pBackendData, const char *pszFilename)
+{
+    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
+
+    PVMDKIMAGE  pImage  = (PVMDKIMAGE)pBackendData;
+    int               rc = VINF_SUCCESS;
+    char   **apszOldName = NULL;
+    char   **apszNewName = NULL;
+    char  **apszNewLines = NULL;
+    char *pszOldDescName = NULL;
+    bool     fImageFreed = false;
+    bool   fEmbeddedDesc = false;
+    unsigned    cExtents = pImage->cExtents;
+    char *pszNewBaseName = NULL;
+    char *pszOldBaseName = NULL;
+    char *pszNewFullName = NULL;
+    char *pszOldFullName = NULL;
+    const char *pszOldImageName;
+    unsigned i, line;
+    VMDKDESCRIPTOR DescriptorCopy;
+    VMDKEXTENT ExtentCopy;
+
+    memset(&DescriptorCopy, 0, sizeof(DescriptorCopy));
+
+    /* Check arguments. */
+    if (   !pImage
+        || (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
+        || !VALID_PTR(pszFilename)
+        || !*pszFilename)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /*
+     * Allocate an array to store both old and new names of renamed files
+     * in case we have to roll back the changes. Arrays are initialized
+     * with zeros. We actually save stuff when and if we change it.
+     */
+    apszOldName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
+    apszNewName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
+    apszNewLines = (char **)RTMemTmpAllocZ((cExtents) * sizeof(char*));
+    if (!apszOldName || !apszNewName || !apszNewLines)
+    {
+        rc = VERR_NO_MEMORY;
+        goto out;
+    }
+
+    /* Save the descriptor size and position. */
+    if (pImage->pDescData)
+    {
+        /* Separate descriptor file. */
+        fEmbeddedDesc = false;
+    }
+    else
+    {
+        /* Embedded descriptor file. */
+        ExtentCopy = pImage->pExtents[0];
+        fEmbeddedDesc = true;
+    }
+    /* Save the descriptor content. */
+    DescriptorCopy.cLines = pImage->Descriptor.cLines;
+    for (i = 0; i < DescriptorCopy.cLines; i++)
+    {
+        DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
+        if (!DescriptorCopy.aLines[i])
+        {
+            rc = VERR_NO_MEMORY;
+            goto out;
+        }
+    }
+
+    /* Prepare both old and new base names used for string replacement. */
+    pszNewBaseName = RTStrDup(RTPathFilename(pszFilename));
+    RTPathStripExt(pszNewBaseName);
+    pszOldBaseName = RTStrDup(RTPathFilename(pImage->pszFilename));
+    RTPathStripExt(pszOldBaseName);
+    /* Prepare both old and new full names used for string replacement. */
+    pszNewFullName = RTStrDup(pszFilename);
+    RTPathStripExt(pszNewFullName);
+    pszOldFullName = RTStrDup(pImage->pszFilename);
+    RTPathStripExt(pszOldFullName);
+
+    /* --- Up to this point we have not done any damage yet. --- */
+
+    /* Save the old name for easy access to the old descriptor file. */
+    pszOldDescName = RTStrDup(pImage->pszFilename);
+    /* Save old image name. */
+    pszOldImageName = pImage->pszFilename;
+
+    /* Update the descriptor with modified extent names. */
+    for (i = 0, line = pImage->Descriptor.uFirstExtent;
+        i < cExtents;
+        i++, line = pImage->Descriptor.aNextLines[line])
+    {
+        /* Assume that vmdkStrReplace will fail. */
+        rc = VERR_NO_MEMORY;
+        /* Update the descriptor. */
+        apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
+            pszOldBaseName, pszNewBaseName);
+        if (!apszNewLines[i])
+            goto rollback;
+        pImage->Descriptor.aLines[line] = apszNewLines[i];
+    }
+    /* Make sure the descriptor gets written back. */
+    pImage->Descriptor.fDirty = true;
+    /* Flush the descriptor now, in case it is embedded. */
+    vmdkFlushImage(pImage);
+
+    /* Close and rename/move extents. */
+    for (i = 0; i < cExtents; i++)
+    {
+        PVMDKEXTENT pExtent = &pImage->pExtents[i];
+        /* Compose new name for the extent. */
+        apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
+            pszOldFullName, pszNewFullName);
+        if (!apszNewName[i])
+            goto rollback;
+        /* Close the extent file. */
+        vmdkFileClose(pImage, &pExtent->pFile, false);
+        /* Rename the extent file. */
+        rc = vmdkFileMove(pImage, pExtent->pszFullname, apszNewName[i], 0);
+        if (RT_FAILURE(rc))
+            goto rollback;
+        /* Remember the old name. */
+        apszOldName[i] = RTStrDup(pExtent->pszFullname);
+    }
+    /* Release all old stuff. */
+    vmdkFreeImage(pImage, false);
+
+    fImageFreed = true;
+
+    /* Last elements of new/old name arrays are intended for
+     * storing descriptor's names.
+     */
+    apszNewName[cExtents] = RTStrDup(pszFilename);
+    /* Rename the descriptor file if it's separate. */
+    if (!fEmbeddedDesc)
+    {
+        rc = vmdkFileMove(pImage, pImage->pszFilename, apszNewName[cExtents], 0);
+        if (RT_FAILURE(rc))
+            goto rollback;
+        /* Save old name only if we may need to change it back. */
+        apszOldName[cExtents] = RTStrDup(pszFilename);
+    }
+
+    /* Update pImage with the new information. */
+    pImage->pszFilename = pszFilename;
+
+    /* Open the new image. */
+    rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
+    if (RT_SUCCESS(rc))
+        goto out;
+
+rollback:
+    /* Roll back all changes in case of failure. */
+    if (RT_FAILURE(rc))
+    {
+        int rrc;
+        if (!fImageFreed)
+        {
+            /*
+             * Some extents may have been closed, close the rest. We will
+             * re-open the whole thing later.
+             */
+            vmdkFreeImage(pImage, false);
+        }
+        /* Rename files back. */
+        for (i = 0; i <= cExtents; i++)
+        {
+            if (apszOldName[i])
+            {
+                rrc = vmdkFileMove(pImage, apszNewName[i], apszOldName[i], 0);
+                AssertRC(rrc);
+            }
+        }
+        /* Restore the old descriptor. */
+        PVMDKFILE pFile;
+        rrc = vmdkFileOpen(pImage, &pFile, pszOldDescName,
+                           VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_NORMAL,
+                                                      false /* fCreate */),
+                           false /* fAsyncIO */);
+        AssertRC(rrc);
+        if (fEmbeddedDesc)
+        {
+            ExtentCopy.pFile = pFile;
+            pImage->pExtents = &ExtentCopy;
+        }
+        else
+        {
+            /* Shouldn't be null for separate descriptor.
+             * There will be no access to the actual content.
+             */
+            pImage->pDescData = pszOldDescName;
+            pImage->pFile = pFile;
+        }
+        pImage->Descriptor = DescriptorCopy;
+        vmdkWriteDescriptor(pImage);
+        vmdkFileClose(pImage, &pFile, false);
+        /* Get rid of the stuff we implanted. */
+        pImage->pExtents = NULL;
+        pImage->pFile = NULL;
+        pImage->pDescData = NULL;
+        /* Re-open the image back. */
+        pImage->pszFilename = pszOldImageName;
+        rrc = vmdkOpenImage(pImage, pImage->uOpenFlags);
+        AssertRC(rrc);
+    }
+
+out:
+    for (i = 0; i < DescriptorCopy.cLines; i++)
+        if (DescriptorCopy.aLines[i])
+            RTStrFree(DescriptorCopy.aLines[i]);
+    if (apszOldName)
+    {
+        for (i = 0; i <= cExtents; i++)
+            if (apszOldName[i])
+                RTStrFree(apszOldName[i]);
+        RTMemTmpFree(apszOldName);
+    }
+    if (apszNewName)
+    {
+        for (i = 0; i <= cExtents; i++)
+            if (apszNewName[i])
+                RTStrFree(apszNewName[i]);
+        RTMemTmpFree(apszNewName);
+    }
+    if (apszNewLines)
+    {
+        for (i = 0; i < cExtents; i++)
+            if (apszNewLines[i])
+                RTStrFree(apszNewLines[i]);
+        RTMemTmpFree(apszNewLines);
+    }
+    if (pszOldDescName)
+        RTStrFree(pszOldDescName);
+    if (pszOldBaseName)
+        RTStrFree(pszOldBaseName);
+    if (pszNewBaseName)
+        RTStrFree(pszNewBaseName);
+    if (pszOldFullName)
+        RTStrFree(pszOldFullName);
+    if (pszNewFullName)
+        RTStrFree(pszNewFullName);
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnClose */
+static int vmdkClose(void *pBackendData, bool fDelete)
+{
+    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    rc = vmdkFreeImage(pImage, fDelete);
+    RTMemFree(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnRead */
+static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
+                    size_t cbToRead, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKEXTENT pExtent;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToRead % 512 == 0);
+
+    if (   uOffset + cbToRead > pImage->cbSize
+        || cbToRead == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
+                        &pExtent, &uSectorExtentRel);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Check access permissions as defined in the extent descriptor. */
+    if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
+    {
+        rc = VERR_VD_VMDK_INVALID_STATE;
+        goto out;
+    }
+
+    /* Clip read range to remain in this extent. */
+    cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+
+    /* Handle the read according to the current extent type. */
+    switch (pExtent->enmType)
+    {
+        case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+        case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
+                               &uSectorExtentAbs);
+            if (RT_FAILURE(rc))
+                goto out;
+            /* Clip read range to at most the rest of the grain. */
+            cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            Assert(!(cbToRead % 512));
+            if (uSectorExtentAbs == 0)
+            {
+                if (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+                    || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+                    || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
+                    rc = VERR_VD_BLOCK_FREE;
+                else
+                    rc = vmdkStreamReadSequential(pImage, pExtent,
+                                                  uSectorExtentRel,
+                                                  pvBuf, cbToRead);
+            }
+            else
+            {
+                if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+                {
+                    uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
+                    uSectorExtentAbs -= uSectorInGrain;
+                    uint64_t uLBA;
+                    if (pExtent->uGrainSectorAbs != uSectorExtentAbs)
+                    {
+                        rc = vmdkFileInflateSync(pImage, pExtent,
+                                                 VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                                 pExtent->pvGrain,
+                                                 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
+                                                 NULL, &uLBA, NULL);
+                        if (RT_FAILURE(rc))
+                        {
+                            pExtent->uGrainSectorAbs = 0;
+                            AssertRC(rc);
+                            goto out;
+                        }
+                        pExtent->uGrainSectorAbs = uSectorExtentAbs;
+                        pExtent->uGrain = uSectorExtentRel / pExtent->cSectorsPerGrain;
+                        Assert(uLBA == uSectorExtentRel);
+                    }
+                    memcpy(pvBuf, (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), cbToRead);
+                }
+                else
+                {
+                    rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                          VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                          pvBuf, cbToRead, NULL);
+                }
+            }
+            break;
+        case VMDKETYPE_VMFS:
+        case VMDKETYPE_FLAT:
+            rc = vmdkFileReadSync(pImage, pExtent->pFile,
+                                  VMDK_SECTOR2BYTE(uSectorExtentRel),
+                                  pvBuf, cbToRead, NULL);
+            break;
+        case VMDKETYPE_ZERO:
+            memset(pvBuf, '\0', cbToRead);
+            break;
+    }
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbToRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnWrite */
+static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
+                     size_t cbToWrite, size_t *pcbWriteProcess,
+                     size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKEXTENT pExtent;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbToWrite % 512 == 0);
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    if (cbToWrite == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* No size check here, will do that later when the extent is located.
+     * There are sparse images out there which according to the spec are
+     * invalid, because the total size is not a multiple of the grain size.
+     * Also for sparse images which are stitched together in odd ways (not at
+     * grain boundaries, and with the nominal size not being a multiple of the
+     * grain size), this would prevent writing to the last grain. */
+
+    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
+                        &pExtent, &uSectorExtentRel);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Check access permissions as defined in the extent descriptor. */
+    if (   pExtent->enmAccess != VMDKACCESS_READWRITE
+        && (   !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+            && !pImage->pExtents[0].uAppendPosition
+            && pExtent->enmAccess != VMDKACCESS_READONLY))
+    {
+        rc = VERR_VD_VMDK_INVALID_STATE;
+        goto out;
+    }
+
+    /* Handle the write according to the current extent type. */
+    switch (pExtent->enmType)
+    {
+        case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+        case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            rc = vmdkGetSector(pImage, pExtent, uSectorExtentRel,
+                               &uSectorExtentAbs);
+            if (RT_FAILURE(rc))
+                goto out;
+            /* Clip write range to at most the rest of the grain. */
+            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            if (    pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
+                &&  uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
+            {
+                rc = VERR_VD_VMDK_INVALID_WRITE;
+                goto out;
+            }
+            if (uSectorExtentAbs == 0)
+            {
+                if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+                {
+                    if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
+                    {
+                        /* Full block write to a previously unallocated block.
+                         * Check if the caller wants feedback. */
+                        if (!(fWrite & VD_WRITE_NO_ALLOC))
+                        {
+                            /* Allocate GT and store the grain. */
+                            rc = vmdkAllocGrain(pImage, pExtent,
+                                                uSectorExtentRel,
+                                                pvBuf, cbToWrite);
+                        }
+                        else
+                            rc = VERR_VD_BLOCK_FREE;
+                        *pcbPreRead = 0;
+                        *pcbPostRead = 0;
+                    }
+                    else
+                    {
+                        /* Clip write range to remain in this extent. */
+                        cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+                        *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
+                        *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
+                        rc = VERR_VD_BLOCK_FREE;
+                    }
+                }
+                else
+                {
+                    rc = vmdkStreamAllocGrain(pImage, pExtent,
+                                              uSectorExtentRel,
+                                              pvBuf, cbToWrite);
+                }
+            }
+            else
+            {
+                if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+                {
+                    /* A partial write to a streamOptimized image is simply
+                     * invalid. It requires rewriting already compressed data
+                     * which is somewhere between expensive and impossible. */
+                    rc = VERR_VD_VMDK_INVALID_STATE;
+                    pExtent->uGrainSectorAbs = 0;
+                    AssertRC(rc);
+                }
+                else
+                {
+                    rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                           VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                           pvBuf, cbToWrite, NULL);
+                }
+            }
+            break;
+        case VMDKETYPE_VMFS:
+        case VMDKETYPE_FLAT:
+            /* Clip write range to remain in this extent. */
+            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+            rc = vmdkFileWriteSync(pImage, pExtent->pFile,
+                                   VMDK_SECTOR2BYTE(uSectorExtentRel),
+                                   pvBuf, cbToWrite, NULL);
+            break;
+        case VMDKETYPE_ZERO:
+            /* Clip write range to remain in this extent. */
+            cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+            break;
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbToWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnFlush */
+static int vmdkFlush(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc = VINF_SUCCESS;
+
+    AssertPtr(pImage);
+
+    if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+        rc = vmdkFlushImage(pImage);
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
+static unsigned vmdkGetVersion(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        return VMDK_IMAGE_VERSION;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetSize */
+static uint64_t vmdkGetSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        return pImage->cbSize;
+    else
+        return 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
+static uint64_t vmdkGetFileSize(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    uint64_t cb = 0;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        uint64_t cbFile;
+        if (pImage->pFile != NULL)
+        {
+            int rc = vmdkFileGetSize(pImage, pImage->pFile, &cbFile);
+            if (RT_SUCCESS(rc))
+                cb += cbFile;
+        }
+        for (unsigned i = 0; i < pImage->cExtents; i++)
+        {
+            if (pImage->pExtents[i].pFile != NULL)
+            {
+                int rc = vmdkFileGetSize(pImage, pImage->pExtents[i].pFile, &cbFile);
+                if (RT_SUCCESS(rc))
+                    cb += cbFile;
+            }
+        }
+    }
+
+    LogFlowFunc(("returns %lld\n", cb));
+    return cb;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
+static int vmdkGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->PCHSGeometry.cCylinders)
+        {
+            *pPCHSGeometry = pImage->PCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
+static int vmdkSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+        if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        {
+            rc = VERR_NOT_SUPPORTED;
+            goto out;
+        }
+        rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        pImage->PCHSGeometry = *pPCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
+static int vmdkGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+     LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->LCHSGeometry.cCylinders)
+        {
+            *pLCHSGeometry = pImage->LCHSGeometry;
+            rc = VINF_SUCCESS;
+        }
+        else
+            rc = VERR_VD_GEOMETRY_NOT_SET;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
+static int vmdkSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+        {
+            rc = VERR_VD_IMAGE_READ_ONLY;
+            goto out;
+        }
+        if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+        {
+            rc = VERR_NOT_SUPPORTED;
+            goto out;
+        }
+        rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
+        if (RT_FAILURE(rc))
+            goto out;
+
+        pImage->LCHSGeometry = *pLCHSGeometry;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
+static unsigned vmdkGetImageFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    unsigned uImageFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uImageFlags = pImage->uImageFlags;
+    else
+        uImageFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uImageFlags));
+    return uImageFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
+static unsigned vmdkGetOpenFlags(void *pBackendData)
+{
+    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    unsigned uOpenFlags;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+        uOpenFlags = pImage->uOpenFlags;
+    else
+        uOpenFlags = 0;
+
+    LogFlowFunc(("returns %#x\n", uOpenFlags));
+    return uOpenFlags;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
+static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    /* Image must be opened and the new flags must be valid. */
+    if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* StreamOptimized images need special treatment: reopen is prohibited. */
+    if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        if (pImage->uOpenFlags == uOpenFlags)
+            rc = VINF_SUCCESS;
+        else
+            rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* Implement this operation via reopening the image. */
+    vmdkFreeImage(pImage, false);
+    rc = vmdkOpenImage(pImage, uOpenFlags);
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetComment */
+static int vmdkGetComment(void *pBackendData, char *pszComment,
+                          size_t cbComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        const char *pszCommentEncoded = NULL;
+        rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
+                              "ddb.comment", &pszCommentEncoded);
+        if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
+            pszCommentEncoded = NULL;
+        else if (RT_FAILURE(rc))
+            goto out;
+
+        if (pszComment && pszCommentEncoded)
+            rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
+        else
+        {
+            if (pszComment)
+                *pszComment = '\0';
+            rc = VINF_SUCCESS;
+        }
+        if (pszCommentEncoded)
+            RTStrFree((char *)(void *)pszCommentEncoded);
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetComment */
+static int vmdkSetComment(void *pBackendData, const char *pszComment)
+{
+    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+    if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
+    {
+        rc = VERR_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (pImage)
+        rc = vmdkSetImageComment(pImage, pszComment);
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
+static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ImageUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
+static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    LogFlowFunc(("%RTuuid\n", pUuid));
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+            {
+                pImage->ImageUuid = *pUuid;
+                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                        VMDK_DDB_IMAGE_UUID, pUuid);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
+                rc = VINF_SUCCESS;
+            }
+            else
+                rc = VERR_NOT_SUPPORTED;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
+static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ModificationUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
+static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+            {
+                /* Only touch the modification uuid if it changed. */
+                if (RTUuidCompare(&pImage->ModificationUuid, pUuid))
+                {
+                    pImage->ModificationUuid = *pUuid;
+                    rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                            VMDK_DDB_MODIFICATION_UUID, pUuid);
+                    if (RT_FAILURE(rc))
+                        return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
+                }
+                rc = VINF_SUCCESS;
+            }
+            else
+                rc = VERR_NOT_SUPPORTED;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
+static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ParentUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
+static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+            {
+                pImage->ParentUuid = *pUuid;
+                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                        VMDK_DDB_PARENT_UUID, pUuid);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
+                rc = VINF_SUCCESS;
+            }
+            else
+                rc = VERR_NOT_SUPPORTED;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
+static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        *pUuid = pImage->ParentModificationUuid;
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
+static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
+{
+    LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    int rc;
+
+    AssertPtr(pImage);
+
+    if (pImage)
+    {
+        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+        {
+            if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
+            {
+                pImage->ParentModificationUuid = *pUuid;
+                rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
+                                        VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
+                if (RT_FAILURE(rc))
+                    return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
+                rc = VINF_SUCCESS;
+            }
+            else
+                rc = VERR_NOT_SUPPORTED;
+        }
+        else
+            rc = VERR_VD_IMAGE_READ_ONLY;
+    }
+    else
+        rc = VERR_VD_NOT_OPENED;
+
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnDump */
+static void vmdkDump(void *pBackendData)
+{
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+
+    AssertPtr(pImage);
+    if (pImage)
+    {
+        vmdkMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
+                    pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
+                    pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
+                    VMDK_BYTE2SECTOR(pImage->cbSize));
+        vmdkMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
+        vmdkMessage(pImage, "Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
+        vmdkMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
+        vmdkMessage(pImage, "Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
+    }
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
+static bool vmdkIsAsyncIOSupported(void *pBackendData)
+{
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+
+    /* We do not support async I/O for stream optimized VMDK images. */
+    return (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) == 0;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
+static int vmdkAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
+                         PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKEXTENT pExtent;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbRead % 512 == 0);
+
+    if (   uOffset + cbRead > pImage->cbSize
+        || cbRead == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
+                        &pExtent, &uSectorExtentRel);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Check access permissions as defined in the extent descriptor. */
+    if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
+    {
+        rc = VERR_VD_VMDK_INVALID_STATE;
+        goto out;
+    }
+
+    /* Clip read range to remain in this extent. */
+    cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+
+    /* Handle the read according to the current extent type. */
+    switch (pExtent->enmType)
+    {
+        case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+        case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent,
+                                    uSectorExtentRel, &uSectorExtentAbs);
+            if (RT_FAILURE(rc))
+                goto out;
+            /* Clip read range to at most the rest of the grain. */
+            cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            Assert(!(cbRead % 512));
+            if (uSectorExtentAbs == 0)
+                rc = VERR_VD_BLOCK_FREE;
+            else
+            {
+                AssertMsg(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED), ("Async I/O is not supported for stream optimized VMDK's\n"));
+                rc = vmdkFileReadUserAsync(pImage, pExtent->pFile,
+                                           VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                           pIoCtx, cbRead);
+            }
+            break;
+        case VMDKETYPE_VMFS:
+        case VMDKETYPE_FLAT:
+            rc = vmdkFileReadUserAsync(pImage, pExtent->pFile,
+                                       VMDK_SECTOR2BYTE(uSectorExtentRel),
+                                       pIoCtx, cbRead);
+            break;
+        case VMDKETYPE_ZERO:
+            size_t cbSet;
+
+            cbSet = vmdkFileIoCtxSet(pImage, pIoCtx, 0, cbRead);
+            Assert(cbSet == cbRead);
+
+            rc = VINF_SUCCESS;
+            break;
+    }
+    if (pcbActuallyRead)
+        *pcbActuallyRead = cbRead;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
+static int vmdkAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
+                          PVDIOCTX pIoCtx,
+                          size_t *pcbWriteProcess, size_t *pcbPreRead,
+                          size_t *pcbPostRead, unsigned fWrite)
+{
+    LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
+                 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKEXTENT pExtent;
+    uint64_t uSectorExtentRel;
+    uint64_t uSectorExtentAbs;
+    int rc;
+
+    AssertPtr(pImage);
+    Assert(uOffset % 512 == 0);
+    Assert(cbWrite % 512 == 0);
+
+    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
+    {
+        rc = VERR_VD_IMAGE_READ_ONLY;
+        goto out;
+    }
+
+    if (cbWrite == 0)
+    {
+        rc = VERR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* No size check here, will do that later when the extent is located.
+     * There are sparse images out there which according to the spec are
+     * invalid, because the total size is not a multiple of the grain size.
+     * Also for sparse images which are stitched together in odd ways (not at
+     * grain boundaries, and with the nominal size not being a multiple of the
+     * grain size), this would prevent writing to the last grain. */
+
+    rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
+                        &pExtent, &uSectorExtentRel);
+    if (RT_FAILURE(rc))
+        goto out;
+
+    /* Check access permissions as defined in the extent descriptor. */
+    if (pExtent->enmAccess != VMDKACCESS_READWRITE)
+    {
+        rc = VERR_VD_VMDK_INVALID_STATE;
+        goto out;
+    }
+
+    /* Handle the write according to the current extent type. */
+    switch (pExtent->enmType)
+    {
+        case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+        case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            rc = vmdkGetSectorAsync(pImage, pIoCtx, pExtent, uSectorExtentRel,
+                                    &uSectorExtentAbs);
+            if (RT_FAILURE(rc))
+                goto out;
+            /* Clip write range to at most the rest of the grain. */
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
+            if (    pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
+                &&  uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
+            {
+                rc = VERR_VD_VMDK_INVALID_WRITE;
+                goto out;
+            }
+            if (uSectorExtentAbs == 0)
+            {
+                if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
+                {
+                    /* Full block write to a previously unallocated block.
+                     * Check if the caller wants to avoid the automatic alloc. */
+                    if (!(fWrite & VD_WRITE_NO_ALLOC))
+                    {
+                        /* Allocate GT and find out where to store the grain. */
+                        rc = vmdkAllocGrainAsync(pImage, pExtent, pIoCtx,
+                                                 uSectorExtentRel, cbWrite);
+                    }
+                    else
+                        rc = VERR_VD_BLOCK_FREE;
+                    *pcbPreRead = 0;
+                    *pcbPostRead = 0;
+                }
+                else
+                {
+                    /* Clip write range to remain in this extent. */
+                    cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+                    *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
+                    *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
+                    rc = VERR_VD_BLOCK_FREE;
+                }
+            }
+            else
+            {
+                Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
+                rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
+                                            VMDK_SECTOR2BYTE(uSectorExtentAbs),
+                                            pIoCtx, cbWrite, NULL, NULL);
+            }
+            break;
+        case VMDKETYPE_VMFS:
+        case VMDKETYPE_FLAT:
+            /* Clip write range to remain in this extent. */
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+            rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile,
+                                        VMDK_SECTOR2BYTE(uSectorExtentRel),
+                                        pIoCtx, cbWrite, NULL, NULL);
+            break;
+        case VMDKETYPE_ZERO:
+            /* Clip write range to remain in this extent. */
+            cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
+            break;
+    }
+
+    if (pcbWriteProcess)
+        *pcbWriteProcess = cbWrite;
+
+out:
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
+static int vmdkAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+    PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
+    PVMDKEXTENT pExtent;
+    int rc = VINF_SUCCESS;
+
+    for (unsigned i = 0; i < pImage->cExtents; i++)
+    {
+        pExtent = &pImage->pExtents[i];
+        if (pExtent->pFile != NULL && pExtent->fMetaDirty)
+        {
+            switch (pExtent->enmType)
+            {
+                case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+                case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+                    rc = vmdkWriteMetaSparseExtentAsync(pImage, pExtent, 0, pIoCtx);
+                    if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+                        goto out;
+                    if (pExtent->fFooter)
+                    {
+                        uint64_t uFileOffset = pExtent->uAppendPosition;
+                        if (!uFileOffset)
+                        {
+                            rc = VERR_INTERNAL_ERROR;
+                            goto out;
+                        }
+                        uFileOffset = RT_ALIGN_64(uFileOffset, 512);
+                        rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);
+                        if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
+                            goto out;
+                    }
+                    break;
+                case VMDKETYPE_VMFS:
+                case VMDKETYPE_FLAT:
+                    /* Nothing to do. */
+                    break;
+                case VMDKETYPE_ZERO:
+                default:
+                    AssertMsgFailed(("extent with type %d marked as dirty\n",
+                                     pExtent->enmType));
+                    break;
+            }
+        }
+        switch (pExtent->enmType)
+        {
+            case VMDKETYPE_HOSTED_SPARSE:
+#ifdef VBOX_WITH_VMDK_ESX
+            case VMDKETYPE_ESX_SPARSE:
+#endif /* VBOX_WITH_VMDK_ESX */
+            case VMDKETYPE_VMFS:
+            case VMDKETYPE_FLAT:
+                /*
+                 * Don't ignore block devices like in the sync case
+                 * (they have an absolute path).
+                 * We might have unwritten data in the writeback cache and
+                 * the async I/O manager will handle these requests properly
+                 * even if the block device doesn't support these requests.
+                 */
+                if (   pExtent->pFile != NULL
+                    && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
+                    rc = vmdkFileFlushAsync(pImage, pExtent->pFile, pIoCtx);
+                break;
+            case VMDKETYPE_ZERO:
+                /* No need to do anything for this extent. */
+                break;
+            default:
+                AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
+                break;
+        }
+    }
+
+out:
+    return rc;
+}
+
+
+VBOXHDDBACKEND g_VmdkBackend =
+{
+    /* pszBackendName */
+    "VMDK",
+    /* cbSize */
+    sizeof(VBOXHDDBACKEND),
+    /* uBackendCaps */
+      VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
+    | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC
+    | VD_CAP_VFS,
+    /* paFileExtensions */
+    s_aVmdkFileExtensions,
+    /* paConfigInfo */
+    NULL,
+    /* hPlugin */
+    NIL_RTLDRMOD,
+    /* pfnCheckIfValid */
+    vmdkCheckIfValid,
+    /* pfnOpen */
+    vmdkOpen,
+    /* pfnCreate */
+    vmdkCreate,
+    /* pfnRename */
+    vmdkRename,
+    /* pfnClose */
+    vmdkClose,
+    /* pfnRead */
+    vmdkRead,
+    /* pfnWrite */
+    vmdkWrite,
+    /* pfnFlush */
+    vmdkFlush,
+    /* pfnGetVersion */
+    vmdkGetVersion,
+    /* pfnGetSize */
+    vmdkGetSize,
+    /* pfnGetFileSize */
+    vmdkGetFileSize,
+    /* pfnGetPCHSGeometry */
+    vmdkGetPCHSGeometry,
+    /* pfnSetPCHSGeometry */
+    vmdkSetPCHSGeometry,
+    /* pfnGetLCHSGeometry */
+    vmdkGetLCHSGeometry,
+    /* pfnSetLCHSGeometry */
+    vmdkSetLCHSGeometry,
+    /* pfnGetImageFlags */
+    vmdkGetImageFlags,
+    /* pfnGetOpenFlags */
+    vmdkGetOpenFlags,
+    /* pfnSetOpenFlags */
+    vmdkSetOpenFlags,
+    /* pfnGetComment */
+    vmdkGetComment,
+    /* pfnSetComment */
+    vmdkSetComment,
+    /* pfnGetUuid */
+    vmdkGetUuid,
+    /* pfnSetUuid */
+    vmdkSetUuid,
+    /* pfnGetModificationUuid */
+    vmdkGetModificationUuid,
+    /* pfnSetModificationUuid */
+    vmdkSetModificationUuid,
+    /* pfnGetParentUuid */
+    vmdkGetParentUuid,
+    /* pfnSetParentUuid */
+    vmdkSetParentUuid,
+    /* pfnGetParentModificationUuid */
+    vmdkGetParentModificationUuid,
+    /* pfnSetParentModificationUuid */
+    vmdkSetParentModificationUuid,
+    /* pfnDump */
+    vmdkDump,
+    /* pfnGetTimeStamp */
+    NULL,
+    /* pfnGetParentTimeStamp */
+    NULL,
+    /* pfnSetParentTimeStamp */
+    NULL,
+    /* pfnGetParentFilename */
+    NULL,
+    /* pfnSetParentFilename */
+    NULL,
+    /* pfnIsAsyncIOSupported */
+    vmdkIsAsyncIOSupported,
+    /* pfnAsyncRead */
+    vmdkAsyncRead,
+    /* pfnAsyncWrite */
+    vmdkAsyncWrite,
+    /* pfnAsyncFlush */
+    vmdkAsyncFlush,
+    /* pfnComposeLocation */
+    genericFileComposeLocation,
+    /* pfnComposeName */
+    genericFileComposeName,
+    /* pfnCompact */
+    NULL,
+    /* pfnResize */
+    NULL
+};
Index: /trunk/src/VBox/Storage/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Storage/testcase/Makefile.kmk	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/Makefile.kmk	(revision 33567)
@@ -0,0 +1,94 @@
+# $Id$
+## @file
+# Sub-Makefile for the storage device & driver testcases.
+#
+
+#
+# Copyright (C) 2006-2010 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include	$(KBUILD_PATH)/subheader.kmk
+
+#
+# vditool - useful too for manipulating VDIs, but now pretty obsolete and
+# probably will go away soon. Testcase only now.
+#
+ifdef VBOX_WITH_TESTCASES
+ PROGRAMS += vditool
+  vditool_TEMPLATE = VBOXR3TSTEXE
+  vditool_LIBS     = $(LIB_DDU) $(LIB_RUNTIME)
+  vditool_SOURCES  = vditool.cpp
+endif
+
+
+#
+# Basic testcase for the VDI code.
+#
+#ifdef VBOX_WITH_TESTCASES
+# PROGRAMS += tstVDI
+# ifeq ($(KBUILD_TARGET),l4)
+#  tstVDI_TEMPLATE = VBOXLNXHOSTR3EXE
+# else
+#  tstVDI_TEMPLATE = VBOXR3TSTEXE
+# endif
+# tstVDI_LIBS     = $(vditool_LIBS)
+# tstVDI_SOURCES  = tstVDI.cpp
+#endif
+
+#
+# Basic testcases for the VD code.
+#
+ifdef VBOX_WITH_TESTCASES
+ PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable vbox-img
+ tstVD_TEMPLATE = VBOXR3TSTEXE
+ tstVD-2_TEMPLATE = VBOXR3TSTEXE
+ tstVDCopy_TEMPLATE = VBOXR3TSTEXE
+ tstVDSnap_TEMPLATE = VBOXR3TSTEXE
+ tstVDShareable_TEMPLATE = VBOXR3TSTEXE
+ vbox-img_TEMPLATE = VBOXR3STATIC
+ tstVD_LIBS     = $(LIB_DDU) $(LIB_RUNTIME)
+ tstVD-2_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
+ tstVDCopy_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
+ tstVDSnap_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
+ tstVDShareable_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
+ tstVDSetUuid_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
+ vbox-img_LIBS = \
+	$(VBOX_LIB_RUNTIME_STATIC) \
+	$(PATH_LIB)/StorageLibNoDB$(VBOX_SUFF_LIB)
+ if1of ($(KBUILD_TARGET),os2 win)
+  vbox-img_LIBS += \
+	$(PATH_LIB)/VBox-liblzf-static$(VBOX_SUFF_LIB) \
+	$(PATH_LIB)/VBox-zlib-static$(VBOX_SUFF_LIB)
+ else
+  vbox-img_LIBS += \
+	$(PATH_LIB)/VBox-liblzf$(VBOX_SUFF_LIB) \
+	$(SDK_VBOX_ZLIB_LIBS)
+ endif
+ ifeq ($(KBUILD_TARGET),linux)
+  vbox-img_LIBS += crypt
+ else if1of ($(KBUILD_TARGET),darwin freebsd)
+  vbox-img_LIBS += iconv
+ else ifeq ($(KBUILD_TARGET),win)
+  vbox-img_SDKS.win = VBOX_NTDLL
+ endif
+ tstVD_SOURCES  = tstVD.cpp
+ tstVD-2_SOURCES  = tstVD-2.cpp
+ tstVDCopy_SOURCES  = tstVDCopy.cpp
+ tstVDSnap_SOURCES  = tstVDSnap.cpp
+ tstVDShareable_SOURCES = tstVDShareable.cpp
+
+ vbox-img_SOURCES = \
+	vbox-img.cpp
+endif
+
+include	$(KBUILD_PATH)/subfooter.kmk
+
Index: /trunk/src/VBox/Storage/testcase/tstVD-2.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVD-2.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVD-2.cpp	(revision 33567)
@@ -0,0 +1,260 @@
+/** @file
+ *
+ * Simple VBox HDD container test utility. Only fast tests.
+ */
+
+/*
+ * Copyright (C) 2006-2007 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <VBox/err.h>
+#include <VBox/vd.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/rand.h>
+#include "stdio.h"
+#include "stdlib.h"
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The error count. */
+unsigned g_cErrors = 0;
+
+static struct KeyValuePair {
+    const char *key;
+    const char *value;
+} aCfgNode[] = {
+    { "TargetName", "test" },
+    { "LUN", "1" },
+    { "TargetAddress", "address" },
+    { NULL, NULL }
+};
+
+static bool tstAreKeysValid(void *pvUser, const char *pszzValid)
+{
+    return true;
+}
+
+static const char *tstGetValueByKey(const char *pszKey)
+{
+    for (int i = 0; aCfgNode[i].key; i++)
+        if (!strcmp(aCfgNode[i].key, pszKey))
+            return aCfgNode[i].value;
+    return NULL;
+}
+
+static int tstQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
+{
+    const char *pszValue = tstGetValueByKey(pszName);
+    if (!pszValue)
+        return VERR_CFGM_VALUE_NOT_FOUND;
+    *pcbValue = strlen(pszValue) + 1;
+    return VINF_SUCCESS;
+}
+
+static int tstQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
+{
+    const char *pszTmp = tstGetValueByKey(pszName);
+    if (!pszValue)
+        return VERR_CFGM_VALUE_NOT_FOUND;
+    size_t cchTmp = strlen(pszTmp) + 1;
+    if (cchValue < cchTmp)
+        return VERR_CFGM_NOT_ENOUGH_SPACE;
+    memcpy(pszValue, pszTmp, cchTmp);
+    return VINF_SUCCESS;
+}
+
+
+VDINTERFACECONFIG icc = {
+    sizeof(VDINTERFACECONFIG),
+    VDINTERFACETYPE_CONFIG,
+    tstAreKeysValid,
+    tstQuerySize,
+    tstQuery
+};
+
+static const char *tstVDDeviceType(VDTYPE enmType)
+{
+    switch (enmType)
+    {
+        case VDTYPE_HDD:
+            return "HardDisk";
+        case VDTYPE_DVD:
+            return "DVD";
+        case VDTYPE_FLOPPY:
+            return "Floppy";
+        default:
+            return "Unknown";
+    }
+}
+
+static int tstVDBackendInfo(void)
+{
+    int rc;
+#define MAX_BACKENDS 100
+    VDBACKENDINFO aVDInfo[MAX_BACKENDS];
+    unsigned cEntries;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+            return rc; \
+    } while (0)
+
+    rc = VDBackendInfo(MAX_BACKENDS, aVDInfo, &cEntries);
+    CHECK("VDBackendInfo()");
+
+    for (unsigned i=0; i < cEntries; i++)
+    {
+        RTPrintf("Backend %u: name=%s capabilities=%#06x extensions=",
+                 i, aVDInfo[i].pszBackend, aVDInfo[i].uBackendCaps);
+        if (aVDInfo[i].paFileExtensions)
+        {
+            PCVDFILEEXTENSION pa = aVDInfo[i].paFileExtensions;
+            while (pa->pszExtension != NULL)
+            {
+                if (pa != aVDInfo[i].paFileExtensions)
+                    RTPrintf(",");
+                RTPrintf("%s (%s)", pa->pszExtension, tstVDDeviceType(pa->enmType));
+                pa++;
+            }
+            if (pa == aVDInfo[i].paFileExtensions)
+                RTPrintf("<EMPTY>");
+        }
+        else
+            RTPrintf("<NONE>");
+        RTPrintf(" config=");
+        if (aVDInfo[i].paConfigInfo)
+        {
+            PCVDCONFIGINFO pa = aVDInfo[i].paConfigInfo;
+            while (pa->pszKey != NULL)
+            {
+                if (pa != aVDInfo[i].paConfigInfo)
+                    RTPrintf(",");
+                RTPrintf("(key=%s type=", pa->pszKey);
+                switch (pa->enmValueType)
+                {
+                    case VDCFGVALUETYPE_INTEGER:
+                        RTPrintf("integer");
+                        break;
+                    case VDCFGVALUETYPE_STRING:
+                        RTPrintf("string");
+                        break;
+                    case VDCFGVALUETYPE_BYTES:
+                        RTPrintf("bytes");
+                        break;
+                    default:
+                        RTPrintf("INVALID!");
+                }
+                RTPrintf(" default=");
+                if (pa->pszDefaultValue)
+                    RTPrintf("%s", pa->pszDefaultValue);
+                else
+                    RTPrintf("<NONE>");
+                RTPrintf(" flags=");
+                if (!pa->uKeyFlags)
+                    RTPrintf("none");
+                unsigned cFlags = 0;
+                if (pa->uKeyFlags & VD_CFGKEY_MANDATORY)
+                {
+                    if (cFlags)
+                        RTPrintf(",");
+                    RTPrintf("mandatory");
+                    cFlags++;
+                }
+                if (pa->uKeyFlags & VD_CFGKEY_EXPERT)
+                {
+                    if (cFlags)
+                        RTPrintf(",");
+                    RTPrintf("expert");
+                    cFlags++;
+                }
+                RTPrintf(")");
+                pa++;
+            }
+            if (pa == aVDInfo[i].paConfigInfo)
+                RTPrintf("<EMPTY>");
+        }
+        else
+            RTPrintf("<NONE>");
+        RTPrintf("\n");
+
+        VDINTERFACE ic;
+        ic.cbSize = sizeof(ic);
+        ic.enmInterface = VDINTERFACETYPE_CONFIG;
+        ic.pCallbacks = &icc;
+        char *pszLocation, *pszName;
+        rc = aVDInfo[i].pfnComposeLocation(&ic, &pszLocation);
+        CHECK("pfnComposeLocation()");
+        if (pszLocation)
+        {
+            RTMemFree(pszLocation);
+            if (aVDInfo[i].uBackendCaps & VD_CAP_FILE)
+            {
+                RTPrintf("Non-NULL location returned for file-based backend!\n");
+                return VERR_INTERNAL_ERROR;
+            }
+        }
+        rc = aVDInfo[i].pfnComposeName(&ic, &pszName);
+        CHECK("pfnComposeName()");
+        if (pszName)
+        {
+            RTMemFree(pszName);
+            if (aVDInfo[i].uBackendCaps & VD_CAP_FILE)
+            {
+                RTPrintf("Non-NULL name returned for file-based backend!\n");
+                return VERR_INTERNAL_ERROR;
+            }
+        }
+    }
+
+#undef CHECK
+    return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+    int rc;
+
+    RTR3Init();
+    RTPrintf("tstVD-2: TESTING...\n");
+
+    rc = tstVDBackendInfo();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD-2: getting backend info test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD-2: unloading backends failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    /*
+     * Summary
+     */
+    if (!g_cErrors)
+        RTPrintf("tstVD-2: SUCCESS\n");
+    else
+        RTPrintf("tstVD-2: FAILURE - %d errors\n", g_cErrors);
+
+    return !!g_cErrors;
+}
+
Index: /trunk/src/VBox/Storage/testcase/tstVD.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVD.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVD.cpp	(revision 33567)
@@ -0,0 +1,1079 @@
+/* $Id$ */
+/** @file
+ * Simple VBox HDD container test utility.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <VBox/vd.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/dir.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/rand.h>
+#include "stdio.h"
+#include "stdlib.h"
+
+#define VHD_TEST
+#define VDI_TEST
+#define VMDK_TEST
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The error count. */
+unsigned g_cErrors = 0;
+
+
+static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
+                       const char *pszFormat, va_list va)
+{
+    g_cErrors++;
+    RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
+    RTPrintfV(pszFormat, va);
+    RTPrintf("\n");
+}
+
+static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+    RTPrintf("tstVD: ");
+    RTPrintfV(pszFormat, va);
+    return VINF_SUCCESS;
+}
+
+static int tstVDCreateDelete(const char *pszBackend, const char *pszFilename,
+                             uint64_t cbSize, unsigned uFlags, bool fDelete)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    VDGEOMETRY       PCHS = { 0, 0, 0 };
+    VDGEOMETRY       LCHS = { 0, 0, 0 };
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    rc = VDCreateBase(pVD, pszBackend, pszFilename, cbSize,
+                      uFlags, "Test image", &PCHS, &LCHS, NULL,
+                      VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+    CHECK("VDCreateBase()");
+
+    VDDumpImages(pVD);
+
+    VDClose(pVD, fDelete);
+    if (fDelete)
+    {
+        RTFILE File;
+        rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+        if (RT_SUCCESS(rc))
+        {
+            RTFileClose(File);
+            return VERR_INTERNAL_ERROR;
+        }
+    }
+
+    VDDestroy(pVD);
+#undef CHECK
+    return 0;
+}
+
+static int tstVDOpenDelete(const char *pszBackend, const char *pszFilename)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    VDGEOMETRY       PCHS = { 0, 0, 0 };
+    VDGEOMETRY       LCHS = { 0, 0, 0 };
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen()");
+
+    VDDumpImages(pVD);
+
+    VDClose(pVD, true);
+    RTFILE File;
+    rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(rc))
+    {
+        RTFileClose(File);
+        return VERR_INTERNAL_ERROR;
+    }
+
+    VDDestroy(pVD);
+#undef CHECK
+    return 0;
+}
+
+
+#undef RTDECL
+#define RTDECL(x) static x
+
+/* Start of IPRT code */
+
+/**
+ * The following code is based on the work of George Marsaglia
+ * taken from
+ * http://groups.google.ws/group/comp.sys.sun.admin/msg/7c667186f6cbf354
+ * and
+ * http://groups.google.ws/group/comp.lang.c/msg/0e170777c6e79e8d
+ */
+
+/*
+A C version of a very very good 64-bit RNG is given below.
+You should be able to adapt it to your particular needs.
+
+It is based on the complimentary-multiple-with-carry
+sequence
+         x(n)=a*x(n-4)+carry mod 2^64-1,
+which works as follows:
+Assume a certain multiplier 'a' and a base 'b'.
+Given a current x value and a current carry 'c',
+form:               t=a*x+c
+Then the new carry is     c=floor(t/b)
+and the new x value is    x = b-1-(t mod b).
+
+
+Ordinarily, for 32-bit mwc or cmwc sequences, the
+value t=a*x+c can be formed in 64 bits, then the new c
+is the top and the new x the bottom 32 bits (with a little
+fiddling when b=2^32-1 and cmwc rather than mwc.)
+
+
+To generate 64-bit x's, it is difficult to form
+t=a*x+c in 128 bits then get the new c and new x
+from the top and bottom halves.
+But if 'a' has a special form, for example,
+a=2^62+2^47+2 and b=2^64-1, then the new c and
+the new x can be formed with shifts, tests and +/-'s,
+again with a little fiddling because b=2^64-1 rather
+than 2^64.   (The latter is not an optimal choice because,
+being a square, it cannot be a primitive root of the
+prime a*b^k+1, where 'k' is the 'lag':
+        x(n)=a*x(n-k)+carry mod b.)
+But the multiplier a=2^62+2^47+2 makes a*b^4+1 a prime for
+which b=2^64-1 is a primitive root, and getting  the new x and
+new c  can be done with arithmetic on integers the size of x.
+*/
+
+struct RndCtx
+{
+    uint64_t x;
+    uint64_t y;
+    uint64_t z;
+    uint64_t w;
+    uint64_t c;
+    uint32_t u32x;
+    uint32_t u32y;
+};
+typedef struct RndCtx RNDCTX;
+typedef RNDCTX *PRNDCTX;
+
+/**
+ * Initialize seeds.
+ *
+ * @remarks You should choose ANY 4 random 64-bit
+ * seeds x,y,z,w < 2^64-1 and a random seed c in
+ * 0<= c < a = 2^62+2^47+2.
+ * There are P=(2^62+2^46+2)*(2^64-1)^4 > 2^318 possible choices
+ * for seeds, the period of the RNG.
+ */
+RTDECL(int) RTPRandInit(PRNDCTX pCtx, uint32_t u32Seed)
+{
+    if (u32Seed == 0)
+        u32Seed = (uint32_t)(ASMReadTSC() >> 8);
+    /* Zero is not a good seed. */
+    if (u32Seed == 0)
+        u32Seed = 362436069;
+    pCtx->x = u32Seed;
+    pCtx->y = 17280675555674358941ULL;
+    pCtx->z = 6376492577913983186ULL;
+    pCtx->w = 9064188857900113776ULL;
+    pCtx->c = 123456789;
+    pCtx->u32x = 2282008;
+    pCtx->u32y = u32Seed;
+    return VINF_SUCCESS;
+}
+
+RTDECL(uint32_t) RTPRandGetSeedInfo(PRNDCTX pCtx)
+{
+    return pCtx->u32y;
+}
+
+/**
+ * Generate a 64-bit unsigned random number.
+ *
+ * @returns The pseudo random number.
+ */
+RTDECL(uint64_t) RTPRandU64(PRNDCTX pCtx)
+{
+    uint64_t t;
+    t = (pCtx->x<<47) + (pCtx->x<<62) + (pCtx->x<<1);
+    t += pCtx->c; t+= (t < pCtx->c);
+    pCtx->c = (t<pCtx->c) + (pCtx->x>>17) + (pCtx->x>>2) + (pCtx->x>>63);
+    pCtx->x = pCtx->y;  pCtx->y = pCtx->z ; pCtx->z = pCtx->w;
+    return (pCtx->w = ~(t + pCtx->c)-1);
+}
+
+/**
+ * Generate a 64-bit unsigned pseudo random number in the set
+ * [u64First..u64Last].
+ *
+ * @returns The pseudo random number.
+ * @param   u64First    First number in the set.
+ * @param   u64Last     Last number in the set.
+ */
+RTDECL(uint64_t) RTPRandU64Ex(PRNDCTX pCtx, uint64_t u64First, uint64_t u64Last)
+{
+    if (u64First == 0 && u64Last == UINT64_MAX)
+        return RTPRandU64(pCtx);
+
+    uint64_t u64Tmp;
+    uint64_t u64Range = u64Last - u64First + 1;
+    uint64_t u64Scale = UINT64_MAX / u64Range;
+
+    do
+    {
+        u64Tmp = RTPRandU64(pCtx) / u64Scale;
+    } while (u64Tmp >= u64Range);
+    return u64First + u64Tmp;
+}
+
+/**
+ * Generate a 32-bit unsigned random number.
+ *
+ * @returns The pseudo random number.
+ */
+RTDECL(uint32_t) RTPRandU32(PRNDCTX pCtx)
+{
+    return ( pCtx->u32x = 69069 * pCtx->u32x + 123,
+             pCtx->u32y ^= pCtx->u32y<<13,
+             pCtx->u32y ^= pCtx->u32y>>17,
+             pCtx->u32y ^= pCtx->u32y<<5,
+             pCtx->u32x + pCtx->u32y );
+}
+
+/**
+ * Generate a 32-bit unsigned pseudo random number in the set
+ * [u32First..u32Last].
+ *
+ * @returns The pseudo random number.
+ * @param   u32First    First number in the set.
+ * @param   u32Last     Last number in the set.
+ */
+RTDECL(uint32_t) RTPRandU32Ex(PRNDCTX pCtx, uint32_t u32First, uint32_t u32Last)
+{
+    if (u32First == 0 && u32Last == UINT32_MAX)
+        return RTPRandU32(pCtx);
+
+    uint32_t u32Tmp;
+    uint32_t u32Range = u32Last - u32First + 1;
+    uint32_t u32Scale = UINT32_MAX / u32Range;
+
+    do
+    {
+        u32Tmp = RTPRandU32(pCtx) / u32Scale;
+    } while (u32Tmp >= u32Range);
+    return u32First + u32Tmp;
+}
+
+/* End of IPRT code */
+
+struct Segment
+{
+    uint64_t u64Offset;
+    uint32_t u32Length;
+    uint32_t u8Value;
+};
+typedef struct Segment *PSEGMENT;
+
+static void initializeRandomGenerator(PRNDCTX pCtx, uint32_t u32Seed)
+{
+    int rc = RTPRandInit(pCtx, u32Seed);
+    if (RT_FAILURE(rc))
+        RTPrintf("ERROR: Failed to initialize random generator. RC=%Rrc\n", rc);
+    else
+    {
+        RTPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
+        RTLogPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
+    }
+}
+
+static int compareSegments(const void *left, const void *right)
+{
+    /* Note that no duplicates are allowed in the array being sorted. */
+    return ((PSEGMENT)left)->u64Offset < ((PSEGMENT)right)->u64Offset ? -1 : 1;
+}
+
+static void generateRandomSegments(PRNDCTX pCtx, PSEGMENT pSegment, uint32_t nSegments, uint32_t u32MaxSegmentSize, uint64_t u64DiskSize, uint32_t u32SectorSize, uint8_t u8ValueLow, uint8_t u8ValueHigh)
+{
+    uint32_t i;
+    /* Generate segment offsets. */
+    for (i = 0; i < nSegments; i++)
+    {
+        bool fDuplicateFound;
+        do
+        {
+            pSegment[i].u64Offset = RTPRandU64Ex(pCtx, 0, u64DiskSize / u32SectorSize - 1) * u32SectorSize;
+            fDuplicateFound = false;
+            for (uint32_t j = 0; j < i; j++)
+                if (pSegment[i].u64Offset == pSegment[j].u64Offset)
+                {
+                    fDuplicateFound = true;
+                    break;
+                }
+        } while (fDuplicateFound);
+    }
+    /* Sort in offset-ascending order. */
+    qsort(pSegment, nSegments, sizeof(*pSegment), compareSegments);
+    /* Put a sentinel at the end. */
+    pSegment[nSegments].u64Offset = u64DiskSize;
+    pSegment[nSegments].u32Length = 0;
+    /* Generate segment lengths and values. */
+    for (i = 0; i < nSegments; i++)
+    {
+        pSegment[i].u32Length = RTPRandU32Ex(pCtx, 1, RT_MIN(pSegment[i+1].u64Offset - pSegment[i].u64Offset,
+                                                             u32MaxSegmentSize) / u32SectorSize) * u32SectorSize;
+        Assert(pSegment[i].u32Length <= u32MaxSegmentSize);
+        pSegment[i].u8Value  = RTPRandU32Ex(pCtx, (uint32_t)u8ValueLow, (uint32_t)u8ValueHigh);
+    }
+}
+
+static void mergeSegments(PSEGMENT pBaseSegment, PSEGMENT pDiffSegment, PSEGMENT pMergeSegment, uint32_t u32MaxLength)
+{
+    while (pBaseSegment->u32Length > 0 || pDiffSegment->u32Length > 0)
+    {
+        if (pBaseSegment->u64Offset < pDiffSegment->u64Offset)
+        {
+            *pMergeSegment = *pBaseSegment;
+            if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pDiffSegment->u64Offset)
+                pBaseSegment++;
+            else
+            {
+                pMergeSegment->u32Length = pDiffSegment->u64Offset - pMergeSegment->u64Offset;
+                Assert(pMergeSegment->u32Length <= u32MaxLength);
+                if (pBaseSegment->u64Offset + pBaseSegment->u32Length >
+                    pDiffSegment->u64Offset + pDiffSegment->u32Length)
+                {
+                    pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
+                    Assert(pBaseSegment->u32Length <= u32MaxLength);
+                    pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
+                }
+                else
+                    pBaseSegment++;
+            }
+            pMergeSegment++;
+        }
+        else
+        {
+            *pMergeSegment = *pDiffSegment;
+            if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pBaseSegment->u64Offset)
+            {
+                pDiffSegment++;
+                pMergeSegment++;
+            }
+            else
+            {
+                if (pBaseSegment->u64Offset + pBaseSegment->u32Length > pDiffSegment->u64Offset + pDiffSegment->u32Length)
+                {
+                    pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
+                    Assert(pBaseSegment->u32Length <= u32MaxLength);
+                    pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
+                    pDiffSegment++;
+                    pMergeSegment++;
+                }
+                else
+                    pBaseSegment++;
+            }
+        }
+    }
+}
+
+static void writeSegmentsToDisk(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
+{
+    while (pSegment->u32Length)
+    {
+        //memset((uint8_t*)pvBuf + pSegment->u64Offset, pSegment->u8Value, pSegment->u32Length);
+        memset(pvBuf, pSegment->u8Value, pSegment->u32Length);
+        VDWrite(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
+        pSegment++;
+    }
+}
+
+static int readAndCompareSegments(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
+{
+    while (pSegment->u32Length)
+    {
+        int rc = VDRead(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
+        if (RT_FAILURE(rc))
+        {
+            RTPrintf("ERROR: Failed to read from virtual disk\n");
+            return rc;
+        }
+        else
+        {
+            for (unsigned i = 0; i < pSegment->u32Length; i++)
+                if (((uint8_t*)pvBuf)[i] != pSegment->u8Value)
+                {
+                    RTPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
+                             pSegment->u64Offset, pSegment->u32Length, i, ((uint8_t*)pvBuf)[i],
+                             pSegment->u8Value);
+                    RTLogPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
+                             pSegment->u64Offset, pSegment->u32Length, i, ((uint8_t*)pvBuf)[i],
+                             pSegment->u8Value);
+                    return VERR_INTERNAL_ERROR;
+                }
+        }
+        pSegment++;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static int tstVDOpenCreateWriteMerge(const char *pszBackend,
+                                     const char *pszBaseFilename,
+                                     const char *pszDiffFilename,
+                                     uint32_t u32Seed)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    char *pszFormat;
+    VDTYPE enmType = VDTYPE_INVALID;
+    VDGEOMETRY PCHS = { 0, 0, 0 };
+    VDGEOMETRY LCHS = { 0, 0, 0 };
+    uint64_t u64DiskSize = 1000 * _1M;
+    uint32_t u32SectorSize = 512;
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            if (pvBuf) \
+                RTMemFree(pvBuf); \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    void *pvBuf = RTMemAlloc(_1M);
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    RTFILE File;
+    rc = RTFileOpen(&File, pszBaseFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(rc))
+    {
+        RTFileClose(File);
+        rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+                         pszBaseFilename, &pszFormat, &enmType);
+        RTPrintf("VDGetFormat() pszFormat=%s rc=%Rrc\n", pszFormat, rc);
+        if (RT_SUCCESS(rc) && strcmp(pszFormat, pszBackend))
+        {
+            rc = VERR_GENERAL_FAILURE;
+            RTPrintf("VDGetFormat() returned incorrect backend name\n");
+        }
+        RTStrFree(pszFormat);
+        CHECK("VDGetFormat()");
+
+        rc = VDOpen(pVD, pszBackend, pszBaseFilename, VD_OPEN_FLAGS_NORMAL,
+                    NULL);
+        CHECK("VDOpen()");
+    }
+    else
+    {
+        rc = VDCreateBase(pVD, pszBackend, pszBaseFilename, u64DiskSize,
+                          VD_IMAGE_FLAGS_NONE, "Test image",
+                          &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
+                          NULL, NULL);
+        CHECK("VDCreateBase()");
+    }
+
+    int nSegments = 100;
+    /* Allocate one extra element for a sentinel. */
+    PSEGMENT paBaseSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
+    PSEGMENT paDiffSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
+    PSEGMENT paMergeSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1) * 3);
+
+    RNDCTX ctx;
+    initializeRandomGenerator(&ctx, u32Seed);
+    generateRandomSegments(&ctx, paBaseSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
+    generateRandomSegments(&ctx, paDiffSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 128u, 255u);
+
+    /*PSEGMENT pSegment;
+    RTPrintf("Base segments:\n");
+    for (pSegment = paBaseSegments; pSegment->u32Length; pSegment++)
+        RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
+    writeSegmentsToDisk(pVD, pvBuf, paBaseSegments);
+
+    rc = VDCreateDiff(pVD, pszBackend, pszDiffFilename,
+                      VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
+                      VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+    CHECK("VDCreateDiff()");
+
+    /*RTPrintf("\nDiff segments:\n");
+    for (pSegment = paDiffSegments; pSegment->u32Length; pSegment++)
+        RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
+    writeSegmentsToDisk(pVD, pvBuf, paDiffSegments);
+
+    VDDumpImages(pVD);
+
+    RTPrintf("Merging diff into base..\n");
+    rc = VDMerge(pVD, VD_LAST_IMAGE, 0, NULL);
+    CHECK("VDMerge()");
+
+    mergeSegments(paBaseSegments, paDiffSegments, paMergeSegments, _1M);
+    /*RTPrintf("\nMerged segments:\n");
+    for (pSegment = paMergeSegments; pSegment->u32Length; pSegment++)
+        RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
+    rc = readAndCompareSegments(pVD, pvBuf, paMergeSegments);
+    CHECK("readAndCompareSegments()");
+
+    RTMemFree(paMergeSegments);
+    RTMemFree(paDiffSegments);
+    RTMemFree(paBaseSegments);
+
+    VDDumpImages(pVD);
+
+    VDDestroy(pVD);
+    if (pvBuf)
+        RTMemFree(pvBuf);
+#undef CHECK
+    return 0;
+}
+
+static int tstVDCreateWriteOpenRead(const char *pszBackend,
+                                    const char *pszFilename,
+                                    uint32_t u32Seed)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    VDGEOMETRY PCHS = { 0, 0, 0 };
+    VDGEOMETRY LCHS = { 0, 0, 0 };
+    uint64_t u64DiskSize = 1000 * _1M;
+    uint32_t u32SectorSize = 512;
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            if (pvBuf) \
+                RTMemFree(pvBuf); \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    void *pvBuf = RTMemAlloc(_1M);
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    RTFILE File;
+    rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(rc))
+    {
+        RTFileClose(File);
+        RTFileDelete(pszFilename);
+    }
+
+    rc = VDCreateBase(pVD, pszBackend, pszFilename, u64DiskSize,
+                      VD_IMAGE_FLAGS_NONE, "Test image",
+                      &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
+                      NULL, NULL);
+    CHECK("VDCreateBase()");
+
+    int nSegments = 100;
+    /* Allocate one extra element for a sentinel. */
+    PSEGMENT paSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
+
+    RNDCTX ctx;
+    initializeRandomGenerator(&ctx, u32Seed);
+    generateRandomSegments(&ctx, paSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
+    /*for (PSEGMENT pSegment = paSegments; pSegment->u32Length; pSegment++)
+        RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
+
+    writeSegmentsToDisk(pVD, pvBuf, paSegments);
+
+    VDCloseAll(pVD);
+
+    rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen()");
+    rc = readAndCompareSegments(pVD, pvBuf, paSegments);
+    CHECK("readAndCompareSegments()");
+
+    RTMemFree(paSegments);
+
+    VDDestroy(pVD);
+    if (pvBuf)
+        RTMemFree(pvBuf);
+#undef CHECK
+    return 0;
+}
+
+static int tstVmdkRename(const char *src, const char *dst)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    rc = VDOpen(pVD, "VMDK", src, VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen()");
+    rc = VDCopy(pVD, 0, pVD, "VMDK", dst, true, 0, VD_IMAGE_FLAGS_NONE, NULL,
+                VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
+    CHECK("VDCopy()");
+
+    VDDestroy(pVD);
+#undef CHECK
+    return 0;
+}
+
+static int tstVmdkCreateRenameOpen(const char *src, const char *dst,
+                                   uint64_t cbSize, unsigned uFlags)
+{
+    int rc = tstVDCreateDelete("VMDK", src, cbSize, uFlags, false);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = tstVmdkRename(src, dst);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    PVBOXHDD pVD = NULL;
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDCloseAll(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    rc = VDOpen(pVD, "VMDK", dst, VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen()");
+
+    VDClose(pVD, true);
+    CHECK("VDClose()");
+    VDDestroy(pVD);
+#undef CHECK
+    return rc;
+}
+
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+#define DST_PATH "tmp\\tmpVDRename.vmdk"
+#else
+#define DST_PATH "tmp/tmpVDRename.vmdk"
+#endif
+
+static void tstVmdk()
+{
+    int rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
+                                     VD_IMAGE_FLAGS_NONE);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, same dir) test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
+                                 VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, same dir) test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
+                                 VD_IMAGE_FLAGS_NONE);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, another dir) test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
+                                 VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir) test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+
+    RTFILE File;
+    rc = RTFileOpen(&File, DST_PATH, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(rc))
+        RTFileClose(File);
+
+    rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
+                                 VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
+    if (RT_SUCCESS(rc))
+    {
+        RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir, already exists) test failed!\n");
+        g_cErrors++;
+    }
+    RTFileDelete(DST_PATH);
+    RTFileDelete("tmpVDCreate.vmdk");
+    RTFileDelete("tmpVDCreate-s001.vmdk");
+    RTFileDelete("tmpVDCreate-s002.vmdk");
+    RTFileDelete("tmpVDCreate-s003.vmdk");
+}
+
+int main(int argc, char *argv[])
+{
+    RTR3Init();
+    int rc;
+
+    uint32_t u32Seed = 0; // Means choose random
+
+    if (argc > 1)
+        if (sscanf(argv[1], "%x", &u32Seed) != 1)
+        {
+            RTPrintf("ERROR: Invalid parameter %s. Valid usage is %s <32-bit seed>.\n",
+                     argv[1], argv[0]);
+            return 1;
+        }
+
+    RTPrintf("tstVD: TESTING...\n");
+
+    /*
+     * Clean up potential leftovers from previous unsuccessful runs.
+     */
+    RTFileDelete("tmpVDCreate.vdi");
+    RTFileDelete("tmpVDCreate.vmdk");
+    RTFileDelete("tmpVDCreate.vhd");
+    RTFileDelete("tmpVDBase.vdi");
+    RTFileDelete("tmpVDDiff.vdi");
+    RTFileDelete("tmpVDBase.vmdk");
+    RTFileDelete("tmpVDDiff.vmdk");
+    RTFileDelete("tmpVDBase.vhd");
+    RTFileDelete("tmpVDDiff.vhd");
+    RTFileDelete("tmpVDCreate-s001.vmdk");
+    RTFileDelete("tmpVDCreate-s002.vmdk");
+    RTFileDelete("tmpVDCreate-s003.vmdk");
+    RTFileDelete("tmpVDRename.vmdk");
+    RTFileDelete("tmpVDRename-s001.vmdk");
+    RTFileDelete("tmpVDRename-s002.vmdk");
+    RTFileDelete("tmpVDRename-s003.vmdk");
+    RTFileDelete("tmp/tmpVDRename.vmdk");
+    RTFileDelete("tmp/tmpVDRename-s001.vmdk");
+    RTFileDelete("tmp/tmpVDRename-s002.vmdk");
+    RTFileDelete("tmp/tmpVDRename-s003.vmdk");
+
+    if (!RTDirExists("tmp"))
+    {
+        rc = RTDirCreate("tmp", RTFS_UNIX_IRWXU);
+        if (RT_FAILURE(rc))
+        {
+            RTPrintf("tstVD: Failed to create 'tmp' directory! rc=%Rrc\n", rc);
+            g_cErrors++;
+        }
+    }
+
+#ifdef VMDK_TEST
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, false);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDOpenDelete("VMDK", "tmpVDCreate.vmdk");
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK delete test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+
+    tstVmdk();
+#endif /* VMDK_TEST */
+#ifdef VDI_TEST
+    rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic VDI create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: fixed VDI create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VDI_TEST */
+#ifdef VMDK_TEST
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_VMDK_IMAGE_FLAGS_SPLIT_2G, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic split VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_IMAGE_FLAGS_FIXED, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: fixed VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
+                           VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_SPLIT_2G,
+                           true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: fixed split VMDK create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VMDK_TEST */
+#ifdef VHD_TEST
+    rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
+                           VD_IMAGE_FLAGS_NONE, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: dynamic VHD create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
+                           VD_IMAGE_FLAGS_FIXED, true);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: fixed VHD create test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VHD_TEST */
+#ifdef VDI_TEST
+    rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VDI test failed (new image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VDI test failed (existing image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VDI_TEST */
+#ifdef VMDK_TEST
+    rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK test failed (new image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VMDK test failed (existing image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VMDK_TEST */
+#ifdef VHD_TEST
+    rc = tstVDCreateWriteOpenRead("VHD", "tmpVDCreate.vhd", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VHD test failed (creating image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+
+    rc = tstVDOpenCreateWriteMerge("VHD", "tmpVDBase.vhd", "tmpVDDiff.vhd", u32Seed);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VHD test failed (existing image)! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VHD_TEST */
+
+    /*
+     * Clean up any leftovers.
+     */
+    RTFileDelete("tmpVDCreate.vdi");
+    RTFileDelete("tmpVDCreate.vmdk");
+    RTFileDelete("tmpVDCreate.vhd");
+    RTFileDelete("tmpVDBase.vdi");
+    RTFileDelete("tmpVDDiff.vdi");
+    RTFileDelete("tmpVDBase.vmdk");
+    RTFileDelete("tmpVDDiff.vmdk");
+    RTFileDelete("tmpVDBase.vhd");
+    RTFileDelete("tmpVDDiff.vhd");
+    RTFileDelete("tmpVDCreate-s001.vmdk");
+    RTFileDelete("tmpVDCreate-s002.vmdk");
+    RTFileDelete("tmpVDCreate-s003.vmdk");
+    RTFileDelete("tmpVDRename.vmdk");
+    RTFileDelete("tmpVDRename-s001.vmdk");
+    RTFileDelete("tmpVDRename-s002.vmdk");
+    RTFileDelete("tmpVDRename-s003.vmdk");
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: unloading backends failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+     /*
+     * Summary
+     */
+    if (!g_cErrors)
+        RTPrintf("tstVD: SUCCESS\n");
+    else
+        RTPrintf("tstVD: FAILURE - %d errors\n", g_cErrors);
+
+    return !!g_cErrors;
+}
+
Index: /trunk/src/VBox/Storage/testcase/tstVDCopy.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVDCopy.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVDCopy.cpp	(revision 33567)
@@ -0,0 +1,193 @@
+/** @file
+ *
+ * Simple VBox HDD container test utility. Compares two images and prints
+ * differences. Mainly used to check cloning of disk images.
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <VBox/err.h>
+#include <VBox/vd.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/rand.h>
+#include "stdio.h"
+#include "stdlib.h"
+
+/* from VBoxHDD.cpp */
+#define VD_MERGE_BUFFER_SIZE    (16 * _1M)
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The error count. */
+unsigned g_cErrors = 0;
+
+
+static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
+                       const char *pszFormat, va_list va)
+{
+    g_cErrors++;
+    RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
+    RTPrintfV(pszFormat, va);
+    RTPrintf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+    int rc;
+
+    RTR3Init();
+
+    if (argc != 3)
+    {
+        RTPrintf("Usage: ./tstVDCopy <hdd1> <hdd2>\n");
+        return 1;
+    }
+
+    RTPrintf("tstVDCopy: TESTING...\n");
+
+    PVBOXHDD         pVD1 = NULL;
+    PVBOXHDD         pVD2 = NULL;
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+    char *pszVD1 = NULL;
+    char *pszVD2 = NULL;
+    char *pbBuf1 = NULL;
+    char *pbBuf2 = NULL;
+    VDTYPE enmTypeVD1 = VDTYPE_INVALID;
+    VDTYPE enmTypeVD2 = VDTYPE_INVALID;
+
+#define CHECK(str) \
+    do \
+    { \
+        if (RT_FAILURE(rc)) \
+        { \
+            RTPrintf("%s rc=%Rrc\n", str, rc); \
+            if (pVD1) \
+                VDCloseAll(pVD1); \
+            if (pVD2) \
+                VDCloseAll(pVD2); \
+            return rc; \
+        } \
+    } while (0)
+
+    pbBuf1 = (char *)RTMemAllocZ(VD_MERGE_BUFFER_SIZE);
+    pbBuf2 = (char *)RTMemAllocZ(VD_MERGE_BUFFER_SIZE);
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+                     argv[1], &pszVD1, &enmTypeVD1);
+    CHECK("VDGetFormat() hdd1");
+
+    rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
+                     argv[2], &pszVD2, &enmTypeVD2);
+    CHECK("VDGetFormat() hdd2");
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD1);
+    CHECK("VDCreate() hdd1");
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD2);
+    CHECK("VDCreate() hdd1");
+
+    rc = VDOpen(pVD1, pszVD1, argv[1], VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen() hdd1");
+
+    rc = VDOpen(pVD2, pszVD2, argv[2], VD_OPEN_FLAGS_NORMAL, NULL);
+    CHECK("VDOpen() hdd2");
+
+    uint64_t cbSize1 = 0;
+    uint64_t cbSize2 = 0;
+
+    cbSize1 = VDGetSize(pVD1, 0);
+    Assert(cbSize1 != 0);
+    cbSize2 = VDGetSize(pVD1, 0);
+    Assert(cbSize1 != 0);
+
+    if (cbSize1 == cbSize2)
+    {
+        uint64_t uOffCurr = 0;
+
+        /* Compare block by block. */
+        while (uOffCurr < cbSize1)
+        {
+            size_t cbRead = RT_MIN((cbSize1 - uOffCurr), VD_MERGE_BUFFER_SIZE);
+
+            rc = VDRead(pVD1, uOffCurr, pbBuf1, cbRead);
+            CHECK("VDRead() hdd1");
+
+            rc = VDRead(pVD2, uOffCurr, pbBuf2, cbRead);
+            CHECK("VDRead() hdd2");
+
+            if (memcmp(pbBuf1, pbBuf2, cbRead))
+            {
+                RTPrintf("tstVDCopy: Images differ uOffCurr=%llu\n", uOffCurr);
+                /* Do byte by byte comparison. */
+                for (size_t i = 0; i < cbRead; i++)
+                {
+                    if (pbBuf1[i] != pbBuf2[i])
+                    {
+                        RTPrintf("tstVDCopy: First different byte is at offset %llu\n", uOffCurr + i);
+                        break;
+                    }
+                }
+                break;
+            }
+
+            uOffCurr += cbRead;
+        }
+    }
+    else
+        RTPrintf("tstVDCopy: Images have different size hdd1=%llu hdd2=%llu\n", cbSize1, cbSize2);
+
+    VDClose(pVD1, false);
+    CHECK("VDClose() hdd1");
+
+    VDClose(pVD2, false);
+    CHECK("VDClose() hdd2");
+
+    VDDestroy(pVD1);
+    VDDestroy(pVD2);
+    RTMemFree(pbBuf1);
+    RTMemFree(pbBuf2);
+#undef CHECK
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVDCopy: unloading backends failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+    /*
+     * Summary
+     */
+    if (!g_cErrors)
+        RTPrintf("tstVDCopy: SUCCESS\n");
+    else
+        RTPrintf("tstVDCopy: FAILURE - %d errors\n", g_cErrors);
+
+    return !!g_cErrors;
+}
+
Index: /trunk/src/VBox/Storage/testcase/tstVDI.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVDI.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVDI.cpp	(revision 33567)
@@ -0,0 +1,117 @@
+/** @file
+ *
+ * Simple VBox HDD container test utility.
+ */
+
+/*
+ * Copyright (C) 2006-2007 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <VBox/err.h>
+#include <VBox/VBoxHDD.h>
+#include <iprt/file.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/mem.h>
+
+
+int dotest(const char *pszBaseFilename, const char *pszDiffFilename)
+{
+    PVDIDISK pVdi = VDIDiskCreate();
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDIDiskCloseAllImages(pVdi); \
+            return rc; \
+        } \
+    } while (0)
+
+
+    int rc = VDIDiskOpenImage(pVdi, pszBaseFilename, VDI_OPEN_FLAGS_NORMAL);
+    RTPrintf("openImage() rc=%Rrc\n", rc);
+    if (RT_FAILURE(rc))
+    {
+        rc = VDICreateBaseImage(pszBaseFilename, VDI_IMAGE_TYPE_NORMAL,
+#ifdef _MSC_VER
+                                (1000 * 1024 * 1024UI64),
+#else
+                                (1000 * 1024 * 1024ULL),
+#endif
+                                "Test image", NULL, NULL);
+        CHECK("createImage()");
+
+        rc = VDIDiskOpenImage(pVdi, pszBaseFilename, VDI_OPEN_FLAGS_NORMAL);
+        CHECK("openImage()");
+    }
+
+    void *pvBuf = RTMemAlloc(1*1124*1024);
+
+    memset(pvBuf, 0x33, 1*1124*1024);
+    rc = VDIDiskWrite(pVdi, 20*1024*1024 + 594040, pvBuf, 1024*1024);
+    CHECK("write()");
+
+    memset(pvBuf, 0x46, 1*1124*1024);
+    rc = VDIDiskWrite(pVdi, 20*1024*1024 + 594040, pvBuf, 1024);
+    CHECK("write()");
+
+    memset(pvBuf, 0x51, 1*1124*1024);
+    rc = VDIDiskWrite(pVdi, 40*1024*1024 + 594040, pvBuf, 1024);
+    CHECK("write()");
+
+    rc = VDIDiskCreateOpenDifferenceImage(pVdi, pszDiffFilename, "Test diff image", NULL, NULL);
+    CHECK("create undo");
+//    rc = VHDDOpenSecondImage(pVdi, "undoimg.vdi");
+//    RTPrintf("open undo rc=%Rrc\n", rc);
+
+    memset(pvBuf, '_', 1*1124*1024);
+    rc = VDIDiskWrite(pVdi, 20*1024*1024 + 594040, pvBuf, 512);
+    CHECK("write()");
+
+    rc = VDIDiskWrite(pVdi, 22*1024*1024 + 594040, pvBuf, 78263);
+    CHECK("write()");
+    rc = VDIDiskWrite(pVdi, 13*1024*1024 + 594040, pvBuf, 782630);
+    CHECK("write()");
+    rc = VDIDiskWrite(pVdi, 44*1024*1024 + 594040, pvBuf, 67899);
+    CHECK("write()");
+
+    RTPrintf("committing..\n");
+    VDIDiskDumpImages(pVdi);
+    rc = VDIDiskCommitLastDiff(pVdi, NULL, NULL);
+    CHECK("commit last diff");
+    VDIDiskCloseAllImages(pVdi);
+#undef CHECK
+    return 0;
+}
+
+
+int main()
+{
+    RTR3Init();
+
+    RTFileDelete("tmpVdiBase.vdi");
+    RTFileDelete("tmpVdiDiff.vdi");
+
+    int rc = dotest("tmpVdiBase.vdi", "tmpVdiDiff.vdi");
+    if (!rc)
+        RTPrintf("tstVDI: SUCCESS\n");
+    else
+        RTPrintf("tstVDI: FAILURE\n");
+
+    RTFileDelete("tmpVdiBase.vdi");
+    RTFileDelete("tmpVdiDiff.vdi");
+    return !!rc;
+}
+
Index: /trunk/src/VBox/Storage/testcase/tstVDShareable.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVDShareable.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVDShareable.cpp	(revision 33567)
@@ -0,0 +1,175 @@
+/* $Id$ */
+/** @file
+ * Simple VBox HDD container test utility for shareable images.
+ */
+
+/*
+ * Copyright (C) 2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <VBox/vd.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/dir.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/rand.h>
+#include "stdio.h"
+#include "stdlib.h"
+
+#define VHD_TEST
+#define VDI_TEST
+#define VMDK_TEST
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The error count. */
+unsigned g_cErrors = 0;
+
+
+static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
+                       const char *pszFormat, va_list va)
+{
+    g_cErrors++;
+    RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
+    RTPrintfV(pszFormat, va);
+    RTPrintf("\n");
+}
+
+static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+    RTPrintf("tstVD: ");
+    RTPrintfV(pszFormat, va);
+    return VINF_SUCCESS;
+}
+
+static int tstVDCreateShareDelete(const char *pszBackend, const char *pszFilename,
+                                  uint64_t cbSize, unsigned uFlags)
+{
+    int rc;
+    PVBOXHDD pVD = NULL, pVD2 = NULL;
+    VDGEOMETRY       PCHS = { 0, 0, 0 };
+    VDGEOMETRY       LCHS = { 0, 0, 0 };
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+    rc = VDCreate(&VDIError, VDTYPE_HDD, &pVD2);
+    CHECK("VDCreate() #2");
+
+    rc = VDCreateBase(pVD, pszBackend, pszFilename, cbSize,
+                      uFlags, "Test image", &PCHS, &LCHS, NULL,
+                      VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+    CHECK("VDCreateBase()");
+
+    VDClose(pVD, false);
+
+    rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_SHAREABLE, NULL);
+    CHECK("VDOpen()");
+    rc = VDOpen(pVD2, pszBackend, pszFilename, VD_OPEN_FLAGS_SHAREABLE, NULL);
+    CHECK("VDOpen() #2");
+    if (VDIsReadOnly(pVD2))
+        rc = VERR_VD_IMAGE_READ_ONLY;
+
+    VDClose(pVD2, false);
+    VDClose(pVD, true);
+
+    VDDestroy(pVD);
+    VDDestroy(pVD2);
+#undef CHECK
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    RTR3Init();
+    int rc;
+
+    RTPrintf("tstVD: TESTING...\n");
+
+    /*
+     * Clean up potential leftovers from previous unsuccessful runs.
+     */
+    RTFileDelete("tmpVDCreate.vdi");
+
+    if (!RTDirExists("tmp"))
+    {
+        rc = RTDirCreate("tmp", RTFS_UNIX_IRWXU);
+        if (RT_FAILURE(rc))
+        {
+            RTPrintf("tstVD: Failed to create 'tmp' directory! rc=%Rrc\n", rc);
+            g_cErrors++;
+        }
+    }
+
+#ifdef VDI_TEST
+    rc = tstVDCreateShareDelete("VDI", "tmpVDCreate.vdi", 10 * _1M,
+                                VD_IMAGE_FLAGS_FIXED);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: VDI shareable test failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+#endif /* VDI_TEST */
+
+    /*
+     * Clean up any leftovers.
+     */
+    RTFileDelete("tmpVDCreate.vdi");
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVD: unloading backends failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+     /*
+      * Summary
+      */
+    if (!g_cErrors)
+        RTPrintf("tstVD: SUCCESS\n");
+    else
+        RTPrintf("tstVD: FAILURE - %d errors\n", g_cErrors);
+
+    return !!g_cErrors;
+}
+
Index: /trunk/src/VBox/Storage/testcase/tstVDSnap.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/tstVDSnap.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/tstVDSnap.cpp	(revision 33567)
@@ -0,0 +1,432 @@
+/** @file
+ *
+ * Snapshot VBox HDD container test utility.
+ */
+
+/*
+ * Copyright (C) 2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <VBox/vd.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/dir.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/initterm.h>
+#include <iprt/rand.h>
+
+/**
+ * A VD snapshot test.
+ */
+typedef struct VDSNAPTEST
+{
+    /** Backend to use */
+    const char *pcszBackend;
+    /** Base image name */
+    const char *pcszBaseImage;
+    /** Diff image ending */
+    const char *pcszDiffSuff;
+    /** Number of iterations before the test exits */
+    uint32_t    cIterations;
+    /** Test pattern size */
+    size_t      cbTestPattern;
+    /** Minimum number of disk segments */
+    uint32_t    cDiskSegsMin;
+    /** Miaximum number of disk segments */
+    uint32_t    cDiskSegsMax;
+    /** Minimum number of diffs needed before a merge
+     * operation can occur */
+    unsigned    cDiffsMinBeforeMerge;
+    /** Chance to get create instead of a merge operation */
+    uint32_t    uCreateDiffChance;
+    /** Chance to change a segment after a diff was created */
+    uint32_t    uChangeSegChance;
+    /** Numer of allocated blocks in the base image in percent */
+    uint32_t    uAllocatedBlocks;
+    /** Merge direction */
+    bool        fForward;
+} VDSNAPTEST, *PVDSNAPTEST;
+
+/**
+ * Structure defining a disk segment.
+ */
+typedef struct VDDISKSEG
+{
+    /** Start offset in the disk. */
+    uint64_t                   off;
+    /** Size of the segment. */
+    uint64_t                   cbSeg;
+    /** Pointer to the start of the data in the test pattern used for the segment. */
+    uint8_t                   *pbData;
+    /** Pointer to the data for a diff write */
+    uint8_t                   *pbDataDiff;
+} VDDISKSEG, *PVDDISKSEG;
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** The error count. */
+unsigned g_cErrors = 0;
+/** Global RNG state. */
+RTRAND   g_hRand;
+
+static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
+                       const char *pszFormat, va_list va)
+{
+    g_cErrors++;
+    RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
+    RTPrintfV(pszFormat, va);
+    RTPrintf("\n");
+}
+
+static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+    RTPrintf("tstVD: ");
+    RTPrintfV(pszFormat, va);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Returns true with the given chance in percent.
+ *
+ * @returns true or false
+ * @param   iPercentage   The percentage of the chance to return true.
+ */
+static bool tstVDSnapIsTrue(int iPercentage)
+{
+    int uRnd = RTRandAdvU32Ex(g_hRand, 0, 100);
+
+    return (uRnd <= iPercentage); /* This should be enough for our purpose */
+}
+
+static void tstVDSnapSegmentsDice(PVDSNAPTEST pTest, PVDDISKSEG paDiskSeg, uint32_t cDiskSegments,
+                                  uint8_t *pbTestPattern, size_t cbTestPattern)
+{
+    for (uint32_t i = 0; i < cDiskSegments; i++)
+    {
+        /* Do we want to change the current segment? */
+        if (tstVDSnapIsTrue(pTest->uChangeSegChance))
+            paDiskSeg[i].pbDataDiff = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, cbTestPattern - paDiskSeg[i].cbSeg - 512), 512);
+    }
+}
+
+static int tstVDSnapWrite(PVBOXHDD pVD, PVDDISKSEG paDiskSegments,
+                          uint32_t cDiskSegments, uint64_t cbDisk, bool fInit)
+{
+    int rc = VINF_SUCCESS;
+
+    for (uint32_t i = 0; i < cDiskSegments; i++)
+    {
+        if (fInit || paDiskSegments[i].pbDataDiff)
+        {
+            size_t cbWrite  = paDiskSegments[i].cbSeg;
+            uint64_t off    = paDiskSegments[i].off;
+            uint8_t *pbData =   fInit
+                              ? paDiskSegments[i].pbData
+                              : paDiskSegments[i].pbDataDiff;
+
+            if (pbData)
+            {
+                rc = VDWrite(pVD, off, pbData, cbWrite);
+                if (RT_FAILURE(rc))
+                    return rc;
+            }
+        }
+    }
+
+    return rc;
+}
+
+static int tstVDSnapReadVerify(PVBOXHDD pVD, PVDDISKSEG paDiskSegments, uint32_t cDiskSegments, uint64_t cbDisk)
+{
+    int rc = VINF_SUCCESS;
+    uint8_t *pbBuf = (uint8_t *)RTMemAlloc(_1M);
+
+    for (uint32_t i = 0; i < cDiskSegments; i++)
+    {
+        size_t cbRead  = paDiskSegments[i].cbSeg;
+        uint64_t off   = paDiskSegments[i].off;
+        uint8_t *pbCmp = paDiskSegments[i].pbData;
+
+        Assert(!paDiskSegments[i].pbDataDiff);
+
+        while (cbRead)
+        {
+            size_t cbToRead = RT_MIN(cbRead, _1M);
+
+            rc = VDRead(pVD, off, pbBuf, cbToRead);
+            if (RT_FAILURE(rc))
+                return rc;
+
+            if (pbCmp)
+            {
+                if (memcmp(pbCmp, pbBuf, cbToRead))
+                {
+                    for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++)
+                    {
+                        if (pbCmp[iCmp] != pbBuf[iCmp])
+                        {
+                            RTPrintf("Unexpected data at %llu expected %#x got %#x\n", off+iCmp, pbCmp[iCmp], pbBuf[iCmp]);
+                            break;
+                        }
+                    }
+                    return VERR_INTERNAL_ERROR;
+                }
+            }
+            else
+            {
+                /* Verify that the block is 0 */
+                for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++)
+                {
+                    if (pbBuf[iCmp] != 0)
+                    {
+                        RTPrintf("Zero block contains data at %llu\n", off+iCmp);
+                        return VERR_INTERNAL_ERROR;
+                    }
+                }
+            }
+
+            cbRead -= cbToRead;
+            off    += cbToRead;
+
+            if (pbCmp)
+                pbCmp  += cbToRead;
+        }
+    }
+
+    RTMemFree(pbBuf);
+
+    return rc;
+}
+
+static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest)
+{
+    int rc;
+    PVBOXHDD pVD = NULL;
+    VDGEOMETRY       PCHS = { 0, 0, 0 };
+    VDGEOMETRY       LCHS = { 0, 0, 0 };
+    PVDINTERFACE     pVDIfs = NULL;
+    VDINTERFACE      VDIError;
+    VDINTERFACEERROR VDIErrorCallbacks;
+    /** Buffer storing the random test pattern. */
+    uint8_t *pbTestPattern = NULL;
+    /** Number of disk segments */
+    uint32_t cDiskSegments;
+    /** Array of disk segments */
+    PVDDISKSEG paDiskSeg = NULL;
+    unsigned   cDiffs = 0;
+    unsigned   idDiff = 0; /* Diff ID counter for the filename */
+
+    /* Create the virtual disk test data */
+    pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern);
+
+    RTRandAdvBytes(g_hRand, pbTestPattern, pTest->cbTestPattern);
+    cDiskSegments = RTRandAdvU32Ex(g_hRand, pTest->cDiskSegsMin, pTest->cDiskSegsMax);
+
+    uint64_t cbDisk = 0;
+
+    paDiskSeg = (PVDDISKSEG)RTMemAllocZ(cDiskSegments * sizeof(VDDISKSEG));
+    for (unsigned i = 0; i < cDiskSegments; i++)
+    {
+        paDiskSeg[i].off    = cbDisk;
+        paDiskSeg[i].cbSeg  = RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 512, pTest->cbTestPattern), 512);
+        if (tstVDSnapIsTrue(pTest->uAllocatedBlocks))
+            paDiskSeg[i].pbData = pbTestPattern + RT_ALIGN_64(RTRandAdvU64Ex(g_hRand, 0, pTest->cbTestPattern - paDiskSeg[i].cbSeg - 512), 512);
+        else
+            paDiskSeg[i].pbData = NULL; /* Not allocated initially */
+        cbDisk += paDiskSeg[i].cbSeg;
+    }
+
+    RTPrintf("Disk size is %llu bytes\n", cbDisk);
+
+#define CHECK(str) \
+    do \
+    { \
+        RTPrintf("%s rc=%Rrc\n", str, rc); \
+        if (RT_FAILURE(rc)) \
+        { \
+            if (pbTestPattern) \
+                RTMemFree(pbTestPattern); \
+            VDDestroy(pVD); \
+            return rc; \
+        } \
+    } while (0)
+
+    /* Create error interface. */
+    VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
+    VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    VDIErrorCallbacks.pfnError = tstVDError;
+    VDIErrorCallbacks.pfnMessage = tstVDMessage;
+
+    rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
+                        NULL, &pVDIfs);
+    AssertRC(rc);
+
+
+    rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD);
+    CHECK("VDCreate()");
+
+    rc = VDCreateBase(pVD, pTest->pcszBackend, pTest->pcszBaseImage, cbDisk,
+                      VD_IMAGE_FLAGS_NONE, "Test image",
+                      &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
+                      NULL, NULL);
+    CHECK("VDCreateBase()");
+
+    bool fInit = true;
+    uint32_t cIteration = 0;
+
+    /* Do the real work now */
+    while (   RT_SUCCESS(rc)
+           && cIteration < pTest->cIterations)
+    {
+        /* Write */
+        rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit);
+        CHECK("tstVDSnapWrite()");
+
+        fInit = false;
+
+        /* Write returned, do we want to create a new diff or merge them? */
+        bool fCreate =   cDiffs < pTest->cDiffsMinBeforeMerge
+                       ? true
+                       : tstVDSnapIsTrue(pTest->uCreateDiffChance);
+
+        if (fCreate)
+        {
+            char *pszDiffFilename = NULL;
+
+            RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", idDiff, pTest->pcszDiffSuff);
+            CHECK("RTStrAPrintf()");
+            idDiff++;
+            cDiffs++;
+
+            rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename,
+                              VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
+                              VD_OPEN_FLAGS_NORMAL, NULL, NULL);
+            CHECK("VDCreateDiff()");
+
+            RTStrFree(pszDiffFilename);
+            VDDumpImages(pVD);
+
+            /* Change data */
+            tstVDSnapSegmentsDice(pTest, paDiskSeg, cDiskSegments, pbTestPattern, pTest->cbTestPattern);
+        }
+        else
+        {
+            uint32_t uStartMerge = RTRandAdvU32Ex(g_hRand, 1, cDiffs - 1);
+            uint32_t uEndMerge   = RTRandAdvU32Ex(g_hRand, uStartMerge + 1, cDiffs);
+            RTPrintf("Merging %u diffs from %u to %u...\n",
+                     uEndMerge - uStartMerge,
+                     uStartMerge,
+                     uEndMerge);
+            if (pTest->fForward)
+                rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL);
+            else
+                rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL);
+            CHECK("VDMerge()");
+
+            cDiffs -= uEndMerge - uStartMerge;
+
+            VDDumpImages(pVD);
+
+            /* Go through the disk segments and reset pointers. */
+            for (uint32_t i = 0; i < cDiskSegments; i++)
+            {
+                if (paDiskSeg[i].pbDataDiff)
+                {
+                    paDiskSeg[i].pbData     = paDiskSeg[i].pbDataDiff;
+                    paDiskSeg[i].pbDataDiff = NULL;
+                }
+            }
+
+            /* Now compare the result with our test pattern */
+            rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk);
+            CHECK("tstVDSnapReadVerify()");
+        }
+        cIteration++;
+    }
+
+    VDDumpImages(pVD);
+
+    VDDestroy(pVD);
+    if (pbTestPattern)
+        RTMemFree(pbTestPattern);
+
+    RTFileDelete(pTest->pcszBaseImage);
+    for (unsigned i = 0; i < idDiff; i++)
+    {
+        char *pszDiffFilename = NULL;
+
+        RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff);
+        RTFileDelete(pszDiffFilename);
+        RTStrFree(pszDiffFilename);
+    }
+#undef CHECK
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    RTR3Init();
+    int rc;
+    VDSNAPTEST Test;
+
+    RTPrintf("tstVDSnap: TESTING...\n");
+
+    rc = RTRandAdvCreateParkMiller(&g_hRand);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVDSnap: Creating RNG failed rc=%Rrc\n", rc);
+        return 1;
+    }
+
+    RTRandAdvSeed(g_hRand, 0x12345678);
+
+    Test.pcszBackend          = "vmdk";
+    Test.pcszBaseImage        = "tstVDSnapBase.vmdk";
+    Test.pcszDiffSuff         = "vmdk";
+    Test.cIterations          = 30;
+    Test.cbTestPattern        = 10 * _1M;
+    Test.cDiskSegsMin         = 10;
+    Test.cDiskSegsMax         = 50;
+    Test.cDiffsMinBeforeMerge = 5;
+    Test.uCreateDiffChance    = 50; /* % */
+    Test.uChangeSegChance     = 50; /* % */
+    Test.uAllocatedBlocks     = 50; /* 50% allocated */
+    Test.fForward             = true;
+    tstVDOpenCreateWriteMerge(&Test);
+
+    /* Same test with backwards merge */
+    Test.fForward             = false;
+    tstVDOpenCreateWriteMerge(&Test);
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("tstVDSnap: unloading backends failed! rc=%Rrc\n", rc);
+        g_cErrors++;
+    }
+     /*
+     * Summary
+     */
+    if (!g_cErrors)
+        RTPrintf("tstVDSnap: SUCCESS\n");
+    else
+        RTPrintf("tstVDSnap: FAILURE - %d errors\n", g_cErrors);
+
+    RTRandAdvDestroy(g_hRand);
+
+    return !!g_cErrors;
+}
+
Index: /trunk/src/VBox/Storage/testcase/vbox-img.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/vbox-img.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/vbox-img.cpp	(revision 33567)
@@ -0,0 +1,1096 @@
+/* $Id$ */
+/** @file
+ * Standalone image manipulation tool
+ */
+
+/*
+ * Copyright (C) 2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <VBox/vd.h>
+#include <VBox/err.h>
+#include <VBox/version.h>
+#include <iprt/initterm.h>
+#include <iprt/buildconfig.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/stream.h>
+#include <iprt/message.h>
+#include <iprt/getopt.h>
+#include <iprt/assert.h>
+
+const char *g_pszProgName = "";
+static void printUsage(PRTSTREAM pStrm)
+{
+    RTStrmPrintf(pStrm,
+                 "Usage: %s\n"
+                 "   setuuid      --filename <filename>\n"
+                 "                [--format VDI|VMDK|VHD|...]\n"
+                 "                [--uuid <uuid>]\n"
+                 "                [--parentuuid <uuid>]\n"
+                 "                [--zeroparentuuid]\n"
+                 "\n"
+                 "   convert      --srcfilename <filename>\n"
+                 "                --dstfilename <filename>\n"
+                 "                [--stdin]|[--stdout]\n"
+                 "                [--srcformat VDI|VMDK|VHD|RAW|..]\n"
+                 "                [--dstformat VDI|VMDK|VHD|RAW|..]\n"
+                 "                [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
+                 "\n"
+                 "   info         --filename <filename>\n"
+                 "\n"
+                 "   compact      --filename <filename>\n",
+                 g_pszProgName);
+}
+
+void showLogo(PRTSTREAM pStrm)
+{
+    static bool s_fShown; /* show only once */
+
+    if (!s_fShown)
+    {
+        RTStrmPrintf(pStrm, VBOX_PRODUCT " Disk Utility " VBOX_VERSION_STRING "\n"
+                     "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
+                     "All rights reserved.\n"
+                     "\n");
+        s_fShown = true;
+    }
+}
+
+/** command handler argument */
+struct HandlerArg
+{
+    int argc;
+    char **argv;
+};
+
+PVDINTERFACE pVDIfs;
+
+static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
+                                        const char *pszFormat, va_list va)
+{
+    NOREF(pvUser);
+    NOREF(rc);
+    RTMsgErrorV(pszFormat, va);
+}
+
+static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
+{
+    NOREF(pvUser);
+    RTPrintfV(pszFormat, va);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Print a usage synopsis and the syntax error message.
+ */
+int errorSyntax(const char *pszFormat, ...)
+{
+    va_list args;
+    showLogo(g_pStdErr); // show logo even if suppressed
+    va_start(args, pszFormat);
+    RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args);
+    va_end(args);
+    printUsage(g_pStdErr);
+    return 1;
+}
+
+int errorRuntime(const char *pszFormat, ...)
+{
+    va_list args;
+
+    va_start(args, pszFormat);
+    RTMsgErrorV(pszFormat, args);
+    va_end(args);
+    return 1;
+}
+
+
+
+int handleSetUUID(HandlerArg *a)
+{
+    const char *pszFilename = NULL;
+    char *pszFormat = NULL;
+    VDTYPE enmType = VDTYPE_INVALID;
+    RTUUID imageUuid;
+    RTUUID parentUuid;
+    bool fSetImageUuid = false;
+    bool fSetParentUuid = false;
+    RTUuidClear(&imageUuid);
+    RTUuidClear(&parentUuid);
+    int rc;
+
+    /* Parse the command line. */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--filename", 'f', RTGETOPT_REQ_STRING },
+        { "--format", 'o', RTGETOPT_REQ_STRING },
+        { "--uuid", 'u', RTGETOPT_REQ_UUID },
+        { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
+        { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
+    };
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+    {
+        switch (ch)
+        {
+            case 'f':   // --filename
+                pszFilename = ValueUnion.psz;
+                break;
+            case 'o':   // --format
+                pszFormat = RTStrDup(ValueUnion.psz);
+                break;
+            case 'u':   // --uuid
+                imageUuid = ValueUnion.Uuid;
+                fSetImageUuid = true;
+                break;
+            case 'p':   // --parentuuid
+                parentUuid = ValueUnion.Uuid;
+                fSetParentUuid = true;
+                break;
+            case 'P':   // --zeroparentuuid
+                RTUuidClear(&parentUuid);
+                fSetParentUuid = true;
+                break;
+
+            default:
+                ch = RTGetOptPrintError(ch, &ValueUnion);
+                printUsage(g_pStdErr);
+                return ch;
+        }
+    }
+
+    /* Check for mandatory parameters. */
+    if (!pszFilename)
+        return errorSyntax("Mandatory --filename option missing\n");
+
+    /* Check for consistency of optional parameters. */
+    if (fSetImageUuid && RTUuidIsNull(&imageUuid))
+        return errorSyntax("Invalid parameter to --uuid option\n");
+
+    /* Autodetect image format. */
+    if (!pszFormat)
+    {
+        /* Don't pass error interface, as that would triggers error messages
+         * because some backends fail to open the image. */
+        rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
+        if (RT_FAILURE(rc))
+            return errorRuntime("Format autodetect failed: %Rrc\n", rc);
+    }
+
+    PVBOXHDD pVD = NULL;
+    rc = VDCreate(pVDIfs, enmType, &pVD);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
+
+
+    rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
+                            pszFilename, rc);
+
+    RTUUID oldImageUuid;
+    rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
+                            pszFilename, rc);
+
+    RTPrintf("Old image UUID:  %RTuuid\n", &oldImageUuid);
+
+    RTUUID oldParentUuid;
+    rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
+                            pszFilename, rc);
+
+    RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
+
+    if (fSetImageUuid)
+    {
+        RTPrintf("New image UUID:  %RTuuid\n", &imageUuid);
+        rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
+        if (RT_FAILURE(rc))
+            return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
+                                pszFilename, rc);
+    }
+
+    if (fSetParentUuid)
+    {
+        RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
+        rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
+        if (RT_FAILURE(rc))
+            return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
+                                pszFilename, rc);
+    }
+
+    rc = VDCloseAll(pVD);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Closing image failed! rc=%Rrc\n", rc);
+
+    if (pszFormat)
+    {
+        RTStrFree(pszFormat);
+        pszFormat = NULL;
+    }
+
+    return 0;
+}
+
+
+typedef struct FILEIOSTATE
+{
+    RTFILE file;
+    /** Offset in the file. */
+    uint64_t off;
+    /** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
+    uint64_t offBuffer;
+    /** Size of valid data in the buffer. */
+    uint32_t cbBuffer;
+    /** Buffer for efficient I/O */
+    uint8_t abBuffer[16 *_1M];
+} FILEIOSTATE, *PFILEIOSTATE;
+
+static int convInOpen(void *pvUser, const char *pszLocation,
+                      uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
+                      void **ppStorage)
+{
+    NOREF(pvUser);
+    /* Validate input. */
+    AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+    AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER);
+    RTFILE file;
+    int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDIN);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* No need to clear the buffer, the data will be read from disk. */
+    PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
+    if (!pFS)
+        return VERR_NO_MEMORY;
+
+    pFS->file = file;
+    pFS->off = 0;
+    pFS->offBuffer = UINT64_MAX;
+    pFS->cbBuffer = 0;
+
+    *ppStorage = pFS;
+    return VINF_SUCCESS;
+}
+
+static int convInClose(void *pvUser, void *pStorage)
+{
+    NOREF(pvUser);
+    AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
+    PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
+
+    RTMemFree(pFS);
+
+    return VINF_SUCCESS;
+}
+
+static int convInDelete(void *pvUser, const char *pcszFilename)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
+                      unsigned fMove)
+{
+    NOREF(pvUser);
+    NOREF(pcszSrc);
+    NOREF(pcszDst);
+    NOREF(fMove);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInGetFreeSpace(void *pvUser, const char *pcszFilename,
+                              int64_t *pcbFreeSpace)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
+    *pcbFreeSpace = 0;
+    return VINF_SUCCESS;
+}
+
+static int convInGetModificationTime(void *pvUser, const char *pcszFilename,
+                                     PRTTIMESPEC pModificationTime)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    NOREF(cbSize);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
+                      void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    NOREF(pvUser);
+    AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
+    PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
+    AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
+    int rc;
+
+    /* Fill buffer if it is empty. */
+    if (pFS->offBuffer == UINT64_MAX)
+    {
+        /* Repeat reading until buffer is full or EOF. */
+        size_t cbSumRead = 0, cbRead;
+        uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+        size_t cbTmp = sizeof(pFS->abBuffer);
+        do
+        {
+            rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+            if (RT_FAILURE(rc))
+                return rc;
+            pTmp += cbRead;
+            cbTmp -= cbRead;
+            cbSumRead += cbRead;
+        } while (cbTmp && cbRead);
+
+        pFS->offBuffer = 0;
+        pFS->cbBuffer = cbSumRead;
+    }
+
+    /* Read several blocks and assemble the result if necessary */
+    size_t cbTotalRead = 0;
+    do
+    {
+        /* Skip over areas no one wants to read. */
+        while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
+        {
+            if (pFS->cbBuffer < sizeof(pFS->abBuffer))
+            {
+                if (pcbRead)
+                    *pcbRead = cbTotalRead;
+                return VERR_EOF;
+            }
+
+            /* Repeat reading until buffer is full or EOF. */
+            size_t cbSumRead = 0, cbRead;
+            uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
+            size_t cbTmp = sizeof(pFS->abBuffer);
+            do
+            {
+                rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
+                if (RT_FAILURE(rc))
+                    return rc;
+                pTmp += cbRead;
+                cbTmp -= cbRead;
+                cbSumRead += cbRead;
+            } while (cbTmp && cbRead);
+
+            pFS->offBuffer += pFS->cbBuffer;
+            pFS->cbBuffer = cbSumRead;
+        }
+
+        uint32_t cbThisRead = RT_MIN(cbBuffer,
+                                     pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
+        memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
+               cbThisRead);
+        uOffset += cbThisRead;
+        pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
+        cbBuffer -= cbThisRead;
+        cbTotalRead += cbThisRead;
+    } while (cbBuffer > 0);
+
+    if (pcbRead)
+        *pcbRead = cbTotalRead;
+
+    pFS->off = uOffset;
+
+    return VINF_SUCCESS;
+}
+
+static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
+                       const void *pvBuffer, size_t cbBuffer,
+                       size_t *pcbWritten)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    NOREF(uOffset);
+    NOREF(cbBuffer);
+    NOREF(pcbWritten);
+    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convInFlush(void *pvUser, void *pStorage)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    return VINF_SUCCESS;
+}
+
+static int convOutOpen(void *pvUser, const char *pszLocation,
+                       uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
+                       void **ppStorage)
+{
+    NOREF(pvUser);
+    /* Validate input. */
+    AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
+    AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
+    RTFILE file;
+    int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDOUT);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* Must clear buffer, so that skipped over data is initialized properly. */
+    PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAllocZ(sizeof(FILEIOSTATE));
+    if (!pFS)
+        return VERR_NO_MEMORY;
+
+    pFS->file = file;
+    pFS->off = 0;
+    pFS->offBuffer = 0;
+    pFS->cbBuffer = sizeof(FILEIOSTATE);
+
+    *ppStorage = pFS;
+    return VINF_SUCCESS;
+}
+
+static int convOutClose(void *pvUser, void *pStorage)
+{
+    NOREF(pvUser);
+    AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
+    PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
+    int rc = VINF_SUCCESS;
+
+    /* Flush any remaining buffer contents. */
+    if (pFS->cbBuffer)
+        rc = RTFileWrite(pFS->file, &pFS->abBuffer[0], pFS->cbBuffer, NULL);
+
+    RTMemFree(pFS);
+
+    return rc;
+}
+
+static int convOutDelete(void *pvUser, const char *pcszFilename)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
+                       unsigned fMove)
+{
+    NOREF(pvUser);
+    NOREF(pcszSrc);
+    NOREF(pcszDst);
+    NOREF(fMove);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
+                               int64_t *pcbFreeSpace)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
+    *pcbFreeSpace = INT64_MAX;
+    return VINF_SUCCESS;
+}
+
+static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
+                                      PRTTIMESPEC pModificationTime)
+{
+    NOREF(pvUser);
+    NOREF(pcszFilename);
+    AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    NOREF(cbSize);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
+                       void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    NOREF(uOffset);
+    NOREF(cbBuffer);
+    NOREF(pcbRead);
+    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
+                        const void *pvBuffer, size_t cbBuffer,
+                        size_t *pcbWritten)
+{
+    NOREF(pvUser);
+    AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
+    PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
+    AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
+    int rc;
+
+    /* Write the data to the buffer, flushing as required. */
+    size_t cbTotalWritten = 0;
+    do
+    {
+        /* Flush the buffer if we need a new one. */
+        while (uOffset > pFS->offBuffer + sizeof(pFS->abBuffer) - 1)
+        {
+            rc = RTFileWrite(pFS->file, &pFS->abBuffer[0],
+                             sizeof(pFS->abBuffer), NULL);
+            RT_ZERO(pFS->abBuffer);
+            pFS->offBuffer += sizeof(pFS->abBuffer);
+            pFS->cbBuffer = 0;
+        }
+
+        uint32_t cbThisWrite = RT_MIN(cbBuffer,
+                                      sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
+        memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
+               cbThisWrite);
+        uOffset += cbThisWrite;
+        pvBuffer = (uint8_t *)pvBuffer + cbThisWrite;
+        cbBuffer -= cbThisWrite;
+        cbTotalWritten += cbThisWrite;
+    } while (cbBuffer > 0);
+
+    if (pcbWritten)
+        *pcbWritten = cbTotalWritten;
+
+    pFS->cbBuffer = uOffset % sizeof(pFS->abBuffer);
+    if (!pFS->cbBuffer)
+        pFS->cbBuffer = sizeof(pFS->abBuffer);
+    pFS->off = uOffset;
+
+    return VINF_SUCCESS;
+}
+
+static int convOutFlush(void *pvUser, void *pStorage)
+{
+    NOREF(pvUser);
+    NOREF(pStorage);
+    return VINF_SUCCESS;
+}
+
+int handleConvert(HandlerArg *a)
+{
+    const char *pszSrcFilename = NULL;
+    const char *pszDstFilename = NULL;
+    bool fStdIn = false;
+    bool fStdOut = false;
+    const char *pszSrcFormat = NULL;
+    VDTYPE enmSrcType = VDTYPE_HDD;
+    const char *pszDstFormat = NULL;
+    const char *pszVariant = NULL;
+    PVBOXHDD pSrcDisk = NULL;
+    PVBOXHDD pDstDisk = NULL;
+    unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
+    PVDINTERFACE pIfsImageInput = NULL;
+    PVDINTERFACE pIfsImageOutput = NULL;
+    VDINTERFACE IfsInputIO;
+    VDINTERFACE IfsOutputIO;
+    VDINTERFACEIO IfsInputIOCb;
+    VDINTERFACEIO IfsOutputIOCb;
+    int rc = VINF_SUCCESS;
+
+    /* Parse the command line. */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
+        { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
+        { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
+        { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
+        { "--srcformat", 's', RTGETOPT_REQ_STRING },
+        { "--dstformat", 'd', RTGETOPT_REQ_STRING },
+        { "--variant", 'v', RTGETOPT_REQ_STRING }
+    };
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+    {
+        switch (ch)
+        {
+            case 'i':   // --srcfilename
+                pszSrcFilename = ValueUnion.psz;
+                break;
+            case 'o':   // --dstfilename
+                pszDstFilename = ValueUnion.psz;
+                break;
+            case 'p':   // --stdin
+                fStdIn = true;
+                break;
+            case 'P':   // --stdout
+                fStdOut = true;
+                break;
+            case 's':   // --srcformat
+                pszSrcFormat = ValueUnion.psz;
+                break;
+            case 'd':   // --dstformat
+                pszDstFormat = ValueUnion.psz;
+                break;
+            case 'v':   // --variant
+                pszVariant = ValueUnion.psz;
+                break;
+
+            default:
+                ch = RTGetOptPrintError(ch, &ValueUnion);
+                printUsage(g_pStdErr);
+                return ch;
+        }
+    }
+
+    /* Check for mandatory parameters and handle dummies/defaults. */
+    if (fStdIn && !pszSrcFormat)
+        return errorSyntax("Mandatory --srcformat option missing\n");
+    if (!pszDstFormat)
+        pszDstFormat = "VDI";
+    if (fStdIn && !pszSrcFilename)
+    {
+        /* Complete dummy, will be just passed to various calls to fulfill
+         * the "must be non-NULL" requirement, and is completely ignored
+         * otherwise. It shown in the stderr message below. */
+        pszSrcFilename = "stdin";
+    }
+    if (fStdOut && !pszDstFilename)
+    {
+        /* Will be stored in the destination image if it is a streamOptimized
+         * VMDK, but it isn't really relevant - use it for "branding". */
+        if (!RTStrICmp(pszDstFormat, "VMDK"))
+            pszDstFilename = "VirtualBoxStream.vmdk";
+        else
+            pszDstFilename = "stdout";
+    }
+    if (!pszSrcFilename)
+        return errorSyntax("Mandatory --srcfilename option missing\n");
+    if (!pszDstFilename)
+        return errorSyntax("Mandatory --dstfilename option missing\n");
+
+    if (fStdIn)
+    {
+        IfsInputIOCb.cbSize                 = sizeof(VDINTERFACEIO);
+        IfsInputIOCb.enmInterface           = VDINTERFACETYPE_IO;
+        IfsInputIOCb.pfnOpen                = convInOpen;
+        IfsInputIOCb.pfnClose               = convInClose;
+        IfsInputIOCb.pfnDelete              = convInDelete;
+        IfsInputIOCb.pfnMove                = convInMove;
+        IfsInputIOCb.pfnGetFreeSpace        = convInGetFreeSpace;
+        IfsInputIOCb.pfnGetModificationTime = convInGetModificationTime;
+        IfsInputIOCb.pfnGetSize             = convInGetSize;
+        IfsInputIOCb.pfnSetSize             = convInSetSize;
+        IfsInputIOCb.pfnReadSync            = convInRead;
+        IfsInputIOCb.pfnWriteSync           = convInWrite;
+        IfsInputIOCb.pfnFlushSync           = convInFlush;
+        VDInterfaceAdd(&IfsInputIO, "stdin", VDINTERFACETYPE_IO,
+                       &IfsInputIOCb, NULL, &pIfsImageInput);
+    }
+    if (fStdOut)
+    {
+        IfsOutputIOCb.cbSize                    = sizeof(VDINTERFACEIO);
+        IfsOutputIOCb.enmInterface              = VDINTERFACETYPE_IO;
+        IfsOutputIOCb.pfnOpen                   = convOutOpen;
+        IfsOutputIOCb.pfnClose                  = convOutClose;
+        IfsOutputIOCb.pfnDelete                 = convOutDelete;
+        IfsOutputIOCb.pfnMove                   = convOutMove;
+        IfsOutputIOCb.pfnGetFreeSpace           = convOutGetFreeSpace;
+        IfsOutputIOCb.pfnGetModificationTime    = convOutGetModificationTime;
+        IfsOutputIOCb.pfnGetSize                = convOutGetSize;
+        IfsOutputIOCb.pfnSetSize                = convOutSetSize;
+        IfsOutputIOCb.pfnReadSync               = convOutRead;
+        IfsOutputIOCb.pfnWriteSync              = convOutWrite;
+        IfsOutputIOCb.pfnFlushSync              = convOutFlush;
+        VDInterfaceAdd(&IfsOutputIO, "stdout", VDINTERFACETYPE_IO,
+                       &IfsOutputIOCb, NULL, &pIfsImageOutput);
+    }
+
+    /* check the variant parameter */
+    if (pszVariant)
+    {
+        char *psz = (char*)pszVariant;
+        while (psz && *psz && RT_SUCCESS(rc))
+        {
+            size_t len;
+            const char *pszComma = strchr(psz, ',');
+            if (pszComma)
+                len = pszComma - psz;
+            else
+                len = strlen(psz);
+            if (len > 0)
+            {
+                if (!RTStrNICmp(pszVariant, "standard", len))
+                    uImageFlags |= VD_IMAGE_FLAGS_NONE;
+                else if (!RTStrNICmp(pszVariant, "fixed", len))
+                    uImageFlags |= VD_IMAGE_FLAGS_FIXED;
+                else if (!RTStrNICmp(pszVariant, "split2g", len))
+                    uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
+                else if (!RTStrNICmp(pszVariant, "stream", len))
+                    uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
+                else if (!RTStrNICmp(pszVariant, "esx", len))
+                    uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
+                else
+                    return errorSyntax("Invalid --variant option\n");
+            }
+            if (pszComma)
+                psz += len + 1;
+            else
+                psz += len;
+        }
+    }
+
+    do
+    {
+        /* try to determine input format if not specified */
+        if (!pszSrcFormat)
+        {
+            char *pszFormat = NULL;
+            VDTYPE enmType = VDTYPE_INVALID;
+            rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszFormat, &enmType);
+            if (RT_FAILURE(rc))
+            {
+                errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
+                break;
+            }
+            pszSrcFormat = pszFormat;
+            enmSrcType = enmType;
+        }
+
+        rc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
+        if (RT_FAILURE(rc))
+        {
+            errorRuntime("Error while creating source disk container: %Rrc\n", rc);
+            break;
+        }
+
+        rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
+                    VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
+                    pIfsImageInput);
+        if (RT_FAILURE(rc))
+        {
+            errorRuntime("Error while opening source image: %Rrc\n", rc);
+            break;
+        }
+
+        rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDstDisk);
+        if (RT_FAILURE(rc))
+        {
+            errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
+            break;
+        }
+
+        uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
+        RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
+
+        /* Create the output image */
+        rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
+                    pszDstFilename, false, 0, uImageFlags, NULL,
+                    VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
+                    pIfsImageOutput, NULL);
+        if (RT_FAILURE(rc))
+        {
+            errorRuntime("Error while copying the image: %Rrc\n", rc);
+            break;
+        }
+
+    }
+    while (0);
+
+    if (pDstDisk)
+        VDCloseAll(pDstDisk);
+    if (pSrcDisk)
+        VDCloseAll(pSrcDisk);
+
+    return RT_SUCCESS(rc) ? 0 : 1;
+}
+
+
+int handleInfo(HandlerArg *a)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = NULL;
+    const char *pszFilename = NULL;
+
+    /* Parse the command line. */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--filename", 'f', RTGETOPT_REQ_STRING }
+    };
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+    {
+        switch (ch)
+        {
+            case 'f':   // --filename
+                pszFilename = ValueUnion.psz;
+                break;
+
+            default:
+                ch = RTGetOptPrintError(ch, &ValueUnion);
+                printUsage(g_pStdErr);
+                return ch;
+        }
+    }
+
+    /* Check for mandatory parameters. */
+    if (!pszFilename)
+        return errorSyntax("Mandatory --filename option missing\n");
+
+    /* just try it */
+    char *pszFormat = NULL;
+    VDTYPE enmType = VDTYPE_INVALID;
+    rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
+    if (RT_FAILURE(rc))
+        return errorSyntax("Format autodetect failed: %Rrc\n", rc);
+
+    rc = VDCreate(pVDIfs, enmType, &pDisk);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
+
+    /* Open the image */
+    rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Error while opening the image: %Rrc\n", rc);
+
+    VDDumpImages(pDisk);
+
+    VDCloseAll(pDisk);
+
+    return rc;
+}
+
+
+int handleCompact(HandlerArg *a)
+{
+    int rc = VINF_SUCCESS;
+    PVBOXHDD pDisk = NULL;
+    const char *pszFilename = NULL;
+
+    /* Parse the command line. */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--filename", 'f', RTGETOPT_REQ_STRING }
+    };
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+    {
+        switch (ch)
+        {
+            case 'f':   // --filename
+                pszFilename = ValueUnion.psz;
+                break;
+
+            default:
+                ch = RTGetOptPrintError(ch, &ValueUnion);
+                printUsage(g_pStdErr);
+                return ch;
+        }
+    }
+
+    /* Check for mandatory parameters. */
+    if (!pszFilename)
+        return errorSyntax("Mandatory --filename option missing\n");
+
+    /* just try it */
+    char *pszFormat = NULL;
+    VDTYPE enmType = VDTYPE_INVALID;
+    rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
+    if (RT_FAILURE(rc))
+        return errorSyntax("Format autodetect failed: %Rrc\n", rc);
+
+    rc = VDCreate(pVDIfs, enmType, &pDisk);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
+
+    /* Open the image */
+    rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
+    if (RT_FAILURE(rc))
+        return errorRuntime("Error while opening the image: %Rrc\n", rc);
+
+    rc = VDCompact(pDisk, 0, NULL);
+    if (RT_FAILURE(rc))
+        errorRuntime("Error while compacting image: %Rrc\n", rc);
+
+    VDCloseAll(pDisk);
+
+    return rc;
+}
+
+
+int main(int argc, char *argv[])
+{
+    RTR3Init();
+    int rc;
+    int exitcode = 0;
+
+    g_pszProgName = RTPathFilename(argv[0]);
+
+    bool fShowLogo = false;
+    int  iCmd      = 1;
+    int  iCmdArg;
+
+    /* global options */
+    for (int i = 1; i < argc || argc <= iCmd; i++)
+    {
+        if (    argc <= iCmd
+            ||  !strcmp(argv[i], "help")
+            ||  !strcmp(argv[i], "-?")
+            ||  !strcmp(argv[i], "-h")
+            ||  !strcmp(argv[i], "-help")
+            ||  !strcmp(argv[i], "--help"))
+        {
+            showLogo(g_pStdOut);
+            printUsage(g_pStdOut);
+            return 0;
+        }
+
+        if (   !strcmp(argv[i], "-v")
+            || !strcmp(argv[i], "-version")
+            || !strcmp(argv[i], "-Version")
+            || !strcmp(argv[i], "--version"))
+        {
+            /* Print version number, and do nothing else. */
+            RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
+            return 0;
+        }
+
+        if (   !strcmp(argv[i], "--nologo")
+            || !strcmp(argv[i], "-nologo")
+            || !strcmp(argv[i], "-q"))
+        {
+            /* suppress the logo */
+            fShowLogo = false;
+            iCmd++;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    iCmdArg = iCmd + 1;
+
+    if (fShowLogo)
+        showLogo(g_pStdOut);
+
+    /* initialize the VD backend with dummy handlers */
+    VDINTERFACE      vdInterfaceError;
+    VDINTERFACEERROR vdInterfaceErrorCallbacks;
+    vdInterfaceErrorCallbacks.cbSize       = sizeof(VDINTERFACEERROR);
+    vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
+    vdInterfaceErrorCallbacks.pfnError     = handleVDError;
+    vdInterfaceErrorCallbacks.pfnMessage   = handleVDMessage;
+
+    rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
+                        &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
+
+    rc = VDInit();
+    if (RT_FAILURE(rc))
+    {
+        errorSyntax("Initalizing backends failed! rc=%Rrc\n", rc);
+        return 1;
+    }
+
+    /*
+     * All registered command handlers
+     */
+    static const struct
+    {
+        const char *command;
+        int (*handler)(HandlerArg *a);
+    } s_commandHandlers[] =
+    {
+        { "setuuid", handleSetUUID },
+        { "convert", handleConvert },
+        { "info",    handleInfo    },
+        { "compact", handleCompact },
+        { NULL,               NULL }
+    };
+
+    HandlerArg handlerArg = { 0, NULL };
+    int commandIndex;
+    for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
+    {
+        if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
+        {
+            handlerArg.argc = argc - iCmdArg;
+            handlerArg.argv = &argv[iCmdArg];
+
+            exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
+            break;
+        }
+    }
+    if (!s_commandHandlers[commandIndex].command)
+    {
+        errorSyntax("Invalid command '%s'", argv[iCmd]);
+        return 1;
+    }
+
+    rc = VDShutdown();
+    if (RT_FAILURE(rc))
+    {
+        errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
+        return 1;
+    }
+
+    return exitcode;
+}
+
+/* dummy stub for RuntimeR3 */
+#ifndef RT_OS_WINDOWS
+RTDECL(bool) RTAssertShouldPanic(void)
+{
+    return true;
+}
+#endif
Index: /trunk/src/VBox/Storage/testcase/vditool.cpp
===================================================================
--- /trunk/src/VBox/Storage/testcase/vditool.cpp	(revision 33567)
+++ /trunk/src/VBox/Storage/testcase/vditool.cpp	(revision 33567)
@@ -0,0 +1,471 @@
+/** @file
+ *
+ * VBox HDD container maintenance/conversion utility
+ */
+
+/*
+ * Copyright (C) 2006-2010 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <VBox/vd.h>
+#include <iprt/alloc.h>
+#include <iprt/file.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include <stdlib.h>
+
+
+
+static void ascii2upper(char *psz)
+{
+    for (;*psz; psz++)
+        if (*psz >= 'a' && *psz <= 'z')
+            *psz += 'A' - 'a';
+}
+
+static int UsageExit()
+{
+    RTPrintf("Usage:   vditool <Command> [Params]\n"
+             "Commands and params:\n"
+             "    NEW Filename Mbytes          - create new image\n"
+#if 0
+             "    DD  Filename DDFilename      - create new image from DD format image\n"
+             "    CONVERT Filename             - convert VDI image from old format\n"
+             "    DUMP Filename                - debug dump\n"
+             "    RESETGEO Filename            - reset geometry information\n"
+             "    COPY FromImage ToImage       - make image copy\n"
+             "    COPYDD FromImage DDFilename  - make a DD copy of the image\n"
+             "    SHRINK Filename              - optimize (reduce) VDI image size\n"
+#endif
+             );
+    return 1;
+}
+
+static int SyntaxError(const char *pszMsg)
+{
+    RTPrintf("Syntax error: %s\n\n", pszMsg);
+    UsageExit();
+    return 1;
+}
+
+/**
+ * Our internal functions use UTF8
+ */
+static int FilenameToUtf8(char **pszUtf8Filename, const char *pszFilename)
+{
+    int rc = RTStrCurrentCPToUtf8(pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        RTPrintf("Error converting filename '%s' to UTF8! (rc=%Rrc)\n",
+                 pszFilename, rc);
+    return rc;
+}
+
+/**
+ * Prints a done message indicating success or failure.
+ * @returns rc
+ * @param   rc      Status code.
+ */
+static int PrintDone(int rc)
+{
+    if (rc == VINF_SUCCESS)
+        RTPrintf("The operation completed successfully!\n");
+    else if (RT_SUCCESS(rc))
+        RTPrintf("The operation completed successfully! (rc=%Rrc)\n", rc);
+    else
+        RTPrintf("FAILURE: %Rrf (%Rrc)\n", rc, rc);
+    return rc;
+}
+
+static int NewImage(const char *pszFilename, uint32_t cMBs)
+{
+    RTPrintf("Creating VDI: file=\"%s\" size=%u MB...\n",
+            pszFilename, cMBs);
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    PVBOXHDD hdd;
+    rc = VDCreate(NULL, VDTYPE_HDD, &hdd);
+    if (RT_FAILURE(rc))
+        return PrintDone(rc);
+
+    VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
+    rc = VDCreateBase(hdd, "vdi", pszUtf8Filename,
+                      (uint64_t)cMBs * _1M,
+                      VD_IMAGE_FLAGS_NONE,
+                      "Newly created test image",
+                      &geo, &geo, NULL,
+                      VD_OPEN_FLAGS_NORMAL,
+                      NULL, NULL);
+    return PrintDone(rc);
+}
+
+#if 0
+static int ConvertDDImage(const char *pszFilename, const char *pszDDFilename)
+{
+    RTPrintf("Converting VDI: from DD image file=\"%s\" to file=\"%s\"...\n",
+             pszDDFilename, pszFilename);
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename, *pszUtf8DDFilename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = FilenameToUtf8(&pszUtf8DDFilename, pszDDFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* open raw image file. */
+    RTFILE File;
+    rc = RTFileOpen(&File, pszUtf8DDFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
+    if (RT_FAILURE(rc))
+    {
+        RTPrintf("File=\"%s\" open error: %Rrf\n", pszDDFilename, rc);
+        return rc;
+    }
+
+    /* get image size. */
+    uint64_t cbFile;
+    rc = RTFileGetSize(File, &cbFile);
+    if (RT_SUCCESS(rc))
+    {
+        RTPrintf("Creating fixed image with size %u Bytes...\n", (unsigned)cbFile);
+        rc = VDICreateBaseImage(pszUtf8Filename,
+                                VDI_IMAGE_TYPE_FIXED,
+                                cbFile,
+                                "Converted from DD test image", NULL, NULL);
+        PrintDone(rc);
+        if (RT_SUCCESS(rc))
+        {
+            RTPrintf("Writing data...\n");
+            PVDIDISK pVdi = VDIDiskCreate();
+            rc = VDIDiskOpenImage(pVdi, pszUtf8Filename, VDI_OPEN_FLAGS_NORMAL);
+            if (RT_SUCCESS(rc))
+            {
+                /* alloc work buffer. */
+                void *pvBuf = RTMemAlloc(VDIDiskGetBufferSize(pVdi));
+                if (pvBuf)
+                {
+                    uint64_t off = 0;
+                    while (off < cbFile)
+                    {
+                        size_t cbRead = 0;
+                        rc = RTFileRead(File, pvBuf, VDIDiskGetBufferSize(pVdi), &cbRead);
+                        if (RT_FAILURE(rc) || !cbRead)
+                            break;
+                        rc = VDIDiskWrite(pVdi, off, pvBuf, cbRead);
+                        if (RT_FAILURE(rc))
+                            break;
+                        off += cbRead;
+                    }
+
+                    RTMemFree(pvBuf);
+                }
+                else
+                    rc = VERR_NO_MEMORY;
+
+                VDIDiskCloseImage(pVdi);
+            }
+
+            if (RT_FAILURE(rc))
+            {
+                /* delete image on error */
+                VDIDeleteImage(pszUtf8Filename);
+            }
+            PrintDone(rc);
+        }
+    }
+    RTFileClose(File);
+
+    return rc;
+}
+#endif
+
+#if 0
+static DECLCALLBACK(int) ProcessCallback(PVM pVM, unsigned uPercent, void *pvUser)
+{
+    unsigned *pPercent = (unsigned *)pvUser;
+
+    if (*pPercent != uPercent)
+    {
+        *pPercent = uPercent;
+        RTPrintf(".");
+        if ((uPercent % 10) == 0 && uPercent)
+            RTPrintf("%d%%", uPercent);
+        RTStrmFlush(g_pStdOut);
+    }
+
+    return VINF_SUCCESS;
+}
+#endif
+
+#if 0
+static int ConvertOldImage(const char *pszFilename)
+{
+    RTPrintf("Converting VDI image file=\"%s\" to a new format...\n"
+             "progress: 0%%",
+             pszFilename);
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    unsigned uPercent = 0;
+    rc = VDIConvertImage(pszUtf8Filename, ProcessCallback, &uPercent);
+    RTPrintf("\n");
+    return PrintDone(rc);
+}
+#endif
+
+#if 0
+static int DumpImage(const char *pszFilename)
+{
+    RTPrintf("Dumping VDI image file=\"%s\" into the log file...\n", pszFilename);
+    PVDIDISK pVdi = VDIDiskCreate();
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = VDIDiskOpenImage(pVdi, pszUtf8Filename, VDI_OPEN_FLAGS_READONLY);
+    if (RT_SUCCESS(rc))
+    {
+        VDIDiskDumpImages(pVdi);
+        VDIDiskCloseAllImages(pVdi);
+    }
+    return PrintDone(rc);
+}
+#endif
+
+#if 0
+static int ResetImageGeometry(const char *pszFilename)
+{
+    RTPrintf("Resetting geometry info of VDI image file=\"%s\"\n", pszFilename);
+    PVDIDISK pVdi = VDIDiskCreate();
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = VDIDiskOpenImage(pVdi, pszUtf8Filename, VDI_OPEN_FLAGS_NORMAL);
+    if (RT_SUCCESS(rc))
+    {
+        VDGEOMETRY LCHSGeometry = {0, 0, 0};
+        rc = VDIDiskSetLCHSGeometry(pVdi, &LCHSGeometry);
+    }
+    VDIDiskCloseImage(pVdi);
+    return PrintDone(rc);
+}
+#endif
+
+#if 0
+static int CopyImage(const char *pszDstFile, const char *pszSrcFile)
+{
+    RTPrintf("Copying VDI image file=\"%s\" to image file=\"%s\"...\n"
+             "progress: 0%%",
+             pszSrcFile, pszDstFile);
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8SrcFile, *pszUtf8DstFile;
+    int rc = FilenameToUtf8(&pszUtf8SrcFile, pszSrcFile);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = FilenameToUtf8(&pszUtf8DstFile, pszDstFile);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    unsigned uPrecent = 0;
+    rc = VDICopyImage(pszUtf8DstFile, pszUtf8SrcFile, NULL, ProcessCallback, &uPrecent);
+    RTPrintf("\n");
+    return PrintDone(rc);
+}
+#endif
+
+#if 0
+static int CopyToDD(const char *pszDstFile, const char *pszSrcFile)
+{
+    RTPrintf("Copying VDI image file=\"%s\" to DD file=\"%s\"...\n",
+             pszSrcFile, pszDstFile);
+    PVDIDISK pVdi = VDIDiskCreate();
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8SrcFile, *pszUtf8DstFile;
+    int rc = FilenameToUtf8(&pszUtf8SrcFile, pszSrcFile);
+    if (RT_FAILURE(rc))
+        return rc;
+    rc = FilenameToUtf8(&pszUtf8DstFile, pszDstFile);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = VDIDiskOpenImage(pVdi, pszUtf8SrcFile, VDI_OPEN_FLAGS_NORMAL);
+    if (RT_SUCCESS(rc))
+    {
+        RTFILE FileDst;
+        rc = RTFileOpen(&FileDst, pszUtf8DstFile, RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE);
+        if (RT_SUCCESS(rc))
+        {
+            uint64_t        cbSrc = VDIDiskGetSize(pVdi);
+            const unsigned  cbBuf = VDIDiskGetBlockSize(pVdi); /* or perhaps VDIDiskGetBufferSize(pVdi)? */
+            void *pvBuf = RTMemAlloc(cbBuf);
+            if (pvBuf)
+            {
+                uint64_t off = 0;
+                while (off < cbSrc)
+                {
+                    rc = VDIDiskRead(pVdi, off, pvBuf, cbBuf);
+                    if (RT_FAILURE(rc))
+                        break;
+                    rc = RTFileWrite(FileDst, pvBuf, cbBuf, NULL);
+                    if (RT_FAILURE(rc))
+                        break;
+                    off += cbBuf;
+                }
+                RTMemFree(pvBuf);
+            }
+            RTFileClose(FileDst);
+        }
+    }
+    VDIDiskCloseImage(pVdi);
+    return PrintDone(rc);
+}
+#endif
+
+#if 0
+static int ShrinkImage(const char *pszFilename)
+{
+    RTPrintf("Shrinking VDI image file=\"%s\"...\n"
+             "progress: 0%%",
+             pszFilename);
+
+    /* translate argv[] to UTF8 */
+    char *pszUtf8Filename;
+    int rc = FilenameToUtf8(&pszUtf8Filename, pszFilename);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    unsigned uPrecent;
+    rc = VDIShrinkImage(pszUtf8Filename, ProcessCallback, &uPrecent);
+    RTPrintf("\n");
+    return PrintDone(rc);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+    putenv((char*)"VBOX_LOG_DEST=stdout");
+    putenv((char*)"VBOX_LOG_FLAGS=");
+
+    RTR3Init();
+    RTPrintf("vditool -- for internal use only!\n"
+             "Copyright (c) 2009 Oracle Corporation\n\n");
+
+    /*
+     * Do cmd line parsing.
+     */
+    if (argc < 2)
+        return UsageExit();
+
+    char szCmd[16];
+    if (strlen(argv[1]) >= sizeof(szCmd))
+        return SyntaxError("Invalid command!");
+    strcpy(szCmd, argv[1]);
+    ascii2upper(szCmd);
+
+    PRTLOGGER pLogger;
+    static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+    int rc = RTLogCreate(&pLogger, 0, "all",
+                         NULL, RT_ELEMENTS(s_apszGroups), s_apszGroups,
+                          RTLOGDEST_STDOUT, NULL);
+    RTLogRelSetDefaultInstance(pLogger);
+
+    if (strcmp(szCmd, "NEW") == 0)
+    {
+        if (argc != 4)
+            return SyntaxError("Invalid argument count!");
+
+        uint32_t cMBs;
+        rc = RTStrToUInt32Ex(argv[3], NULL, 10, &cMBs);
+        if (RT_FAILURE(rc))
+            return SyntaxError("Invalid number!");
+        if (cMBs < 2 || cMBs > _1M)
+        {
+            RTPrintf("error: Disk size %RU32 (MB) is not within the range %u-%u!\n",
+                     cMBs, 2, _1M);
+            return 1;
+        }
+
+        rc = NewImage(argv[2], cMBs);
+    }
+#if 0
+    else if (strcmp(szCmd, "DD") == 0)
+    {
+        if (argc != 4)
+            return SyntaxError("Invalid argument count!");
+        rc = ConvertDDImage(argv[2], argv[3]);
+    }
+    else if (strcmp(szCmd, "CONVERT") == 0)
+    {
+        if (argc != 3)
+            return SyntaxError("Invalid argument count!");
+        rc = ConvertOldImage(argv[2]);
+    }
+    else if (strcmp(szCmd, "DUMP") == 0)
+    {
+        if (argc != 3)
+            return SyntaxError("Invalid argument count!");
+        rc = DumpImage(argv[2]);
+    }
+    else if (strcmp(szCmd, "RESETGEO") == 0)
+    {
+        if (argc != 3)
+            return SyntaxError("Invalid argument count!");
+        rc = ResetImageGeometry(argv[2]);
+    }
+    else if (strcmp(szCmd, "COPY") == 0)
+    {
+        if (argc != 4)
+            return SyntaxError("Invalid argument count!");
+        rc = CopyImage(argv[3], argv[2]);
+    }
+    else if (strcmp(szCmd, "COPYDD") == 0)
+    {
+        if (argc != 4)
+            return SyntaxError("Invalid argument count!");
+        rc = CopyToDD(argv[3], argv[2]);
+    }
+    else if (strcmp(szCmd, "SHRINK") == 0)
+    {
+        if (argc != 3)
+            return SyntaxError("Invalid argument count!");
+        rc = ShrinkImage(argv[2]);
+    }
+#endif
+    else
+        return SyntaxError("Invalid command!");
+
+    RTLogFlush(NULL);
+    return !RT_SUCCESS(rc);
+}
