VirtualBox

Ignore:
Timestamp:
Feb 26, 2008 6:20:08 PM (17 years ago)
Author:
vboxsync
Message:

Added VBoxHDD-new VDI backend. Untested, but harmless as the code won't be used without further changes.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/VDIHDDCore.cpp

    r6486 r7159  
    1818*   Header Files                                                               *
    1919*******************************************************************************/
    20 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
    21 #include <VBox/VBoxHDD.h>
     20#define LOG_GROUP LOG_GROUP_VD_VDI
     21#include "VBoxHDD-newInternal.h"
     22#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
    2223#include "VDICore.h"
    2324#include <VBox/err.h>
     
    3132#include <iprt/asm.h>
    3233
     34#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
    3335
    3436/*******************************************************************************
     
    3840static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
    3941static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
    40 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
     42static void vdiInitHeader(PVDIHEADER pHeader, VDIMAGETYPE enmType, uint32_t uImageFlags,
    4143                          const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    4244                          uint32_t cbBlockExtra);
    4345static int  vdiValidateHeader(PVDIHEADER pHeader);
    44 static int  vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    45                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    46                           PFNVMPROGRESS pfnProgress, void *pvUser);
    47 static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
    4846static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
    49 static int  vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename, unsigned fOpen,
    50                         PVDIIMAGEDESC pParent);
    5147static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
    5248static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
    53 static int  vdiUpdateBlocks(PVDIIMAGEDESC pImage);
    54 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
    55 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
    56 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    57 #if 0 /* unused */
    58 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    59 #endif
    60 static void vdiCloseImage(PVDIIMAGEDESC pImage);
    61 static int  vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    62                            size_t cbToRead, void *pvBuf);
    63 static int  vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    64 static int  vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock,
    65                             unsigned offWrite, size_t cbToWrite, const void *pvBuf);
    66 static int  vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    67 static int  vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    68                           PFNVMPROGRESS pfnProgress, void *pvUser);
    69 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    70 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    71 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
    72 static int  vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
    73 
    74 static int  vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    75                             PFNVMPROGRESS pfnProgress, void *pvUser);
    76 static void vdiDumpImage(PVDIIMAGEDESC pImage);
     49static void vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete);
     50
     51
     52/**
     53 * Internal: signal an error to the frontend.
     54 */
     55DECLINLINE(int) vdiError(PVDIIMAGEDESC pImage, int rc, RT_SRC_POS_DECL,
     56                         const char *pszFormat, ...)
     57{
     58    va_list va;
     59    va_start(va, pszFormat);
     60    if (pImage->pfnError)
     61        pImage->pfnError(pImage->pvErrorUser, rc, RT_SRC_POS_ARGS,
     62                         pszFormat, va);
     63    va_end(va);
     64    return rc;
     65}
    7766
    7867
     
    9382}
    9483
     84
    9585/**
    96  * internal: init HDD preheader.
     86 * Internal: Init VDI preheader.
    9787 */
    9888static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
     
    10595
    10696/**
    107  * internal: check HDD preheader.
     97 * Internal: check VDI preheader.
    10898 */
    10999static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
     
    120110
    121111/**
    122  * internal: init HDD header. Always use latest header version.
     112 * Internal: Init VDI header. Always use latest header version.
    123113 * @param   pHeader     Assumes it was initially initialized to all zeros.
    124114 */
    125 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
     115static void vdiInitHeader(PVDIHEADER pHeader, VDIMAGETYPE enmType, uint32_t uImageFlags,
    126116                          const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    127117                          uint32_t cbBlockExtra)
     
    129119    pHeader->uVersion = VDI_IMAGE_VERSION;
    130120    pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
    131     pHeader->u.v1.u32Type = (uint32_t)enmType;
    132     pHeader->u.v1.fFlags = fFlags;
     121    pHeader->u.v1.u32Type = (uint32_t)(  enmType == VD_IMAGE_TYPE_NORMAL
     122                                       ? VDI_IMAGE_TYPE_NORMAL
     123                                       : VDI_IMAGE_TYPE_DIFF);
     124    pHeader->u.v1.fFlags = uImageFlags;
    133125#ifdef VBOX_STRICT
    134126    char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
     
    176168
    177169/**
    178  * internal: check HDD header.
     170 * Internal: Check VDI header.
    179171 */
    180172static int vdiValidateHeader(PVDIHEADER pHeader)
    181173{
    182     /* Check verion-dependend header parameters. */
     174    /* Check version-dependend header parameters. */
    183175    switch (GET_MAJOR_HEADER_VERSION(pHeader))
    184176    {
     
    213205            }
    214206
    215             if (    getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
    216                 ||  getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
    217             {
    218                 if (RTUuidIsNull(getImageParentUUID(pHeader)))
    219                 {
    220                     LogRel(("VDI: v1 uuid of parent is 0)\n"));
    221                     return VERR_VDI_INVALID_HEADER;
    222                 }
    223                 if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
    224                 {
    225                     LogRel(("VDI: v1 uuid of parent modification is 0\n"));
    226                     return VERR_VDI_INVALID_HEADER;
    227                 }
    228             }
    229 
    230207            break;
    231208        }
     
    246223    }
    247224
    248     if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
     225    if (getImageFlags(pHeader) & ~VD_VDI_IMAGE_FLAGS_MASK)
    249226    {
    250227        LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
     
    311288
    312289/**
    313  * internal: init VDIIMAGEDESC structure.
    314  */
    315 static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
    316 {
    317     pImage->pPrev = NULL;
    318     pImage->pNext = NULL;
    319     pImage->File = NIL_RTFILE;
    320     pImage->paBlocks = NULL;
    321 }
    322 
    323 /**
    324  * internal: setup VDIIMAGEDESC structure by image header.
     290 * Internal: Set up VDIIMAGEDESC structure by image header.
    325291 */
    326292static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
    327293{
    328     pImage->fFlags             = getImageFlags(&pImage->Header);
     294    pImage->uImageFlags        = getImageFlags(&pImage->Header);
    329295    pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
    330296    pImage->offStartData       = getImageDataOffset(&pImage->Header);
     
    338304
    339305/**
    340  * internal: create image.
     306 * Internal: Create VDI image file.
    341307 */
    342 static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    343                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    344                           PFNVMPROGRESS pfnProgress, void *pvUser)
    345 {
    346     /* Check args. */
    347     Assert(pszFilename);
    348     Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
    349     Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
    350     Assert(cbSize);
     308static int vdiCreateImage(PVDIIMAGEDESC pImage, VDIMAGETYPE enmType,
     309                          uint64_t cbSize, unsigned uImageFlags,
     310                          const char *pszComment,
     311                          PCPDMMEDIAGEOMETRY pPCHSGeometry,
     312                          PCPDMMEDIAGEOMETRY pLCHSGeometry,
     313                          PFNVMPROGRESS pfnProgress, void *pvUser,
     314                          unsigned uPercentStart, unsigned uPercentSpan)
     315{
     316    int rc;
     317    RTFILE File;
     318    uint64_t cbTotal;
     319    uint64_t cbFill;
     320    uint64_t uOff;
    351321
    352322    /* Special check for comment length. */
    353     if (    pszComment
    354         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    355     {
    356         Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    357         return VERR_VDI_COMMENT_TOO_LONG;
    358     }
    359 
    360     if (    enmType == VDI_IMAGE_TYPE_UNDO
    361         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    362     {
    363         Assert(pParent);
    364         if (VDI_GET_VERSION_MAJOR(pParent->PreHeader.u32Version) != VDI_IMAGE_VERSION_MAJOR)
    365         {
    366             /* Invalid parent image version. */
    367             Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
    368             return VERR_VDI_UNSUPPORTED_VERSION;
    369         }
    370 
    371         /* get image params from the parent image. */
    372         fFlags = getImageFlags(&pParent->Header);
    373         cbSize = getImageDiskSize(&pParent->Header);
    374     }
    375 
    376     PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    377     if (!pImage)
    378         return VERR_NO_MEMORY;
    379     vdiInitImageDesc(pImage);
     323    if (   VALID_PTR(pszComment)
     324        && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
     325    {
     326        rc = vdiError(pImage, VERR_VDI_COMMENT_TOO_LONG, RT_SRC_POS, N_("VDI: comment is too long for '%s'"), pImage->pszFilename);
     327        goto out;
     328    }
     329    Assert(VALID_PTR(pPCHSGeometry));
     330    Assert(VALID_PTR(pLCHSGeometry));
    380331
    381332    vdiInitPreHeader(&pImage->PreHeader);
    382     vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
    383 
    384     if (    enmType == VDI_IMAGE_TYPE_UNDO
    385         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    386     {
    387         /* Set up linkage information. */
    388         pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
    389         pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
    390     }
     333    vdiInitHeader(&pImage->Header, enmType, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
     334    /* Save PCHS geometry. Not much work, and makes the flow of information
     335     * quite a bit clearer - relying on the higher level isn't obvious. */
     336    pImage->PCHSGeometry = *pPCHSGeometry;
     337    /* Set LCHS geometry (legacy geometry is ignored for the current 1.1+). */
     338    pImage->Header.u.v1plus.LCHSGeometry.cCylinders = pLCHSGeometry->cCylinders;
     339    pImage->Header.u.v1plus.LCHSGeometry.cHeads = pLCHSGeometry->cHeads;
     340    pImage->Header.u.v1plus.LCHSGeometry.cSectors = pLCHSGeometry->cSectors;
     341    pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    391342
    392343    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    393344    if (!pImage->paBlocks)
    394345    {
    395         RTMemFree(pImage);
    396         return VERR_NO_MEMORY;
    397     }
    398 
    399     if (enmType != VDI_IMAGE_TYPE_FIXED)
     346        rc = VERR_NO_MEMORY;
     347        goto out;
     348    }
     349
     350    if (enmType != VD_IMAGE_TYPE_FIXED)
    400351    {
    401352        /* for growing images mark all blocks in paBlocks as free. */
     
    415366
    416367    /* create file */
    417     int rc = RTFileOpen(&pImage->File,
    418                         pszFilename,
    419                         RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    420     if (VBOX_SUCCESS(rc))
    421     {
    422         /* Lock image exclusively to close any wrong access by VDI API calls. */
    423         uint64_t cbLock = pImage->offStartData
    424                         + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    425 
    426         if (enmType == VDI_IMAGE_TYPE_FIXED)
    427         {
    428             /* check the free space on the disk and leave early if there is not
    429              * sufficient space available */
    430             RTFOFF cbFree = 0;
    431             rc = RTFsQuerySizes(pszFilename, NULL, &cbFree, NULL, NULL);
    432             if (VBOX_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbLock))
     368    rc = RTFileOpen(&File, pImage->pszFilename,
     369                    RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
     370    if (VBOX_FAILURE(rc))
     371    {
     372        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: cannot create image '%s'"), pImage->pszFilename);
     373        goto out;
     374    }
     375
     376    cbTotal =   pImage->offStartData
     377              + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
     378
     379    if (enmType == VD_IMAGE_TYPE_FIXED)
     380    {
     381        /* check the free space on the disk and leave early if there is not
     382         * sufficient space available */
     383        RTFOFF cbFree = 0;
     384        rc = RTFsQuerySizes(pImage->pszFilename, NULL, &cbFree, NULL, NULL);
     385        if (VBOX_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbTotal))
     386        {
     387            rc = vdiError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("VDI: disk would overflow creating image '%s'"), pImage->pszFilename);
     388            goto out;
     389        }
     390    }
     391
     392    if (enmType == VD_IMAGE_TYPE_FIXED)
     393    {
     394        /*
     395         * Allocate & commit whole file if fixed image, it must be more
     396         * effective than expanding file by write operations.
     397         */
     398        rc = RTFileSetSize(pImage->File, cbTotal);
     399    }
     400    else
     401    {
     402        /* Set file size to hold header and blocks array. */
     403        rc = RTFileSetSize(pImage->File, pImage->offStartData);
     404    }
     405    if (VBOX_FAILURE(rc))
     406    {
     407        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: setting image size failed for '%s'"), pImage->pszFilename);
     408        goto out;
     409    }
     410
     411    /* Generate image last-modify uuid */
     412    RTUuidCreate(getImageModificationUUID(&pImage->Header));
     413
     414    /* Write pre-header. */
     415    rc = RTFileWriteAt(pImage->File, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
     416    if (VBOX_FAILURE(rc))
     417    {
     418        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"), pImage->pszFilename);
     419        goto out;
     420    }
     421
     422    /* Write header. */
     423    rc = RTFileWriteAt(pImage->File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
     424    if (VBOX_FAILURE(rc))
     425    {
     426        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"), pImage->pszFilename);
     427        goto out;
     428    }
     429
     430    rc = RTFileWriteAt(pImage->File, pImage->offStartBlocks,
     431                       pImage->paBlocks,
     432                       getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
     433                       NULL);
     434    if (VBOX_FAILURE(rc))
     435    {
     436        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block pointers failed for '%s'"), pImage->pszFilename);
     437        goto out;
     438    }
     439
     440    if (enmType == VD_IMAGE_TYPE_FIXED)
     441    {
     442        /* Fill image with zeroes. We do this for every fixed-size image since on some systems
     443         * (for example Windows Vista), it takes ages to write a block near the end of a sparse
     444         * file and the guest could complain about an ATA timeout. */
     445
     446        /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
     447         *        Currently supported file systems are ext4 and ocfs2. */
     448
     449        /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
     450        const size_t cbBuf = 128 * _1K;
     451        void *pvBuf = RTMemTmpAllocZ(cbBuf);
     452        if (!pvBuf)
     453        {
     454            rc = VERR_NO_MEMORY;
     455            goto out;
     456        }
     457
     458        cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
     459        uOff = 0;
     460        /* do loop to fill all image. */
     461        while (uOff < cbFill)
     462        {
     463            unsigned cbChunk = (unsigned)RT_MIN(cbFill, cbBuf);
     464
     465            rc = RTFileWriteAt(pImage->File, pImage->offStartData + uOff,
     466                               pvBuf, cbChunk, NULL);
     467            if (VBOX_FAILURE(rc))
    433468            {
    434                 rc = VERR_DISK_FULL;
    435                 cbLock = 0;
    436                 goto l_create_failed;
     469                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block failed for '%s'"), pImage->pszFilename);
     470                goto out;
    437471            }
    438         }
    439 
    440         rc = RTFileLock(pImage->File,
    441                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    442         if (VBOX_FAILURE(rc))
    443         {
    444             cbLock = 0;    /* Not locked. */
    445             goto l_create_failed;
    446         }
    447 
    448         if (enmType == VDI_IMAGE_TYPE_FIXED)
    449         {
    450             /*
    451              * Allocate & commit whole file if fixed image, it must be more
    452              * effective than expanding file by write operations.
    453              */
    454             rc = RTFileSetSize(pImage->File, cbLock);
    455         }
    456         else
    457         {
    458             /* Set file size to hold header and blocks array. */
    459             rc = RTFileSetSize(pImage->File, pImage->offStartData);
    460         }
    461         if (VBOX_FAILURE(rc))
    462             goto l_create_failed;
    463 
    464         /* Generate image last-modify uuid */
    465         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    466 
    467         /* Write pre-header. */
    468         rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    469         if (VBOX_FAILURE(rc))
    470             goto l_create_failed;
    471 
    472         /* Write header. */
    473         rc = RTFileWrite(pImage->File, &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
    474         if (VBOX_FAILURE(rc))
    475             goto l_create_failed;
    476 
    477         /* Write blocks array. */
    478         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    479         if (VBOX_FAILURE(rc))
    480             goto l_create_failed;
    481         rc = RTFileWrite(pImage->File,
    482                          pImage->paBlocks,
    483                          getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
    484                          NULL);
    485         if (VBOX_FAILURE(rc))
    486             goto l_create_failed;
    487 
    488         if (enmType == VDI_IMAGE_TYPE_FIXED)
    489         {
    490             /* Fill image with zeroes. We do this for every fixed-size image since on some systems
    491              * (for example Windows Vista), it takes ages to write a block near the end of a sparse
    492              * file and the guest could complain about an ATA timeout. */
    493 
    494             /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
    495              *        Currently supported file systems are ext4 and ocfs2. */
    496 
    497             rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
    498             if (VBOX_FAILURE(rc))
    499                 goto l_create_failed;
    500 
    501             /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
    502             const size_t cbBuf = 128 * _1K;
    503             void *pvBuf = RTMemTmpAllocZ(cbBuf);
    504             if (pvBuf)
     472
     473            uOff += cbChunk;
     474
     475            if (pfnProgress)
    505476            {
    506                 uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
    507                 uint64_t cbDisk = cbFill;
    508 
    509                 /* do loop to fill all image. */
    510                 while (cbFill > 0)
    511                 {
    512                     unsigned to_fill = (unsigned)RT_MIN(cbFill, cbBuf);
    513 
    514                     rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    515                     if (VBOX_FAILURE(rc))
    516                         break;
    517 
    518                     cbFill -= to_fill;
    519 
    520                     if (pfnProgress)
    521                     {
    522                         rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    523                                          (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
    524                                          pvUser);
    525                         if (VBOX_FAILURE(rc))
    526                             break;
    527                     }
    528                 }
    529                 RTMemTmpFree(pvBuf);
     477                rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
     478                                 uPercentStart + uOff * uPercentSpan / cbFill,
     479                                 pvUser);
     480                if (VBOX_FAILURE(rc))
     481                    goto out;
    530482            }
    531             else
    532             {
    533                 /* alloc error */
    534                 rc = VERR_NO_MEMORY;
    535             }
    536         }
    537 
    538     l_create_failed:
    539 
    540         if (cbLock)
    541             RTFileUnlock(pImage->File, 0, cbLock);
    542 
    543         RTFileClose(pImage->File);
    544 
    545         /* Delete image file if error occured while creating */
    546         if (VBOX_FAILURE(rc))
    547             RTFileDelete(pszFilename);
    548     }
    549 
    550     RTMemFree(pImage->paBlocks);
    551     RTMemFree(pImage);
    552 
    553     if (    VBOX_SUCCESS(rc)
    554         &&  pfnProgress)
    555         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    556 
    557     Log(("vdiCreateImage: done, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
    558 
     483        }
     484        RTMemTmpFree(pvBuf);
     485    }
     486
     487out:
     488    if (VBOX_SUCCESS(rc) && pfnProgress)
     489        pfnProgress(NULL /* WARNING! pVM=NULL  */,
     490                    uPercentStart + uPercentSpan, pvUser);
     491
     492    if (VBOX_FAILURE(rc))
     493        vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
    559494    return rc;
    560495}
    561496
    562497/**
    563  * Open an image.
    564  * @internal
     498 * Internal: Open a VDI image.
    565499 */
    566 static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename,
    567                         unsigned fOpen, PVDIIMAGEDESC pParent)
    568 {
    569     /*
    570      * Validate input.
    571      */
    572     Assert(ppImage);
    573     Assert(pszFilename);
    574     Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
    575 
    576     PVDIIMAGEDESC   pImage;
    577     size_t          cchFilename = strlen(pszFilename);
    578     if (cchFilename >= sizeof(pImage->szFilename))
    579     {
    580         AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
    581         return VERR_FILENAME_TOO_LONG;
    582     }
    583 
    584     pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    585     if (!pImage)
    586         return VERR_NO_MEMORY;
    587     vdiInitImageDesc(pImage);
    588 
    589     memcpy(pImage->szFilename, pszFilename, cchFilename);
    590     pImage->fOpen = fOpen;
     500static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
     501{
     502    int rc;
     503    RTFILE File;
     504
     505    pImage->uOpenFlags = uOpenFlags;
    591506
    592507    /*
    593508     * Open the image.
    594509     */
    595     int rc = RTFileOpen(&pImage->File,
    596                         pImage->szFilename,
    597                         fOpen & VDI_OPEN_FLAGS_READONLY
    598                         ? RTFILE_O_READ      | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
    599                         : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
     510    rc = RTFileOpen(&File, pImage->pszFilename,
     511                    uOpenFlags & VD_OPEN_FLAGS_READONLY
     512                     ? RTFILE_O_READ      | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
     513                     : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    600514    if (VBOX_FAILURE(rc))
    601515    {
    602         if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
    603         {
    604             /* Try to open image for reading only. */
    605             rc = RTFileOpen(&pImage->File,
    606                             pImage->szFilename,
    607                             RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    608             if (VBOX_SUCCESS(rc))
    609                 pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
    610         }
    611         if (VBOX_FAILURE(rc))
    612         {
    613             RTMemFree(pImage);
    614             return rc;
    615         }
    616     }
    617     /* Set up current image r/w state. */
    618     pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
    619 
    620     /*
    621      * Set initial file lock for reading header only.
    622      * Length of lock doesn't matter, it just must include image header.
    623      */
    624     uint64_t cbLock = _1M;
    625     rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
     516        /* Do NOT signal an appropriate error here, as the VD layer has the
     517         * choice of retrying the open if it failed. */
     518        goto out;
     519    }
     520    pImage->File = File;
     521
     522    /* Read pre-header. */
     523    rc = RTFileReadAt(File, 0, &pImage->PreHeader, sizeof(pImage->PreHeader),
     524                      NULL);
    626525    if (VBOX_FAILURE(rc))
    627526    {
    628         cbLock = 0;
    629         goto l_open_failed;
    630     }
    631 
    632     /* Read pre-header. */
    633     rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    634     if (VBOX_FAILURE(rc))
    635         goto l_open_failed;
     527        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
     528        goto out;
     529    }
    636530    rc = vdiValidatePreHeader(&pImage->PreHeader);
    637531    if (VBOX_FAILURE(rc))
    638         goto l_open_failed;
     532    {
     533        rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: invalid pre-header in '%s'"), pImage->pszFilename);
     534        goto out;
     535    }
    639536
    640537    /* Read header. */
     
    643540    {
    644541        case 0:
    645             rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
     542            rc = RTFileReadAt(File, sizeof(pImage->PreHeader),
     543                              &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
     544                              NULL);
     545            if (VBOX_FAILURE(rc))
     546            {
     547                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
     548                goto out;
     549            }
    646550            break;
    647551        case 1:
    648             switch (GET_MINOR_HEADER_VERSION(&pImage->Header))
     552            rc = RTFileReadAt(File, sizeof(pImage->PreHeader),
     553                              &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
     554                              NULL);
     555            if (VBOX_FAILURE(rc))
    649556            {
    650                 case 1:
    651                     rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    652                     /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write
    653                      * mode. Conversion is harmless, as any VirtualBox version
    654                      * supporting VDI 1.1 doesn't touch fields it doesn't know
    655                      * about. And it accepts bigger headers. */
    656                     if (   VBOX_SUCCESS(rc)
    657                         && !pImage->fReadOnly
    658                         && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
    659                     {
    660                         pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
    661                         /* Mark LCHS geometry not-calculated. */
    662                         pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
    663                         pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
    664                         pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
    665                         pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    666                     }
    667                     else if (   VBOX_SUCCESS(rc)
    668                              && pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
    669                     {
    670                         /* Read the actual VDI 1.1+ header completely. */
    671                         rc = RTFileReadAt(pImage->File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
    672                     }
    673                     break;
    674                 default:
    675                     rc = VERR_VDI_UNSUPPORTED_VERSION;
    676                     break;
     557                rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
     558                goto out;
    677559            }
     560            /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write mode.
     561             * Conversion is harmless, as any VirtualBox version supporting VDI
     562             * 1.1 doesn't touch fields it doesn't know about. */
     563            if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
     564                && GET_MINOR_HEADER_VERSION(&pImage->Header) == 1
     565                && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
     566            {
     567                pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
     568                /* Mark LCHS geometry not-calculated. */
     569                pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
     570                pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
     571                pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
     572                pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
     573            }
     574            else if (pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
     575            {
     576                /* Read the actual VDI 1.1+ header completely. */
     577                rc = RTFileReadAt(File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
     578                if (VBOX_FAILURE(rc))
     579                {
     580                    rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
     581                    goto out;
     582                }
     583            }
     584            break;
     585        default:
     586            rc = vdiError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: unsupported major version %u in '%s'"), GET_MAJOR_HEADER_VERSION(&pImage->Header), pImage->pszFilename);
     587            goto out;
     588    }
     589
     590    rc = vdiValidateHeader(&pImage->Header);
     591    if (VBOX_FAILURE(rc))
     592    {
     593        rc = vdiError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: invalid header in '%s'"), pImage->pszFilename);
     594        goto out;
     595    }
     596
     597    /* Setup image parameters by header. */
     598    vdiSetupImageDesc(pImage);
     599
     600    /* Allocate memory for blocks array. */
     601    pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
     602    if (!pImage->paBlocks)
     603    {
     604        rc = VERR_NO_MEMORY;
     605        goto out;
     606    }
     607
     608    /* Read blocks array. */
     609    rc = RTFileReadAt(pImage->File, pImage->offStartBlocks, pImage->paBlocks,
     610                      getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
     611                      NULL);
     612
     613out:
     614    if (VBOX_FAILURE(rc))
     615        vdiFreeImage(pImage, false);
     616    return rc;
     617}
     618
     619/**
     620 * Internal: Save header to file.
     621 */
     622static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
     623{
     624    int rc;
     625    switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
     626    {
     627        case 0:
     628            rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
     629            break;
     630        case 1:
     631            if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
     632                rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
     633            else
     634                rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
    678635            break;
    679636        default:
     
    681638            break;
    682639    }
    683     if (VBOX_FAILURE(rc))
    684         goto l_open_failed;
    685 
    686     rc = vdiValidateHeader(&pImage->Header);
    687     if (VBOX_FAILURE(rc))
    688         goto l_open_failed;
    689 
    690     /* Check diff image correctness. */
    691     if (pParent)
    692     {
    693         if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
    694         {
    695             rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
    696             goto l_open_failed;
    697         }
    698 
    699         if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
    700             &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
    701         {
    702             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    703             goto l_open_failed;
    704         }
    705 
    706         if (    getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
    707             ||  getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
    708             ||  getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
    709             ||  getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
    710         {
    711             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    712             goto l_open_failed;
    713         }
    714 
    715         /* Check linkage data. */
    716         if (    RTUuidCompare(getImageParentUUID(&pImage->Header),
    717                               getImageCreationUUID(&pParent->Header))
    718             ||  RTUuidCompare(getImageParentModificationUUID(&pImage->Header),
    719                               getImageModificationUUID(&pParent->Header)))
    720         {
    721             rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    722             goto l_open_failed;
    723         }
    724     }
    725 
    726     /* Setup image parameters by header. */
    727     vdiSetupImageDesc(pImage);
    728 
    729     /* reset modified flag into first-modified state. */
    730     pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
    731 
    732     /* Image is validated, set working file lock on it. */
    733     rc = RTFileUnlock(pImage->File, 0, cbLock);
    734     AssertRC(rc);
    735     cbLock = pImage->offStartData
    736            + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    737     rc = RTFileLock(pImage->File,
    738                     (pImage->fReadOnly) ?
    739                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    740                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    741                     0,
    742                     cbLock);
    743     if (    VBOX_FAILURE(rc)
    744         &&  !pImage->fReadOnly)
    745     {
    746         /* Failed to lock image for writing, try read-only lock. */
    747         rc = RTFileLock(pImage->File,
    748                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    749         if (VBOX_SUCCESS(rc))
    750             pImage->fReadOnly = true;
    751     }
    752     if (VBOX_FAILURE(rc))
    753     {
    754         cbLock = 0;    /* Not locked. */
    755         goto l_open_failed;
    756     }
    757 
    758     /* Allocate memory for blocks array. */
    759     pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    760     if (!pImage->paBlocks)
    761     {
    762         rc = VERR_NO_MEMORY;
    763         goto l_open_failed;
    764     }
    765 
    766     /* Read blocks array. */
    767     rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    768     if (VBOX_FAILURE(rc))
    769         goto l_open_failed;
    770     rc = RTFileRead(pImage->File, pImage->paBlocks,
    771                     getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), NULL);
    772     if (VBOX_FAILURE(rc))
    773         goto l_open_failed;
    774 
    775     /* all done. */
    776     *ppImage = pImage;
    777     return VINF_SUCCESS;
    778 
    779 l_open_failed:
    780     /* Clean up. */
    781     if (pImage->paBlocks)
    782         RTMemFree(pImage->paBlocks);
    783     if (cbLock)
    784         RTFileUnlock(pImage->File, 0, cbLock);
    785     RTFileClose(pImage->File);
    786     RTMemFree(pImage);
    787     Log(("vdiOpenImage: failed, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
     640    AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->pszFilename, rc));
    788641    return rc;
    789642}
    790643
    791644/**
    792  * internal: save header to file.
    793  */
    794 static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
    795 {
    796     /* Seek to header start. */
    797     int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
    798     if (VBOX_SUCCESS(rc))
    799     {
    800         switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    801         {
    802             case 0:
    803                 rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
    804                 break;
    805             case 1:
    806                 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    807                 {
    808                     case 1:
    809                         if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
    810                             rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    811                         else
    812                             rc = RTFileWrite(pImage->File, &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
    813                         break;
    814                     default:
    815                         rc = VERR_VDI_UNSUPPORTED_VERSION;
    816                         break;
    817                 }
    818                 break;
    819             default:
    820                 rc = VERR_VDI_UNSUPPORTED_VERSION;
    821                 break;
    822         }
    823     }
    824     AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->szFilename, rc));
    825     return rc;
    826 }
    827 
    828 /**
    829  * internal: save block pointer to file, save header to file.
     645 * Internal: Save block pointer to file, save header to file.
    830646 */
    831647static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
     
    836652    {
    837653        /* write only one block pointer. */
    838         rc = RTFileSeek(pImage->File,
    839                         pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
    840                         RTFILE_SEEK_BEGIN,
    841                         NULL);
    842         if (VBOX_SUCCESS(rc))
    843             rc = RTFileWrite(pImage->File,
    844                              &pImage->paBlocks[uBlock],
    845                              sizeof(VDIIMAGEBLOCKPOINTER),
    846                              NULL);
     654        rc = RTFileWriteAt(pImage->File,
     655                           pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
     656                           &pImage->paBlocks[uBlock],
     657                           sizeof(VDIIMAGEBLOCKPOINTER),
     658                           NULL);
    847659        AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
    848                          uBlock, pImage->szFilename, rc));
     660                         uBlock, pImage->pszFilename, rc));
    849661    }
    850662    return rc;
     
    852664
    853665/**
    854  * internal: save blocks array to file, save header to file.
     666 * Internal: Flush the image file to disk.
    855667 */
    856 static int vdiUpdateBlocks(PVDIIMAGEDESC pImage)
    857 {
    858     /* Update image header. */
    859     int rc = vdiUpdateHeader(pImage);
    860     if (VBOX_SUCCESS(rc))
    861     {
    862         /* write the block pointers array. */
    863         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    864         if (VBOX_SUCCESS(rc))
    865             rc = RTFileWrite(pImage->File,
    866                              pImage->paBlocks,
    867                              sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header),
    868                              NULL);
    869         AssertMsgRC(rc, ("vdiUpdateBlocks failed, filename=\"%s\", rc=%Vrc\n",
    870                          pImage->szFilename, rc));
    871     }
    872     return rc;
    873 }
    874 
    875 /**
    876  * internal: mark image as modified, if this is the first change - update image header
    877  * on disk with a new uuidModify value.
    878  */
    879 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
    880 {
    881     pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
    882     if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
    883     {
    884         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
    885 
    886         /* first modify - generate uuidModify and save to file. */
    887         vdiResetModifiedFlag(pImage);
    888 
    889         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    890         {
    891             /* save header to file,
    892              * note: no rc checking.
    893              */
    894             vdiUpdateHeader(pImage);
    895         }
    896     }
    897 }
    898 
    899 /**
    900  * internal: generate new uuidModify if the image was changed.
    901  */
    902 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
    903 {
    904     if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
    905     {
    906         /* generate new last-modified uuid */
    907         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    908             RTUuidCreate(getImageModificationUUID(&pImage->Header));
    909 
    910         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
    911     }
    912 }
    913 
    914 /**
    915  * internal: disables updates of the last-modified UUID
    916  * when performing image writes.
    917  */
    918 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    919 {
    920     pImage->fModified |= VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    921 }
    922 
    923 #if 0 /* unused */
    924 /**
    925  * internal: enables updates of the last-modified UUID
    926  * when performing image writes.
    927  */
    928 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    929 {
    930     pImage->fModified &= ~VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    931 }
    932 #endif
    933 
    934 /**
    935  * Flush the image file to disk.
    936  */
    937 void vdiFlushImage(PVDIIMAGEDESC pImage)
    938 {
    939     if (!pImage->fReadOnly)
    940     {
    941         /* Update last-modified uuid if need. */
    942         vdiResetModifiedFlag(pImage);
    943 
     668static void vdiFlushImage(PVDIIMAGEDESC pImage)
     669{
     670    if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
     671    {
    944672        /* Save header. */
    945673        int rc = vdiUpdateHeader(pImage);
    946674        AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    947                          pImage->szFilename, rc));
     675                         pImage->pszFilename, rc));
    948676        RTFileFlush(pImage->File);
    949677    }
     
    951679
    952680/**
    953  * internal: close image file.
     681 * Internal: Free all allocated space for representing an image, and optionally
     682 * delete the image from disk.
    954683 */
    955 static void vdiCloseImage(PVDIIMAGEDESC pImage)
    956 {
    957     /* Params checking. */
    958     Assert(pImage);
    959     Assert(pImage->File != NIL_RTFILE);
     684static void vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
     685{
     686    Assert(pImage);
    960687
    961688    vdiFlushImage(pImage);
    962     RTFileUnlock(pImage->File,
    963                  0,
    964                  pImage->offStartData
    965                + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
    966     RTFileClose(pImage->File);
    967 
    968     /* free image resources */
    969     RTMemFree(pImage->paBlocks);
    970     RTMemFree(pImage);
    971 }
    972 
    973 /**
    974  * internal: read data inside image block.
    975  *
    976  * note: uBlock must be valid, readed data must not overlap block bounds.
    977  */
    978 static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    979                           size_t cbToRead, void *pvBuf)
    980 {
    981     if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
     689    if (pImage->File != NIL_RTFILE)
     690    {
     691        RTFileClose(pImage->File);
     692        pImage->File = NIL_RTFILE;
     693    }
     694    if (pImage->paBlocks)
     695    {
     696        RTMemFree(pImage->paBlocks);
     697        pImage->paBlocks = NULL;
     698    }
     699    if (fDelete && pImage->pszFilename)
     700        RTFileDelete(pImage->pszFilename);
     701}
     702
     703
     704/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
     705static int vdiCheckIfValid(const char *pszFilename)
     706{
     707    LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
     708    int rc = VINF_SUCCESS;
     709    PVDIIMAGEDESC pImage;
     710
     711    if (   !pszFilename
     712        || !*pszFilename)
     713    {
     714        rc = VERR_INVALID_PARAMETER;
     715        goto out;
     716    }
     717
     718    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
     719    if (!pImage)
     720    {
     721        rc = VERR_NO_MEMORY;
     722        goto out;
     723    }
     724    pImage->pszFilename = pszFilename;
     725
     726    rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
     727    vdiFreeImage(pImage, false);
     728
     729out:
     730    LogFlowFunc(("returns %Vrc\n", rc));
     731    return rc;
     732}
     733
     734/** @copydoc VBOXHDDBACKEND::pfnOpen */
     735static int vdiOpen(const char *pszFilename, unsigned uOpenFlags,
     736                   PFNVDERROR pfnError, void *pvErrorUser,
     737                   void **ppBackendData)
     738{
     739    LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x ppBackendData=%#p\n", pszFilename, uOpenFlags, ppBackendData));
     740    int rc;
     741    PVDIIMAGEDESC pImage;
     742
     743    /* Check open flags. All valid flags are supported. */
     744    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
     745    {
     746        rc = VERR_INVALID_PARAMETER;
     747        goto out;
     748    }
     749
     750    /* Check remaining arguments. */
     751    if (   !VALID_PTR(pszFilename)
     752        || !*pszFilename)
     753    {
     754        rc = VERR_INVALID_PARAMETER;
     755        goto out;
     756    }
     757
     758    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
     759    if (!pImage)
     760    {
     761        rc = VERR_NO_MEMORY;
     762        goto out;
     763    }
     764    pImage->pszFilename = pszFilename;
     765    pImage->paBlocks = NULL;
     766    pImage->pfnError = pfnError;
     767    pImage->pvErrorUser = pvErrorUser;
     768
     769    rc = vdiOpenImage(pImage, uOpenFlags);
     770    if (VBOX_SUCCESS(rc))
     771        *ppBackendData = pImage;
     772
     773out:
     774    LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData));
     775    return rc;
     776}
     777
     778/** @copydoc VBOXHDDBACKEND::pfnCreate */
     779static int vdiCreate(const char *pszFilename, VDIMAGETYPE enmType,
     780                     uint64_t cbSize, unsigned uImageFlags,
     781                     const char *pszComment,
     782                     PCPDMMEDIAGEOMETRY pPCHSGeometry,
     783                     PCPDMMEDIAGEOMETRY pLCHSGeometry,
     784                     unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
     785                     void *pvUser, unsigned uPercentStart,
     786                     unsigned uPercentSpan, PFNVDERROR pfnError,
     787                     void *pvErrorUser, void **ppBackendData)
     788{
     789    LogFlowFunc(("pszFilename=\"%s\" enmType=%d cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p uOpenFlags=%#x pfnProgress=%#p pvUser=%#p uPercentStart=%u uPercentSpan=%u pfnError=%#p pvErrorUser=%#p ppBackendData=%#p", pszFilename, enmType, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, uOpenFlags, pfnProgress, pvUser, uPercentStart, uPercentSpan, pfnError, pvErrorUser, ppBackendData));
     790    int rc;
     791    PVDIIMAGEDESC pImage;
     792
     793    /* Check open flags. All valid flags are supported. */
     794    if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
     795    {
     796        rc = VERR_INVALID_PARAMETER;
     797        goto out;
     798    }
     799
     800    /* Check remaining arguments. */
     801    if (   !pszFilename
     802        || !*pszFilename
     803        || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
     804        || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
     805        || !VALID_PTR(pPCHSGeometry)
     806        || !VALID_PTR(pLCHSGeometry))
     807    {
     808        rc = VERR_INVALID_PARAMETER;
     809        goto out;
     810    }
     811
     812    pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
     813    if (!pImage)
     814    {
     815        rc = VERR_NO_MEMORY;
     816        goto out;
     817    }
     818    pImage->pszFilename = pszFilename;
     819    pImage->paBlocks = NULL;
     820    pImage->pfnError = pfnError;
     821    pImage->pvErrorUser = pvErrorUser;
     822
     823    rc = vdiCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment,
     824                        pPCHSGeometry, pLCHSGeometry,
     825                        pfnProgress, pvUser, uPercentStart, uPercentSpan);
     826    if (VBOX_SUCCESS(rc))
     827    {
     828        /* So far the image is opened in read/write mode. Make sure the
     829         * image is opened in read-only mode if the caller requested that. */
     830        if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
     831        {
     832            vdiFreeImage(pImage, false);
     833            rc = vdiOpenImage(pImage, uOpenFlags);
     834            if (VBOX_FAILURE(rc))
     835                goto out;
     836        }
     837        *ppBackendData = pImage;
     838    }
     839
     840out:
     841    LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData));
     842    return rc;
     843}
     844
     845/** @copydoc VBOXHDDBACKEND::pfnRename */
     846static int vdiRename(void *pBackendData, const char *pszFilename)
     847{
     848    LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
     849    int rc = VERR_NOT_IMPLEMENTED;
     850
     851    LogFlowFunc(("returns %Vrc\n", rc));
     852    return rc;
     853}
     854
     855/** @copydoc VBOXHDDBACKEND::pfnClose */
     856static int vdiClose(void *pBackendData, bool fDelete)
     857{
     858    LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
     859    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     860    int rc = VINF_SUCCESS;
     861
     862    /* Freeing a never allocated image (e.g. because the open failed) is
     863     * not signalled as an error. After all nothing bad happens. */
     864    if (pImage)
     865        vdiFreeImage(pImage, fDelete);
     866
     867    LogFlowFunc(("returns %Vrc\n", rc));
     868    return rc;
     869}
     870
     871/** @copydoc VBOXHDDBACKEND::pfnRead */
     872static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
     873                   size_t cbToRead, size_t *pcbActuallyRead)
     874{
     875    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
     876    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     877    unsigned uBlock;
     878    unsigned offRead;
     879    int rc;
     880
     881    Assert(pImage);
     882    Assert(uOffset % 512 == 0);
     883    Assert(cbToRead % 512 == 0);
     884
     885    if (   uOffset + cbToRead > getImageDiskSize(&pImage->Header)
     886        || cbToRead == 0)
     887    {
     888        rc = VERR_INVALID_PARAMETER;
     889        goto out;
     890    }
     891
     892    /* Calculate starting block number and offset inside it. */
     893    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
     894    offRead = (unsigned)uOffset & pImage->uBlockMask;
     895
     896    /* Clip read range to at most the rest of the block. */
     897    cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
     898    Assert(!(cbToRead % 512));
     899
     900    if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
     901        rc = VERR_VDI_BLOCK_FREE;
     902    else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
     903    {
     904        memset(pvBuf, 0, cbToRead);
     905        rc = VINF_SUCCESS;
     906    }
     907    else
    982908    {
    983909        /* block present in image file */
    984910        uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    985911                           + (pImage->offStartData + pImage->offStartBlockData + offRead);
    986         int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    987         if (VBOX_SUCCESS(rc))
    988             rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
    989         if (VBOX_FAILURE(rc))
    990             Log(("vdiReadInBlock: rc=%Vrc filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
    991                  rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
    992         return rc;
    993     }
    994 
    995     /* Returns zeroes for both free and zero block types. */
    996     memset(pvBuf, 0, cbToRead);
    997     return VINF_SUCCESS;
    998 }
    999 
    1000 /**
    1001  * Read data from virtual HDD.
    1002  *
    1003  * @returns VBox status code.
    1004  * @param   pDisk           Pointer to VDI HDD container.
    1005  * @param   offStart        Offset of first reading byte from start of disk.
    1006  * @param   pvBuf           Pointer to buffer for reading data.
    1007  * @param   cbToRead        Number of bytes to read.
    1008  */
    1009 VBOXDDU_DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, size_t cbToRead)
    1010 {
    1011     /* sanity check */
    1012     Assert(pDisk);
    1013     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1014 
    1015     PVDIIMAGEDESC pImage = pDisk->pLast;
    1016     Assert(pImage);
    1017 
    1018     /* Check params. */
    1019     if (    offStart + cbToRead > getImageDiskSize(&pImage->Header)
    1020         ||  cbToRead == 0)
    1021     {
    1022         AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
    1023         return VERR_INVALID_PARAMETER;
    1024     }
     912        rc = RTFileReadAt(pImage->File, u64Offset, pvBuf, cbToRead, NULL);
     913    }
     914
     915    if (VBOX_SUCCESS(rc))
     916        *pcbActuallyRead = cbToRead;
     917
     918out:
     919    LogFlowFunc(("returns %Vrc\n", rc));
     920    return rc;
     921}
     922
     923/**@copydoc VBOXHDDBACKEND::pfnWrite */
     924static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
     925                    size_t cbToWrite, size_t *pcbWriteProcess,
     926                    size_t *pcbPreRead, size_t *pcbPostRead)
     927{
     928    LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
     929    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     930    unsigned uBlock;
     931    unsigned offWrite;
     932    int rc = VINF_SUCCESS;
     933
     934    Assert(pImage);
     935    Assert(uOffset % 512 == 0);
     936    Assert(cbToWrite % 512 == 0);
     937
     938    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
     939    {
     940        rc = VERR_VDI_IMAGE_READ_ONLY;
     941        goto out;
     942    }
     943
     944    if (cbToWrite == 0)
     945    {
     946        rc = VERR_INVALID_PARAMETER;
     947        goto out;
     948    }
     949
     950    /* No size check here, will do that later.  For dynamic images which are
     951     * not multiples of the block size in length, this would prevent writing to
     952     * the last grain. */
    1025953
    1026954    /* Calculate starting block number and offset inside it. */
    1027     unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1028     unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
    1029 
    1030     /* Save block size here for speed optimization. */
    1031     unsigned cbBlock = getImageBlockSize(&pImage->Header);
    1032 
    1033     /* loop through blocks */
    1034     int rc;
    1035     for (;;)
    1036     {
    1037         size_t to_read;
    1038         if ((offRead + cbToRead) <= cbBlock)
    1039             to_read = cbToRead;
    1040         else
    1041             to_read = cbBlock - offRead;
    1042 
    1043         if (pDisk->cImages > 1)
    1044         {
    1045             /* Differencing images are used, handle them. */
    1046             pImage = pDisk->pLast;
    1047 
    1048             /* Search for image with allocated block. */
    1049             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1050             {
    1051                 pImage = pImage->pPrev;
    1052                 if (!pImage)
    1053                 {
    1054                     /* Block is not allocated in all images of chain. */
    1055                     pImage = pDisk->pLast;
    1056                     break;
    1057                 }
    1058             }
    1059         }
    1060 
    1061         rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
    1062 
    1063         cbToRead -= to_read;
    1064         if (    cbToRead == 0
    1065             ||  VBOX_FAILURE(rc))
    1066             break;
    1067 
    1068         /* goto next block */
    1069         uBlock++;
    1070         offRead = 0;
    1071         pvBuf = (char *)pvBuf + to_read;
    1072     }
    1073 
    1074     return rc;
    1075 }
    1076 
    1077 /**
    1078  * internal: fill the whole block with zeroes.
    1079  *
    1080  * note: block id must be valid, block must be already allocated in file.
    1081  * note: if pDisk is NULL, the default buffer size is used
    1082  */
    1083 static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1084 {
    1085     int rc;
    1086 
    1087     /* seek to start of block in file. */
    1088     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1089                        + (pImage->offStartData + pImage->offStartBlockData);
    1090     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1091     if (VBOX_FAILURE(rc))
    1092     {
    1093         Log(("vdiFillBlockByZeroes: seek rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu\n",
    1094              rc, pImage->szFilename, uBlock, u64Offset));
    1095         return rc;
    1096     }
    1097 
    1098     /* alloc tmp zero-filled buffer */
    1099     void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1100     if (!pvBuf)
    1101         return VERR_NO_MEMORY;
    1102 
    1103     unsigned cbFill = getImageBlockSize(&pImage->Header);
    1104 
    1105     /* do loop, because buffer size may be less then block size */
    1106     while (cbFill > 0)
    1107     {
    1108         unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1109         rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    1110         if (VBOX_FAILURE(rc))
    1111         {
    1112             Log(("vdiFillBlockByZeroes: write rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
    1113                  rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
    1114             break;
    1115         }
    1116 
    1117         cbFill -= to_fill;
    1118     }
    1119 
    1120     RTMemTmpFree(pvBuf);
    1121     return rc;
    1122 }
    1123 
    1124 /**
    1125  * internal: write data inside image block.
    1126  *
    1127  * note: uBlock must be valid, written data must not overlap block bounds.
    1128  */
    1129 static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, size_t cbToWrite, const void *pvBuf)
    1130 {
    1131     int rc;
    1132 
    1133     /* Check if we can write into file. */
    1134     if (pImage->fReadOnly)
    1135     {
    1136         Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
    1137         return VERR_WRITE_PROTECT;
    1138     }
    1139 
    1140     /* This could be optimized a little (not setting it when writing zeroes
    1141      * to a zeroed block). Won't buy us much, because it's very unlikely
    1142      * that only such zero data block writes occur while the VDI is opened. */
    1143     vdiSetModifiedFlag(pImage);
     955    uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
     956    offWrite = (unsigned)uOffset & pImage->uBlockMask;
     957
     958    /* Clip write range to at most the rest of the block. */
     959    cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
     960    Assert(!(cbToWrite % 512));
    1144961
    1145962    if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    1146963    {
    1147         if (!pDisk || !pDisk->fHonorZeroWrites)
    1148         {
    1149             /* If the destination block is unallocated at this point, it's either
    1150              * a zero block or a block which hasn't been used so far (which also
    1151              * means that it's a zero block. Don't need to write anything to this
    1152              * block if the data consists of just zeroes. */
     964        /* Block is either free or zero. */
     965        if (   !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
     966            && (   pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
     967                || cbToWrite == getImageBlockSize(&pImage->Header)))
     968        {
     969            /* If the destination block is unallocated at this point, it's
     970             * either a zero block or a block which hasn't been used so far
     971             * (which also means that it's a zero block. Don't need to write
     972             * anything to this block  if the data consists of just zeroes. */
    1153973            Assert(cbToWrite % 4 == 0);
    1154             if (ASMBitFirstSet((volatile void *)pvBuf, cbToWrite * 8) == -1)
     974            Assert(cbToWrite * 8 <= UINT32_MAX);
     975            if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
    1155976            {
    1156977                pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1157                 return VINF_SUCCESS;
     978                goto out;
    1158979            }
    1159980        }
    1160981
    1161         /* need to allocate a new block in image file */
    1162 
    1163         /* expand file by one block */
    1164         uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
    1165                          + pImage->offStartData;
    1166         rc = RTFileSetSize(pImage->File, u64Size);
    1167         if (VBOX_FAILURE(rc))
    1168         {
    1169             Log(("vdiWriteInBlock: set size rc=%Vrc filename=\"%s\" uBlock=%u u64Size=%llu\n",
    1170                  rc, pImage->szFilename, uBlock, u64Size));
    1171             return rc;
    1172         }
    1173 
    1174         unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    1175         pImage->paBlocks[uBlock] = cBlocksAllocated;
    1176         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
    1177 
    1178         if (    pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
    1179             ||  pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1180         {
    1181             /* Fill newly allocated block by zeroes. */
    1182 
    1183             if (offWrite || cbToWrite != getImageBlockSize(&pImage->Header))
    1184             {
    1185                 rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
    1186                 if (VBOX_FAILURE(rc))
    1187                     return rc;
    1188             }
    1189         }
    1190 
    1191         rc = vdiUpdateBlockInfo(pImage, uBlock);
    1192         if (VBOX_FAILURE(rc))
    1193             return rc;
    1194     }
    1195 
    1196     /* Now block present in image file, write data inside it. */
    1197     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1198                        + (pImage->offStartData + pImage->offStartBlockData + offWrite);
    1199     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1200     if (VBOX_SUCCESS(rc))
    1201     {
    1202         rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
    1203         if (VBOX_FAILURE(rc))
    1204             Log(("vdiWriteInBlock: write rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
    1205                  rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
    1206     }
    1207     else
    1208         Log(("vdiWriteInBlock: seek rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
    1209              rc, pImage->szFilename, uBlock, offWrite, u64Offset));
    1210 
    1211     return rc;
    1212 }
    1213 
    1214 /**
    1215  * internal: copy data block from one (parent) image to last image.
    1216  */
    1217 static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1218 {
    1219     Assert(pImage != pDisk->pLast);
    1220 
    1221     if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1222     {
    1223         /*
    1224          * if src block is zero, set dst block to zero too.
    1225          */
    1226         pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1227         return VINF_SUCCESS;
    1228     }
    1229 
    1230     /* alloc tmp buffer */
    1231     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1232     if (!pvBuf)
    1233         return VERR_NO_MEMORY;
    1234 
     982        if (cbToWrite == getImageBlockSize(&pImage->Header))
     983        {
     984            /* Full block write to previously unallocated block.
     985             * Allocate block and write data. */
     986            unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
     987            rc = RTFileWriteAt(pImage->File,
     988                                 ((uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
     989                               + pImage->offStartData,
     990                               pvBuf, cbToWrite, NULL);
     991            if (VBOX_FAILURE(rc))
     992                goto out;
     993            pImage->paBlocks[uBlock] = cBlocksAllocated;
     994            setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
     995
     996            rc = vdiUpdateBlockInfo(pImage, uBlock);
     997            if (VBOX_FAILURE(rc))
     998                goto out;
     999
     1000            *pcbPreRead = 0;
     1001            *pcbPostRead = 0;
     1002        }
     1003        else
     1004        {
     1005            /* Trying to do a partial write to an unallocated block. Don't do
     1006             * anything except letting the upper layer know what to do. */
     1007            *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
     1008            *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
     1009            rc = VERR_VDI_BLOCK_FREE;
     1010        }
     1011    }
     1012    else
     1013        rc = RTFileWriteAt(pImage->File,
     1014                             ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
     1015                           + pImage->offStartData + pImage->offStartBlockData + offWrite,
     1016                           pvBuf, cbToWrite, NULL);
     1017
     1018out:
     1019    LogFlowFunc(("returns %Vrc\n", rc));
     1020    return rc;
     1021}
     1022
     1023/** @copydoc VBOXHDDBACKEND::pfnFlush */
     1024static int vdiFlush(void *pBackendData)
     1025{
     1026    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1027    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
    12351028    int rc = VINF_SUCCESS;
    12361029
    1237     unsigned cbCopy = getImageBlockSize(&pImage->Header);
    1238     unsigned offCopy = 0;
    1239 
    1240     /* do loop, because buffer size may be less then block size */
    1241     while (cbCopy > 0)
    1242     {
    1243         unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
    1244         rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
    1245         if (VBOX_FAILURE(rc))
    1246             break;
    1247 
    1248         rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
    1249         if (VBOX_FAILURE(rc))
    1250             break;
    1251 
    1252         cbCopy -= to_copy;
    1253         offCopy += to_copy;
    1254     }
    1255 
    1256     RTMemTmpFree(pvBuf);
    1257     return rc;
    1258 }
    1259 
    1260 /**
    1261  * Write data to virtual HDD.
    1262  *
    1263  * @returns VBox status code.
    1264  * @param   pDisk           Pointer to VDI HDD container.
    1265  * @param   offStart        Offset of first writing byte from start of HDD.
    1266  * @param   pvBuf           Pointer to buffer of writing data.
    1267  * @param   cbToWrite       Number of bytes to write.
    1268  */
    1269 VBOXDDU_DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, size_t cbToWrite)
    1270 {
    1271     /* sanity check */
    1272     Assert(pDisk);
    1273     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1274 
    1275     PVDIIMAGEDESC pImage = pDisk->pLast;
    1276     Assert(pImage);
    1277 
    1278     /* Check params. */
    1279     if (    offStart + cbToWrite > getImageDiskSize(&pImage->Header)
    1280         ||  cbToWrite == 0)
    1281     {
    1282         AssertMsgFailed(("offStart=%llu cbToWrite=%u\n", offStart, cbToWrite));
    1283         return VERR_INVALID_PARAMETER;
    1284     }
    1285 
    1286     /* Calculate starting block number and offset inside it. */
    1287     unsigned uBlock   = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1288     unsigned offWrite = (unsigned)offStart   & pImage->uBlockMask;
    1289     unsigned cbBlock  = getImageBlockSize(&pImage->Header);
    1290 
    1291     /* loop through blocks */
     1030    vdiFlushImage(pImage);
     1031    LogFlowFunc(("returns %Vrc\n", rc));
     1032    return rc;
     1033}
     1034
     1035/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
     1036static unsigned vdiGetVersion(void *pBackendData)
     1037{
     1038    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1039    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1040    unsigned uVersion;
     1041
     1042    Assert(pImage);
     1043
     1044    if (pImage)
     1045        uVersion = pImage->PreHeader.u32Version;
     1046    else
     1047        uVersion = 0;
     1048
     1049    LogFlowFunc(("returns %#x\n", uVersion));
     1050    return uVersion;
     1051}
     1052
     1053/** @copydoc VBOXHDDBACKEND::pfnGetImageType */
     1054static int vdiGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
     1055{
     1056    LogFlowFunc(("pBackendData=%#p penmImageType=%#p\n", pBackendData, penmImageType));
     1057    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1058    int rc = VINF_SUCCESS;
     1059
     1060    Assert(pImage);
     1061    Assert(penmImageType);
     1062
     1063    if (pImage)
     1064        *penmImageType =   getImageType(&pImage->Header) == VDI_IMAGE_TYPE_NORMAL
     1065                         ? VD_IMAGE_TYPE_NORMAL
     1066                         : VD_IMAGE_TYPE_DIFF;
     1067    else
     1068        rc = VERR_VDI_NOT_OPENED;
     1069
     1070    LogFlowFunc(("returns %Vrc enmImageType=%u\n", rc, *penmImageType));
     1071    return rc;
     1072}
     1073
     1074/** @copydoc VBOXHDDBACKEND::pfnGetSize */
     1075static uint64_t vdiGetSize(void *pBackendData)
     1076{
     1077    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1078    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1079    uint64_t cbSize;
     1080
     1081    Assert(pImage);
     1082
     1083    if (pImage)
     1084        cbSize = getImageDiskSize(&pImage->Header);
     1085    else
     1086        cbSize = 0;
     1087
     1088    LogFlowFunc(("returns %llu\n", cbSize));
     1089    return cbSize;
     1090}
     1091
     1092/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
     1093static uint64_t vdiGetFileSize(void *pBackendData)
     1094{
     1095    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1096    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1097    uint64_t cb = 0;
     1098
     1099    Assert(pImage);
     1100
     1101    if (pImage)
     1102    {
     1103        uint64_t cbFile;
     1104        if (pImage->File != NIL_RTFILE)
     1105        {
     1106            int rc = RTFileGetSize(pImage->File, &cbFile);
     1107            if (VBOX_SUCCESS(rc))
     1108                cb += cbFile;
     1109        }
     1110    }
     1111
     1112    LogFlowFunc(("returns %lld\n", cb));
     1113    return cb;
     1114}
     1115
     1116/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
     1117static int vdiGetPCHSGeometry(void *pBackendData,
     1118                              PPDMMEDIAGEOMETRY pPCHSGeometry)
     1119{
     1120    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
     1121    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
    12921122    int rc;
    1293     for (;;)
    1294     {
    1295         unsigned to_write;
    1296         if (offWrite + cbToWrite <= cbBlock)
    1297             to_write = cbToWrite;
     1123
     1124    Assert(pImage);
     1125
     1126    if (pImage)
     1127    {
     1128        if (pImage->PCHSGeometry.cCylinders)
     1129        {
     1130            *pPCHSGeometry = pImage->PCHSGeometry;
     1131            rc = VINF_SUCCESS;
     1132        }
    12981133        else
    1299             to_write = cbBlock - offWrite;
    1300 
    1301         /* All callers write less than a VDI block right now (assuming
    1302          * default VDI block size). So not worth optimizing for the case
    1303          * where a full block is overwritten (no copying required).
    1304          * Checking whether a block is all zeroes after the write is too
    1305          * expensive (would require reading the rest of the block). */
    1306 
    1307         if (pDisk->cImages > 1)
    1308         {
    1309             /* Differencing images are used, handle them. */
    1310 
    1311             /* Search for image with allocated block. */
    1312             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1313             {
    1314                 pImage = pImage->pPrev;
    1315                 if (!pImage)
    1316                 {
    1317                     /* Block is not allocated in all images of chain. */
    1318                     pImage = pDisk->pLast;
    1319                     break;
    1320                 }
    1321             }
    1322 
    1323             if (pImage != pDisk->pLast)
    1324             {
    1325                 /* One of parent image has a block data, copy it into last image. */
    1326                 rc = vdiCopyBlock(pDisk, pImage, uBlock);
    1327                 if (VBOX_FAILURE(rc))
    1328                     break;
    1329                 pImage = pDisk->pLast;
    1330             }
    1331         }
    1332 
    1333         /* Actually write the data into block. */
    1334         rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
    1335 
    1336         cbToWrite -= to_write;
    1337         if (    cbToWrite == 0
    1338             || VBOX_FAILURE(rc))
    1339             break;
    1340 
    1341         /* goto next block */
    1342         uBlock++;
    1343         offWrite = 0;
    1344         pvBuf = (char *)pvBuf + to_write;
    1345     }
    1346 
    1347     return rc;
    1348 }
    1349 
    1350 /**
    1351  * internal: commit one image to another, no changes to header, just
    1352  * plain copy operation. Blocks that are not allocated in the source
    1353  * image (i.e. inherited by its parent(s)) are not merged.
    1354  *
    1355  * @param pImageFrom        source image
    1356  * @param pImageTo          target image (will receive all the modifications)
    1357  * @param fParentToChild    true if the source image is parent of the target one,
    1358  *                          false of the target image is the parent of the source.
    1359  * @param pfnProgress       progress callback (NULL if not to be used)
    1360  * @param pvUser            user argument for the progress callback
    1361  *
    1362  * @note the target image has to be opened read/write
    1363  * @note this method does not check whether merging is possible!
    1364  */
    1365 static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    1366                           PFNVMPROGRESS pfnProgress, void *pvUser)
    1367 {
    1368     Assert(pImageFrom);
    1369     Assert(pImageTo);
    1370 
    1371     Log(("vdiMergeImages: merging from image \"%s\" to image \"%s\" (fParentToChild=%d)\n",
    1372          pImageFrom->szFilename, pImageTo->szFilename, fParentToChild));
    1373 
    1374     /* alloc tmp buffer */
    1375     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    1376     if (!pvBuf)
    1377         return VERR_NO_MEMORY;
    1378 
     1134            rc = VERR_VDI_GEOMETRY_NOT_SET;
     1135    }
     1136    else
     1137        rc = VERR_VDI_NOT_OPENED;
     1138
     1139    LogFlowFunc(("returns %Vrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
     1140    return rc;
     1141}
     1142
     1143/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
     1144static int vdiSetPCHSGeometry(void *pBackendData,
     1145                              PCPDMMEDIAGEOMETRY pPCHSGeometry)
     1146{
     1147    LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
     1148    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1149    int rc;
     1150
     1151    Assert(pImage);
     1152
     1153    if (pImage)
     1154    {
     1155        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
     1156        {
     1157            rc = VERR_VDI_IMAGE_READ_ONLY;
     1158            goto out;
     1159        }
     1160
     1161        pImage->PCHSGeometry = *pPCHSGeometry;
     1162        rc = VINF_SUCCESS;
     1163    }
     1164    else
     1165        rc = VERR_VDI_NOT_OPENED;
     1166
     1167out:
     1168    LogFlowFunc(("returns %Vrc\n", rc));
     1169    return rc;
     1170}
     1171
     1172/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
     1173static int vdiGetLCHSGeometry(void *pBackendData,
     1174                              PPDMMEDIAGEOMETRY pLCHSGeometry)
     1175{
     1176    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
     1177    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1178    int rc;
     1179
     1180    Assert(pImage);
     1181
     1182    if (pImage)
     1183    {
     1184        VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
     1185        PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
     1186        if (!pGeometry)
     1187            pGeometry = &DummyGeo;
     1188
     1189        if (    pGeometry->cCylinders > 0
     1190            &&  pGeometry->cHeads > 0
     1191            &&  pGeometry->cSectors > 0)
     1192        {
     1193            pLCHSGeometry->cCylinders = pGeometry->cCylinders;
     1194            pLCHSGeometry->cHeads = pGeometry->cHeads;
     1195            pLCHSGeometry->cSectors = pGeometry->cSectors;
     1196            rc = VINF_SUCCESS;
     1197        }
     1198        else
     1199            rc = VERR_VDI_GEOMETRY_NOT_SET;
     1200    }
     1201    else
     1202        rc = VERR_VDI_NOT_OPENED;
     1203
     1204    LogFlowFunc(("returns %Vrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
     1205    return rc;
     1206}
     1207
     1208/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
     1209static int vdiSetLCHSGeometry(void *pBackendData,
     1210                              PCPDMMEDIAGEOMETRY pLCHSGeometry)
     1211{
     1212    LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
     1213    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1214    PVDIDISKGEOMETRY pGeometry;
     1215    int rc;
     1216
     1217    Assert(pImage);
     1218
     1219    if (pImage)
     1220    {
     1221        if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
     1222        {
     1223            rc = VERR_VDI_IMAGE_READ_ONLY;
     1224            goto out;
     1225        }
     1226
     1227        pGeometry = getImageLCHSGeometry(&pImage->Header);
     1228        if (pGeometry)
     1229        {
     1230            pGeometry->cCylinders = pLCHSGeometry->cCylinders;
     1231            pGeometry->cHeads = pLCHSGeometry->cHeads;
     1232            pGeometry->cSectors = pLCHSGeometry->cSectors;
     1233            pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
     1234
     1235            /* Update header information in base image file. */
     1236            vdiFlushImage(pImage);
     1237        }
     1238        rc = VINF_SUCCESS;
     1239    }
     1240    else
     1241        rc = VERR_VDI_NOT_OPENED;
     1242
     1243out:
     1244    LogFlowFunc(("returns %Vrc\n", rc));
     1245    return rc;
     1246}
     1247
     1248/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
     1249static unsigned vdiGetImageFlags(void *pBackendData)
     1250{
     1251    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1252    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1253    unsigned uImageFlags;
     1254
     1255    Assert(pImage);
     1256
     1257    if (pImage)
     1258        uImageFlags = getImageFlags(&pImage->Header);
     1259    else
     1260        uImageFlags = 0;
     1261
     1262    LogFlowFunc(("returns %#x\n", uImageFlags));
     1263    return uImageFlags;
     1264}
     1265
     1266/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
     1267static unsigned vdiGetOpenFlags(void *pBackendData)
     1268{
     1269    LogFlowFunc(("pBackendData=%#p\n", pBackendData));
     1270    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1271    unsigned uOpenFlags;
     1272
     1273    Assert(pImage);
     1274
     1275    if (pImage)
     1276        uOpenFlags = pImage->uOpenFlags;
     1277    else
     1278        uOpenFlags = 0;
     1279
     1280    LogFlowFunc(("returns %#x\n", uOpenFlags));
     1281    return uOpenFlags;
     1282}
     1283
     1284/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
     1285static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
     1286{
     1287    LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
     1288    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1289    int rc;
     1290    const char *pszFilename;
     1291
     1292    /* Image must be opened and the new flags must be valid. Just readonly flag
     1293     * is supported. */
     1294    if (!pImage || uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
     1295    {
     1296        rc = VERR_INVALID_PARAMETER;
     1297        goto out;
     1298    }
     1299
     1300    /* Implement this operation via reopening the image. */
     1301    pszFilename = pImage->pszFilename;
     1302    vdiFreeImage(pImage, false);
     1303    rc = vdiOpenImage(pImage, uOpenFlags);
     1304
     1305out:
     1306    LogFlowFunc(("returns %Vrc\n", rc));
     1307    return rc;
     1308}
     1309
     1310/** @copydoc VBOXHDDBACKEND::pfnGetComment */
     1311static int vdiGetComment(void *pBackendData, char *pszComment,
     1312                         size_t cbComment)
     1313{
     1314    LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
     1315    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
    13791316    int rc = VINF_SUCCESS;
    13801317
    1381     if (!fParentToChild)
    1382     {
    1383         /*
    1384          *  Commit the child image to the parent image.
    1385          *  Child is the source (from), parent is the target (to).
    1386          */
    1387 
    1388         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1389 
    1390         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1391         {
    1392             /* only process blocks that are allocated in the source image */
    1393             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE)
    1394             {
    1395                 /* Found used block in source image, commit it. */
    1396                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    1397                     &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pImageTo->paBlocks[uBlock]))
    1398                 {
    1399                     /* Block is zero in the source image and not allocated in the target image. */
    1400                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1401                     vdiSetModifiedFlag(pImageTo);
    1402                 }
    1403                 else
    1404                 {
    1405                     /* Block is not zero / allocated in source image. */
    1406                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1407                     unsigned offCommit = 0;
    1408 
    1409                     /* do loop, because buffer size may be less then block size */
    1410                     while (cbCommit > 0)
    1411                     {
    1412                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1413 
    1414                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1415                         if (VBOX_FAILURE(rc))
    1416                             break;
    1417 
    1418                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1419                         if (VBOX_FAILURE(rc))
    1420                             break;
    1421 
    1422                         cbCommit -= cbToCopy;
    1423                         offCommit += cbToCopy;
    1424                     }
    1425                     if (VBOX_FAILURE(rc))
    1426                         break;
    1427                 }
    1428             }
    1429 
    1430             if (pfnProgress)
    1431             {
    1432                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1433                             (uBlock * 100) / cBlocks,
    1434                             pvUser);
    1435                 /* Note: commiting is non breakable operation, skipping rc here. */
    1436             }
    1437         }
    1438     }
    1439     else
    1440     {
    1441         /*
    1442          *  Commit the parent image to the child image.
    1443          *  Parent is the source (from), child is the target (to).
    1444          */
    1445 
    1446         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1447 
    1448         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1449         {
    1450             /*
    1451              *  only process blocks that are allocated or zero in the source image
    1452              *  and NEITHER allocated NOR zero in the target image
    1453              */
    1454             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE &&
    1455                 pImageTo->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1456             {
    1457                 /* Found used block in source image (but unused in target), commit it. */
    1458                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1459                 {
    1460                     /* Block is zero in the source image and not allocated in the target image. */
    1461                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1462                     vdiSetModifiedFlag(pImageTo);
    1463                 }
    1464                 else
    1465                 {
    1466                     /* Block is not zero / allocated in source image. */
    1467                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1468                     unsigned offCommit = 0;
    1469 
    1470                     /* do loop, because buffer size may be less then block size */
    1471                     while (cbCommit > 0)
    1472                     {
    1473                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1474 
    1475                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1476                         if (VBOX_FAILURE(rc))
    1477                             break;
    1478 
    1479                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1480                         if (VBOX_FAILURE(rc))
    1481                             break;
    1482 
    1483                         cbCommit -= cbToCopy;
    1484                         offCommit += cbToCopy;
    1485                     }
    1486                     if (VBOX_FAILURE(rc))
    1487                         break;
    1488                 }
    1489             }
    1490 
    1491             if (pfnProgress)
    1492             {
    1493                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1494                             (uBlock * 100) / cBlocks,
    1495                             pvUser);
    1496                 /* Note: commiting is non breakable operation, skipping rc here. */
    1497             }
    1498         }
    1499     }
    1500 
    1501     RTMemTmpFree(pvBuf);
    1502     return rc;
    1503 }
    1504 
    1505 /**
    1506  * internal: commit last image(s) to selected previous image.
    1507  * note: all images accessed across this call must be opened in R/W mode.
    1508  * @remark    Only used by tstVDI.
    1509  */
    1510 static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    1511                             PFNVMPROGRESS pfnProgress, void *pvUser)
    1512 {
    1513     /* sanity check */
    1514     Assert(pDisk);
    1515     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1516     Assert(pDstImage);
    1517 
    1518     PVDIIMAGEDESC pImage = pDisk->pLast;
    1519     Assert(pImage);
    1520     Log(("vdiCommitToImage: commiting from image \"%s\" to image \"%s\"\n",
    1521          pImage->szFilename, pDstImage->szFilename));
    1522     if (pDstImage == pImage)
    1523     {
    1524         Log(("vdiCommitToImage: attempt to commit to the same image!\n"));
    1525         return VERR_VDI_NO_DIFF_IMAGES;
    1526     }
    1527 
    1528     /* Scan images for pDstImage. */
    1529     while (pImage && pImage != pDstImage)
    1530         pImage = pImage->pPrev;
    1531     if (!pImage)
    1532     {
    1533         AssertMsgFailed(("Invalid arguments: pDstImage is not in images chain\n"));
    1534         return VERR_INVALID_PARAMETER;
    1535     }
    1536     pImage = pDisk->pLast;
    1537 
    1538     /* alloc tmp buffer */
    1539     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1540     if (!pvBuf)
    1541         return VERR_NO_MEMORY;
    1542 
    1543     int rc = VINF_SUCCESS;
    1544     unsigned cBlocks = getImageBlocks(&pImage->Header);
    1545 
    1546     for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1547     {
    1548         pImage = pDisk->pLast;
    1549 
    1550         /* Find allocated block to commit. */
    1551         while (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE
    1552                &&  pImage != pDstImage)
    1553             pImage = pImage->pPrev;
    1554 
    1555         if (pImage != pDstImage)
    1556         {
    1557             /* Found used block in diff image (pImage), commit it. */
    1558             if (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    1559                 &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pDstImage->paBlocks[uBlock]))
    1560             {
    1561                 /* Block is zero in difference image and not allocated in primary image. */
    1562                 pDstImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1563                 vdiSetModifiedFlag(pDstImage);
    1564             }
    1565             else
    1566             {
    1567                 /* Block is not zero / allocated in primary image. */
    1568                 unsigned cbCommit = getImageBlockSize(&pImage->Header);
    1569                 unsigned offCommit = 0;
    1570 
    1571                 /* do loop, because buffer size may be less then block size */
    1572                 while (cbCommit > 0)
    1573                 {
    1574                     unsigned cbToCopy = RT_MIN(cbCommit, pDisk->cbBuf);
    1575 
    1576                     rc = vdiReadInBlock(pImage, uBlock, offCommit, cbToCopy, pvBuf);
    1577                     if (VBOX_FAILURE(rc))
    1578                         break;
    1579 
    1580                     rc = vdiWriteInBlock(pDisk, pDstImage, uBlock, offCommit, cbToCopy, pvBuf);
    1581                     if (VBOX_FAILURE(rc))
    1582                         break;
    1583 
    1584                     cbCommit -= cbToCopy;
    1585                     offCommit += cbToCopy;
    1586                 }
    1587                 if (VBOX_FAILURE(rc))
    1588                     break;
    1589             }
    1590             pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_FREE;
    1591         }
    1592 
    1593         if (pfnProgress)
    1594         {
    1595             pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1596                         (uBlock * 100) / cBlocks,
    1597                         pvUser);
    1598             /* Note: commiting is non breakable operation, skipping rc here. */
    1599         }
    1600     }
    1601 
    1602     RTMemTmpFree(pvBuf);
    1603 
    1604     /* Go forward and update linkage information. */
    1605     for (pImage = pDstImage; pImage; pImage = pImage->pNext)
    1606     {
    1607         /* generate new last-modified uuid. */
    1608         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    1609 
    1610         /* fix up linkage. */
    1611         if (pImage != pDstImage)
    1612             *getImageParentModificationUUID(&pImage->Header) = *getImageModificationUUID(&pImage->pPrev->Header);
    1613 
    1614         /* reset modified flag. */
    1615         pImage->fModified = 0;
    1616     }
    1617 
    1618     /* Process committed images - truncate them. */
    1619     for (pImage = pDisk->pLast; pImage != pDstImage; pImage = pImage->pPrev)
    1620     {
    1621         /* note: can't understand how to do error works here? */
    1622 
    1623         setImageBlocksAllocated(&pImage->Header, 0);
    1624 
    1625         /* Truncate file. */
    1626         int rc2 = RTFileSetSize(pImage->File, pImage->offStartData);
    1627         if (VBOX_FAILURE(rc2))
    1628         {
    1629             rc = rc2;
    1630             Log(("vdiCommitToImage: set size (truncate) rc=%Vrc filename=\"%s\"\n",
    1631                  rc, pImage->szFilename));
    1632         }
    1633 
    1634         /* Save header and blocks array. */
    1635         rc2 = vdiUpdateBlocks(pImage);
    1636         if (VBOX_FAILURE(rc2))
    1637         {
    1638             rc = rc2;
    1639             Log(("vdiCommitToImage: update blocks and header rc=%Vrc filename=\"%s\"\n",
    1640                  rc, pImage->szFilename));
    1641         }
    1642     }
    1643 
    1644     if (pfnProgress)
    1645     {
    1646         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    1647        /* Note: commiting is non breakable operation, skipping rc here. */
    1648     }
    1649 
    1650     Log(("vdiCommitToImage: done, rc=%Vrc\n", rc));
    1651 
    1652     return rc;
    1653 }
    1654 
    1655 /**
    1656  * Checks if image is available and not broken, returns some useful image parameters if requested.
    1657  *
    1658  * @returns VBox status code.
    1659  * @param   pszFilename     Name of the image file to check.
    1660  * @param   puVersion       Where to store the version of image. NULL is ok.
    1661  * @param   penmType        Where to store the type of image. NULL is ok.
    1662  * @param   pcbSize         Where to store the size of image in bytes. NULL is ok.
    1663  * @param   pUuid           Where to store the uuid of image creation. NULL is ok.
    1664  * @param   pParentUuid     Where to store the UUID of the parent image. NULL is ok.
    1665  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    1666  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    1667  */
    1668 VBOXDDU_DECL(int) VDICheckImage(const char *pszFilename, unsigned *puVersion, PVDIIMAGETYPE penmType,
    1669                                 uint64_t *pcbSize, PRTUUID pUuid, PRTUUID pParentUuid,
    1670                                 char *pszComment, unsigned cbComment)
    1671 {
    1672     LogFlow(("VDICheckImage:\n"));
    1673 
    1674     /* Check arguments. */
    1675     if (    !pszFilename
    1676         ||  *pszFilename == '\0')
    1677     {
    1678         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    1679         return VERR_INVALID_PARAMETER;
    1680     }
    1681 
    1682     PVDIIMAGEDESC pImage;
    1683     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    1684     if (VBOX_SUCCESS(rc))
    1685     {
    1686         Log(("VDICheckImage: filename=\"%s\" version=%08X type=%X cbDisk=%llu uuid={%Vuuid}\n",
    1687              pszFilename,
    1688              pImage->PreHeader.u32Version,
    1689              getImageType(&pImage->Header),
    1690              getImageDiskSize(&pImage->Header),
    1691              getImageCreationUUID(&pImage->Header)));
    1692 
    1693         if (    pszComment
    1694             &&  cbComment > 0)
    1695         {
    1696             char *pszTmp = getImageComment(&pImage->Header);
    1697             unsigned cb = strlen(pszTmp);
    1698             if (cbComment > cb)
    1699                 memcpy(pszComment, pszTmp, cb + 1);
    1700             else
    1701                 rc = VERR_BUFFER_OVERFLOW;
    1702         }
    1703         if (VBOX_SUCCESS(rc))
    1704         {
    1705             if (puVersion)
    1706                 *puVersion = pImage->PreHeader.u32Version;
    1707             if (penmType)
    1708                 *penmType = getImageType(&pImage->Header);
    1709             if (pcbSize)
    1710                 *pcbSize = getImageDiskSize(&pImage->Header);
    1711             if (pUuid)
    1712                 *pUuid = *getImageCreationUUID(&pImage->Header);
    1713             if (pParentUuid)
    1714                 *pParentUuid = *getImageParentUUID(&pImage->Header);
    1715         }
    1716         vdiCloseImage(pImage);
    1717     }
    1718 
    1719     LogFlow(("VDICheckImage: returns %Vrc\n", rc));
    1720     return rc;
    1721 }
    1722 
    1723 /**
    1724  * Changes an image's comment string.
    1725  *
    1726  * @returns VBox status code.
    1727  * @param   pszFilename     Name of the image file to operate on.
    1728  * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
    1729  */
    1730 VBOXDDU_DECL(int) VDISetImageComment(const char *pszFilename, const char *pszComment)
    1731 {
    1732     LogFlow(("VDISetImageComment:\n"));
    1733 
    1734     /*
    1735      * Validate arguments.
    1736      */
    1737     if (    !pszFilename
    1738         ||  *pszFilename == '\0')
    1739     {
    1740         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    1741         return VERR_INVALID_PARAMETER;
    1742     }
    1743 
    1744     const size_t cchComment = pszComment ? strlen(pszComment) : 0;
    1745     if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
    1746     {
    1747         Log(("VDISetImageComment: pszComment is too long, %d bytes!\n", cchComment));
    1748         return VERR_VDI_COMMENT_TOO_LONG;
    1749     }
    1750 
    1751     /*
    1752      * Open the image for updating.
    1753      */
    1754     PVDIIMAGEDESC pImage;
    1755     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    1756     if (VBOX_FAILURE(rc))
    1757     {
    1758         Log(("VDISetImageComment: vdiOpenImage rc=%Vrc filename=\"%s\"!\n", rc, pszFilename));
    1759         return rc;
    1760     }
    1761     if (!pImage->fReadOnly)
    1762     {
     1318    Assert(pImage);
     1319
     1320    if (pImage)
     1321    {
     1322        char *pszTmp = getImageComment(&pImage->Header);
     1323        unsigned cb = strlen(pszTmp);
     1324        if (cb < cbComment)
     1325        {
     1326            /* memcpy is much better than strncpy. */
     1327            memcpy(pszComment, pszTmp, cb + 1);
     1328        }
     1329        else
     1330            rc = VERR_BUFFER_OVERFLOW;
     1331    }
     1332    else
     1333        rc = VERR_VDI_NOT_OPENED;
     1334
     1335    LogFlowFunc(("returns %Vrc comment=\"%s\"\n", rc, pszComment));
     1336    return rc;
     1337}
     1338
     1339/** @copydoc VBOXHDDBACKEND::pfnGetComment */
     1340static int vdiSetComment(void *pBackendData, const char *pszComment)
     1341{
     1342    LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
     1343    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1344    int rc;
     1345
     1346    Assert(pImage);
     1347
     1348    if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
     1349    {
     1350        rc = VERR_VDI_IMAGE_READ_ONLY;
     1351        goto out;
     1352    }
     1353
     1354    if (pImage)
     1355    {
     1356        size_t cchComment = pszComment ? strlen(pszComment) : 0;
     1357        if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
     1358        {
     1359            LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
     1360            rc = VERR_VDI_COMMENT_TOO_LONG;
     1361            goto out;
     1362        }
     1363
    17631364        /* we don't support old style images */
    17641365        if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
     
    17721373            /* write out new the header */
    17731374            rc = vdiUpdateHeader(pImage);
    1774             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    1775                              pImage->szFilename, rc));
    17761375        }
    17771376        else
    1778         {
    1779             Log(("VDISetImageComment: Unsupported version!\n"));
    17801377            rc = VERR_VDI_UNSUPPORTED_VERSION;
    1781         }
    1782     }
    1783     else
    1784     {
    1785         Log(("VDISetImageComment: image \"%s\" is opened as read-only!\n", pszFilename));
    1786         rc = VERR_VDI_IMAGE_READ_ONLY;
    1787     }
    1788 
    1789     vdiCloseImage(pImage);
    1790     return rc;
    1791 }
    1792 
    1793 /**
    1794  * Creates a new base image file.
    1795  *
    1796  * @returns VBox status code.
    1797  * @param   pszFilename     Name of the creating image file.
    1798  * @param   enmType         Image type, only base image types are acceptable.
    1799  * @param   cbSize          Image size in bytes.
    1800  * @param   pszComment      Pointer to image comment. NULL is ok.
    1801  * @param   pfnProgress     Progress callback. Optional.
    1802  * @param   pvUser          User argument for the progress callback.
    1803  */
    1804 VBOXDDU_DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize,
    1805                                      const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    1806 {
    1807     LogFlow(("VDICreateBaseImage:\n"));
    1808 
    1809     /* Check arguments. */
    1810     if (    !pszFilename
    1811         ||  *pszFilename == '\0'
    1812         ||  (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
    1813         ||  cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
    1814     {
    1815         AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
    1816                          pszFilename, enmType, cbSize));
    1817         return VERR_INVALID_PARAMETER;
    1818     }
    1819 
    1820     int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
    1821                             pfnProgress, pvUser);
    1822     LogFlow(("VDICreateBaseImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    1823     return rc;
    1824 }
    1825 
    1826 /**
    1827  * Creates a differencing dynamically growing image file for specified parent image.
    1828  *
    1829  * @returns VBox status code.
    1830  * @param   pszFilename     Name of the creating differencing image file.
    1831  * @param   pszParent       Name of the parent image file. May be base or diff image type.
    1832  * @param   pszComment      Pointer to image comment. NULL is ok.
    1833  * @param   pfnProgress     Progress callback. Optional.
    1834  * @param   pvUser          User argument for the progress callback.
    1835  */
    1836 VBOXDDU_DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent,
    1837                                            const char *pszComment, PFNVMPROGRESS pfnProgress,
    1838                                            void *pvUser)
    1839 {
    1840     LogFlow(("VDICreateDifferenceImage:\n"));
    1841 
    1842     /* Check arguments. */
    1843     if (    !pszFilename
    1844         ||  *pszFilename == '\0'
    1845         ||  !pszParent
    1846         ||  *pszParent == '\0')
    1847     {
    1848         AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
    1849                          pszFilename, pszParent));
    1850         return VERR_INVALID_PARAMETER;
    1851     }
    1852 
    1853     PVDIIMAGEDESC pParent;
    1854     int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
    1855     if (VBOX_SUCCESS(rc))
    1856     {
    1857         rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
    1858                             getImageDiskSize(&pParent->Header), pszComment, pParent,
    1859                             pfnProgress, pvUser);
    1860         vdiCloseImage(pParent);
    1861     }
    1862     LogFlow(("VDICreateDifferenceImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    1863     return rc;
    1864 }
    1865 
    1866 /**
    1867  * Deletes an image. Only valid image files can be deleted by this call.
    1868  *
    1869  * @returns VBox status code.
    1870  * @param   pszFilename     Name of the image file to check.
    1871  */
    1872 VBOXDDU_DECL(int) VDIDeleteImage(const char *pszFilename)
    1873 {
    1874     LogFlow(("VDIDeleteImage:\n"));
    1875     /* Check arguments. */
    1876     if (    !pszFilename
    1877         ||  *pszFilename == '\0')
    1878     {
    1879         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    1880         return VERR_INVALID_PARAMETER;
    1881     }
    1882 
    1883     int rc = VDICheckImage(pszFilename, NULL, NULL, NULL, NULL, NULL, NULL, 0);
    1884     if (VBOX_SUCCESS(rc))
    1885         rc = RTFileDelete(pszFilename);
    1886 
    1887     LogFlow(("VDIDeleteImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    1888     return rc;
    1889 }
    1890 
    1891 /**
    1892  * Makes a copy of image file with a new (other) creation uuid.
    1893  *
    1894  * @returns VBox status code.
    1895  * @param   pszDstFilename  Name of the image file to create.
    1896  * @param   pszSrcFilename  Name of the image file to copy from.
    1897  * @param   pszComment      Pointer to image comment. If NULL specified comment
    1898  *                          will be copied from source image.
    1899  * @param   pfnProgress     Progress callback. Optional.
    1900  * @param   pvUser          User argument for the progress callback.
    1901  */
    1902 VBOXDDU_DECL(int) VDICopyImage(const char *pszDstFilename, const char *pszSrcFilename,
    1903                                const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    1904 {
    1905     LogFlow(("VDICopyImage:\n"));
    1906 
    1907     /* Check arguments. */
    1908     if (    !pszDstFilename
    1909         ||  *pszDstFilename == '\0'
    1910         ||  !pszSrcFilename
    1911         ||  *pszSrcFilename == '\0')
    1912     {
    1913         AssertMsgFailed(("Invalid arguments: pszDstFilename=%p pszSrcFilename=%p\n",
    1914                          pszDstFilename, pszSrcFilename));
    1915         return VERR_INVALID_PARAMETER;
    1916     }
    1917 
    1918     /* Special check for comment length. */
    1919     if (    pszComment
    1920         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    1921     {
    1922         Log(("VDICopyImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    1923         return VERR_VDI_COMMENT_TOO_LONG;
    1924     }
    1925 
    1926     PVDIIMAGEDESC pImage;
    1927     int rc = vdiOpenImage(&pImage, pszSrcFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    1928     if (VBOX_FAILURE(rc))
    1929     {
    1930         Log(("VDICopyImage: src image \"%s\" open failed rc=%Vrc\n", pszSrcFilename, rc));
    1931         return rc;
    1932     }
    1933 
    1934     uint64_t cbFile = pImage->offStartData
    1935                     + ((uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset);
    1936 
    1937     /* create file */
    1938     RTFILE File;
    1939     rc = RTFileOpen(&File,
    1940                     pszDstFilename,
    1941                     RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    1942     if (VBOX_SUCCESS(rc))
    1943     {
    1944         /* lock new image exclusively to close any wrong access by VDI API calls. */
    1945         rc = RTFileLock(File, RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbFile);
    1946         if (VBOX_SUCCESS(rc))
    1947         {
    1948             /* Set the size of a new file. */
    1949             rc = RTFileSetSize(File, cbFile);
    1950             if (VBOX_SUCCESS(rc))
    1951             {
    1952                 /* A dirty trick - use original image data to fill the new image. */
    1953                 RTFILE oldFileHandle = pImage->File;
    1954                 pImage->File = File;
    1955                 pImage->fReadOnly = false;
    1956 
    1957                 /* generate a new image creation uuid. */
    1958                 RTUuidCreate(getImageCreationUUID(&pImage->Header));
    1959                 /* generate a new image last-modified uuid. */
    1960                 RTUuidCreate(getImageModificationUUID(&pImage->Header));
    1961                 /* set image comment, if present. */
    1962                 if (pszComment)
    1963                     strncpy(getImageComment(&pImage->Header), pszComment, VDI_IMAGE_COMMENT_SIZE);
    1964 
    1965                 /* Write the pre-header to new image. */
    1966                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    1967                 if (VBOX_SUCCESS(rc))
    1968                     rc = RTFileWrite(pImage->File,
    1969                                      &pImage->PreHeader,
    1970                                      sizeof(pImage->PreHeader),
    1971                                      NULL);
    1972 
    1973                 /* Write the header and the blocks array to new image. */
    1974                 if (VBOX_SUCCESS(rc))
    1975                     rc = vdiUpdateBlocks(pImage);
    1976 
    1977                 pImage->File = oldFileHandle;
    1978                 pImage->fReadOnly = true;
    1979 
    1980                 /* Seek to the data start in both images. */
    1981                 if (VBOX_SUCCESS(rc))
    1982                     rc = RTFileSeek(pImage->File,
    1983                                     pImage->offStartData,
    1984                                     RTFILE_SEEK_BEGIN,
    1985                                     NULL);
    1986                 if (VBOX_SUCCESS(rc))
    1987                     rc = RTFileSeek(File,
    1988                                     pImage->offStartData,
    1989                                     RTFILE_SEEK_BEGIN,
    1990                                     NULL);
    1991 
    1992                 if (VBOX_SUCCESS(rc))
    1993                 {
    1994                     /* alloc tmp buffer */
    1995                     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    1996                     if (pvBuf)
    1997                     {
    1998                         /* Main copy loop. */
    1999                         uint64_t cbData = cbFile - pImage->offStartData;
    2000                         unsigned cBlocks = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2001                         unsigned c = 0;
    2002 
    2003                         while (cbData)
    2004                         {
    2005                             unsigned cbToCopy = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2006 
    2007                             /* Read. */
    2008                             rc = RTFileRead(pImage->File, pvBuf, cbToCopy, NULL);
    2009                             if (VBOX_FAILURE(rc))
    2010                                 break;
    2011 
    2012                             /* Write. */
    2013                             rc = RTFileWrite(File, pvBuf, cbToCopy, NULL);
    2014                             if (VBOX_FAILURE(rc))
    2015                                 break;
    2016 
    2017                             if (pfnProgress)
    2018                             {
    2019                                 c++;
    2020                                 rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2021                                                  (c * 100) / cBlocks,
    2022                                                  pvUser);
    2023                                 if (VBOX_FAILURE(rc))
    2024                                     break;
    2025                             }
    2026                             cbData -= cbToCopy;
    2027                         }
    2028 
    2029                         RTMemTmpFree(pvBuf);
    2030                     }
    2031                     else
    2032                         rc = VERR_NO_MEMORY;
    2033                 }
    2034             }
    2035 
    2036             RTFileUnlock(File, 0, cbFile);
    2037         }
    2038 
    2039         RTFileClose(File);
    2040 
    2041         if (VBOX_FAILURE(rc))
    2042             RTFileDelete(pszDstFilename);
    2043 
    2044         if (pfnProgress)
    2045             pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2046     }
    2047 
    2048     vdiCloseImage(pImage);
    2049 
    2050     LogFlow(("VDICopyImage: returns %Vrc for pszSrcFilename=\"%s\" pszDstFilename=\"%s\"\n",
    2051              rc, pszSrcFilename, pszDstFilename));
    2052     return rc;
    2053 }
    2054 
    2055 /**
    2056  * Shrinks growing image file by removing zeroed data blocks.
    2057  *
    2058  * @returns VBox status code.
    2059  * @param   pszFilename     Name of the image file to shrink.
    2060  * @param   pfnProgress     Progress callback. Optional.
    2061  * @param   pvUser          User argument for the progress callback.
    2062  */
    2063 VBOXDDU_DECL(int) VDIShrinkImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2064 {
    2065     LogFlow(("VDIShrinkImage:\n"));
    2066 
    2067     /* Check arguments. */
    2068     if (    !pszFilename
    2069         ||  *pszFilename == '\0')
    2070     {
    2071         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2072         return VERR_INVALID_PARAMETER;
    2073     }
    2074 
    2075     PVDIIMAGEDESC pImage;
    2076     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2077     if (VBOX_FAILURE(rc))
    2078     {
    2079         Log(("VDIShrinkImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2080         return rc;
    2081     }
    2082     if (pImage->fReadOnly)
    2083     {
    2084         Log(("VDIShrinkImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2085         vdiCloseImage(pImage);
    2086         return VERR_VDI_IMAGE_READ_ONLY;
    2087     }
    2088 
    2089     /* Do debug dump. */
    2090     vdiDumpImage(pImage);
    2091 
    2092     /* Working data. */
    2093     unsigned cbBlock          = getImageBlockSize(&pImage->Header);
    2094     unsigned cBlocks          = getImageBlocks(&pImage->Header);
    2095     unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    2096 
    2097     uint64_t cbFile;
    2098     rc = RTFileGetSize(pImage->File, &cbFile);
    2099     if (VBOX_FAILURE(rc))
    2100     {
    2101         Log(("VDIShrinkImage: RTFileGetSize rc=%Vrc for file=\"%s\"\n", rc, pszFilename));
    2102         vdiCloseImage(pImage);
    2103         return rc;
    2104     }
    2105 
    2106     uint64_t cbData = cbFile - pImage->offStartData;
    2107     unsigned cBlocksAllocated2 = (unsigned)(cbData >> pImage->uShiftIndex2Offset);
    2108     if (cbData != (uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
    2109         Log(("VDIShrinkImage: invalid image file length, cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2110              cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2111 
    2112     /* Allocate second blocks array for back resolving. */
    2113     PVDIIMAGEBLOCKPOINTER paBlocks2 =
    2114                 (PVDIIMAGEBLOCKPOINTER)RTMemTmpAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks);
    2115     if (!paBlocks2)
    2116     {
    2117         Log(("VDIShrinkImage: failed to allocate paBlocks2 buffer (%u bytes)\n", sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks));
    2118         vdiCloseImage(pImage);
    2119         return VERR_NO_MEMORY;
    2120     }
    2121 
    2122     /* Init second blocks array. */
    2123     for (unsigned n = 0; n < cBlocks; n++)
    2124         paBlocks2[n] = VDI_IMAGE_BLOCK_FREE;
    2125 
    2126     /* Fill second blocks array, check for allocational errors. */
    2127     for (unsigned n = 0; n < cBlocks; n++)
    2128     {
    2129         if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[n]))
    2130         {
    2131             unsigned uBlock = pImage->paBlocks[n];
    2132             if (uBlock < cBlocksAllocated2)
    2133             {
    2134                 if (paBlocks2[uBlock] == VDI_IMAGE_BLOCK_FREE)
    2135                     paBlocks2[uBlock] = n;
    2136                 else
    2137                 {
    2138                     Log(("VDIShrinkImage: block n=%u -> uBlock=%u is already in use!\n", n, uBlock));
    2139                     /* free second link to block. */
    2140                     pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
    2141                 }
    2142             }
     1378    }
     1379    else
     1380        rc = VERR_VDI_NOT_OPENED;
     1381
     1382out:
     1383    LogFlowFunc(("returns %Vrc\n", rc));
     1384    return rc;
     1385}
     1386
     1387/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
     1388static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
     1389{
     1390    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
     1391    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1392    int rc;
     1393
     1394    Assert(pImage);
     1395
     1396    if (pImage)
     1397    {
     1398        *pUuid = *getImageCreationUUID(&pImage->Header);
     1399        rc = VINF_SUCCESS;
     1400    }
     1401    else
     1402        rc = VERR_VDI_NOT_OPENED;
     1403
     1404    LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
     1405    return rc;
     1406}
     1407
     1408/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
     1409static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
     1410{
     1411    LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
     1412    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1413    int rc = VINF_SUCCESS;
     1414
     1415    Assert(pImage);
     1416
     1417    if (pImage)
     1418    {
     1419        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
     1420        {
     1421            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
     1422                pImage->Header.u.v1.uuidCreate = *pUuid;
     1423            /* Make it possible to clone old VDIs. */
     1424            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
     1425                pImage->Header.u.v0.uuidCreate = *pUuid;
    21431426            else
    21441427            {
    2145                 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is out of blocks range! (cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu)\n",
    2146                      n, uBlock, cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2147                 /* free link to invalid block. */
    2148                 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
     1428                LogFunc(("Version is not supported!\n"));
     1429                rc = VERR_VDI_UNSUPPORTED_VERSION;
    21491430            }
    21501431        }
    2151     }
    2152 
    2153     /* Allocate a working buffer for one block. */
    2154     void *pvBuf = RTMemTmpAlloc(cbBlock);
    2155     if (pvBuf)
    2156     {
    2157         /* Main voodoo loop, search holes and fill it. */
    2158         unsigned uBlockWrite = 0;
    2159         for (unsigned uBlock = 0; uBlock < cBlocksAllocated2; uBlock++)
    2160         {
    2161             if (paBlocks2[uBlock] != VDI_IMAGE_BLOCK_FREE)
     1432        else
     1433            rc = VERR_VDI_IMAGE_READ_ONLY;
     1434    }
     1435    else
     1436        rc = VERR_VDI_NOT_OPENED;
     1437
     1438    LogFlowFunc(("returns %Vrc\n", rc));
     1439    return rc;
     1440}
     1441
     1442/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
     1443static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
     1444{
     1445    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
     1446    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1447    int rc;
     1448
     1449    Assert(pImage);
     1450
     1451    if (pImage)
     1452    {
     1453        *pUuid = *getImageModificationUUID(&pImage->Header);
     1454        rc = VINF_SUCCESS;
     1455    }
     1456    else
     1457        rc = VERR_VDI_NOT_OPENED;
     1458
     1459    LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
     1460    return rc;
     1461}
     1462
     1463/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
     1464static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
     1465{
     1466    LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
     1467    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1468    int rc = VINF_SUCCESS;
     1469
     1470    Assert(pImage);
     1471
     1472    if (pImage)
     1473    {
     1474        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
     1475        {
     1476            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
     1477                pImage->Header.u.v1.uuidModify = *pUuid;
     1478            /* Make it possible to clone old VDIs. */
     1479            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
     1480                pImage->Header.u.v0.uuidModify = *pUuid;
     1481            else
    21621482            {
    2163                 /* Read the block from file and check for zeroes. */
    2164                 uint64_t u64Offset = ((uint64_t)uBlock << pImage->uShiftIndex2Offset)
    2165                                    + (pImage->offStartData + pImage->offStartBlockData);
    2166                 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2167                 if (VBOX_FAILURE(rc))
    2168                 {
    2169                     Log(("VDIShrinkImage: seek rc=%Vrc filename=\"%s\" uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2170                          rc, pImage->szFilename, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2171                     break;
    2172                 }
    2173                 rc = RTFileRead(pImage->File, pvBuf, cbBlock, NULL);
    2174                 if (VBOX_FAILURE(rc))
    2175                 {
    2176                     Log(("VDIShrinkImage: read rc=%Vrc filename=\"%s\" cbBlock=%u uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2177                          rc, pImage->szFilename, cbBlock, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2178                     break;
    2179                 }
    2180 
    2181                 /* Check block for data. */
    2182                 Assert(cbBlock % 4 == 0);
    2183                 if (ASMBitFirstSet(pvBuf, cbBlock * 8) != -1)
    2184                 {
    2185                     /* Block has a data, may be it must be moved. */
    2186                     if (uBlockWrite < uBlock)
    2187                     {
    2188                         /* Move the block. */
    2189                         u64Offset = ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)
    2190                                   + (pImage->offStartData + pImage->offStartBlockData);
    2191                         rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2192                         if (VBOX_FAILURE(rc))
    2193                         {
    2194                             Log(("VDIShrinkImage: seek(2) rc=%Vrc filename=\"%s\" uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2195                                  rc, pImage->szFilename, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2196                             break;
    2197                         }
    2198                         rc = RTFileWrite(pImage->File, pvBuf, cbBlock, NULL);
    2199                         if (VBOX_FAILURE(rc))
    2200                         {
    2201                             Log(("VDIShrinkImage: write rc=%Vrc filename=\"%s\" cbBlock=%u uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2202                                  rc, pImage->szFilename, cbBlock, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2203                             break;
    2204                         }
    2205                     }
    2206                     /* Fix the block pointer. */
    2207                     pImage->paBlocks[paBlocks2[uBlock]] = uBlockWrite;
    2208                     uBlockWrite++;
    2209                 }
    2210                 else
    2211                 {
    2212                     Log(("VDIShrinkImage: found a zeroed block, uBlock=%u\n", uBlock));
    2213 
    2214                     /* Fix the block pointer. */
    2215                     pImage->paBlocks[paBlocks2[uBlock]] = VDI_IMAGE_BLOCK_ZERO;
    2216                 }
     1483                LogFunc(("Version is not supported!\n"));
     1484                rc = VERR_VDI_UNSUPPORTED_VERSION;
    22171485            }
     1486        }
     1487        else
     1488            rc = VERR_VDI_IMAGE_READ_ONLY;
     1489    }
     1490    else
     1491        rc = VERR_VDI_NOT_OPENED;
     1492
     1493    LogFlowFunc(("returns %Vrc\n", rc));
     1494    return rc;
     1495}
     1496
     1497/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
     1498static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
     1499{
     1500    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
     1501    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1502    int rc;
     1503
     1504    Assert(pImage);
     1505
     1506    if (pImage)
     1507    {
     1508        *pUuid = *getImageParentUUID(&pImage->Header);
     1509        rc = VINF_SUCCESS;
     1510    }
     1511    else
     1512        rc = VERR_VDI_NOT_OPENED;
     1513
     1514    LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
     1515    return rc;
     1516}
     1517
     1518/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
     1519static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
     1520{
     1521    LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
     1522    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1523    int rc = VINF_SUCCESS;
     1524
     1525    Assert(pImage);
     1526
     1527    if (pImage)
     1528    {
     1529        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
     1530        {
     1531            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
     1532                pImage->Header.u.v1.uuidLinkage = *pUuid;
     1533            /* Make it possible to clone old VDIs. */
     1534            else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
     1535                pImage->Header.u.v0.uuidLinkage = *pUuid;
    22181536            else
    2219                 Log(("VDIShrinkImage: found an unused block, uBlock=%u\n", uBlock));
    2220 
    2221             if (pfnProgress)
    22221537            {
    2223                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2224                             (uBlock * 100) / cBlocksAllocated2,
    2225                             pvUser);
    2226                 /* Shrink is unbreakable operation! */
     1538                LogFunc(("Version is not supported!\n"));
     1539                rc = VERR_VDI_UNSUPPORTED_VERSION;
    22271540            }
    22281541        }
    2229 
    2230         RTMemTmpFree(pvBuf);
    2231 
    2232         if (    VBOX_SUCCESS(rc)
    2233             &&  uBlockWrite < cBlocksAllocated2)
    2234         {
    2235             /* File size must be shrinked. */
    2236             Log(("VDIShrinkImage: shrinking file size from %llu to %llu bytes\n",
    2237                  cbFile,
    2238                  pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)));
    2239             rc = RTFileSetSize(pImage->File,
    2240                                pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset));
    2241             if (VBOX_FAILURE(rc))
    2242                 Log(("VDIShrinkImage: RTFileSetSize rc=%Vrc\n", rc));
    2243         }
    2244         cBlocksAllocated2 = uBlockWrite;
    2245     }
    2246     else
    2247     {
    2248         Log(("VDIShrinkImage: failed to allocate working buffer (%u bytes)\n", cbBlock));
    2249         rc = VERR_NO_MEMORY;
    2250     }
    2251 
    2252     /* Save header and blocks array. */
    2253     if (VBOX_SUCCESS(rc))
    2254     {
    2255         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated2);
    2256         rc = vdiUpdateBlocks(pImage);
    2257         if (pfnProgress)
    2258             pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2259     }
    2260 
    2261     /* Do debug dump. */
    2262     vdiDumpImage(pImage);
    2263 
    2264     /* Clean up. */
    2265     RTMemTmpFree(paBlocks2);
    2266     vdiCloseImage(pImage);
    2267 
    2268     LogFlow(("VDIShrinkImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2269     return rc;
    2270 }
    2271 
    2272 /**
    2273  * Converts image file from older VDI formats to current one.
    2274  *
    2275  * @returns VBox status code.
    2276  * @param   pszFilename     Name of the image file to convert.
    2277  * @param   pfnProgress     Progress callback. Optional.
    2278  * @param   pvUser          User argument for the progress callback.
    2279  * @remark  Only used by vditool
    2280  */
    2281 VBOXDDU_DECL(int) VDIConvertImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2282 {
    2283     LogFlow(("VDIConvertImage:\n"));
    2284 
    2285     /* Check arguments. */
    2286     if (    !pszFilename
    2287         ||  *pszFilename == '\0')
    2288     {
    2289         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2290         return VERR_INVALID_PARAMETER;
    2291     }
    2292 
    2293     PVDIIMAGEDESC pImage;
    2294     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2295     if (VBOX_FAILURE(rc))
    2296     {
    2297         Log(("VDIConvertImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2298         return rc;
    2299     }
    2300 
    2301     VDIHEADER Header = {0};
    2302     int off;
    2303     uint64_t cbFile;
    2304     uint64_t cbData;
    2305 
    2306     if (pImage->fReadOnly)
    2307     {
    2308         Log(("VDIConvertImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2309         rc = VERR_VDI_IMAGE_READ_ONLY;
    2310         goto l_conversion_failed;
    2311     }
    2312 
    2313     if (pImage->PreHeader.u32Version != 0x00000002)
    2314     {
    2315         Log(("VDIConvertImage: unsupported version=%08X filename=\"%s\"\n",
    2316              pImage->PreHeader.u32Version, pszFilename));
    2317         rc = VERR_VDI_UNSUPPORTED_VERSION;
    2318         goto l_conversion_failed;
    2319     }
    2320 
    2321     /* Build new version header from old one. */
    2322     vdiInitHeader(&Header,
    2323                   getImageType(&pImage->Header),
    2324                   VDI_IMAGE_FLAGS_DEFAULT,    /* Safety issue: Always use default flags. */
    2325                   getImageComment(&pImage->Header),
    2326                   getImageDiskSize(&pImage->Header),
    2327                   getImageBlockSize(&pImage->Header),
    2328                   0);
    2329     setImageBlocksAllocated(&Header, getImageBlocksAllocated(&pImage->Header));
    2330     /* Set both the LCHSGeometry and LegacyGeometry. If they're wrong they
    2331      * will be fixed later when the image is used. */
    2332     if (getImageLCHSGeometry(&pImage->Header))
    2333     {
    2334         Header.u.v1.LegacyGeometry = *getImageLCHSGeometry(&pImage->Header);
    2335         Header.u.v1plus.LCHSGeometry = *getImageLCHSGeometry(&pImage->Header);
    2336     }
    2337     *getImageCreationUUID(&Header) = *getImageCreationUUID(&pImage->Header);
    2338     *getImageModificationUUID(&Header) = *getImageModificationUUID(&pImage->Header);
    2339 
    2340     /* Calc data offset. */
    2341     off = getImageDataOffset(&Header) - getImageDataOffset(&pImage->Header);
    2342     if (off <= 0)
    2343     {
    2344         rc = VERR_VDI_INVALID_HEADER;
    2345         goto l_conversion_failed;
    2346     }
    2347 
    2348     rc = RTFileGetSize(pImage->File, &cbFile);
    2349     if (VBOX_FAILURE(rc))
    2350         goto l_conversion_failed;
    2351 
    2352     /* Check file size. */
    2353     cbData = cbFile - getImageDataOffset(&pImage->Header);
    2354     if (cbData != (uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset)
    2355     {
    2356         AssertMsgFailed(("Invalid file size, broken image?\n"));
    2357         rc = VERR_VDI_INVALID_HEADER;
    2358         goto l_conversion_failed;
    2359     }
    2360 
    2361     /* Expand file. */
    2362     rc = RTFileSetSize(pImage->File, cbFile + off);
    2363     if (VBOX_FAILURE(rc))
    2364         goto l_conversion_failed;
    2365 
    2366     if (cbData > 0)
    2367     {
    2368         /* Calc current file position to move data from. */
    2369         uint64_t offFile;
    2370         if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2371             offFile = cbFile - VDIDISK_DEFAULT_BUFFER_SIZE;
    23721542        else
    2373             offFile = getImageDataOffset(&pImage->Header);
    2374 
    2375         unsigned cMoves = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2376         unsigned c = 0;
    2377 
    2378         /* alloc tmp buffer */
    2379         void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    2380         if (pvBuf)
    2381         {
    2382             /* Move data. */
    2383             for (;;)
     1543            rc = VERR_VDI_IMAGE_READ_ONLY;
     1544    }
     1545    else
     1546        rc = VERR_VDI_NOT_OPENED;
     1547
     1548    LogFlowFunc(("returns %Vrc\n", rc));
     1549    return rc;
     1550}
     1551
     1552/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
     1553static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
     1554{
     1555    LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
     1556    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1557    int rc;
     1558
     1559    Assert(pImage);
     1560
     1561    if (pImage)
     1562    {
     1563        *pUuid = *getImageParentModificationUUID(&pImage->Header);
     1564        rc = VINF_SUCCESS;
     1565    }
     1566    else
     1567        rc = VERR_VDI_NOT_OPENED;
     1568
     1569    LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
     1570    return rc;
     1571}
     1572
     1573/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
     1574static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
     1575{
     1576    LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
     1577    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1578    int rc = VINF_SUCCESS;
     1579
     1580    Assert(pImage);
     1581
     1582    if (pImage)
     1583    {
     1584        if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
     1585        {
     1586            if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
     1587                pImage->Header.u.v1.uuidParentModify = *pUuid;
     1588            else
    23841589            {
    2385                 unsigned cbToMove = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2386 
    2387                 /* Read. */
    2388                 rc = RTFileSeek(pImage->File, offFile, RTFILE_SEEK_BEGIN, NULL);
    2389                 if (VBOX_FAILURE(rc))
    2390                     break;
    2391                 rc = RTFileRead(pImage->File, pvBuf, cbToMove, NULL);
    2392                 if (VBOX_FAILURE(rc))
    2393                     break;
    2394 
    2395                 /* Write. */
    2396                 rc = RTFileSeek(pImage->File, offFile + off, RTFILE_SEEK_BEGIN, NULL);
    2397                 if (VBOX_FAILURE(rc))
    2398                     break;
    2399                 rc = RTFileWrite(pImage->File, pvBuf, cbToMove, NULL);
    2400                 if (VBOX_FAILURE(rc))
    2401                     break;
    2402 
    2403                 if (pfnProgress)
    2404                 {
    2405                     c++;
    2406                     pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2407                                 (c * 100) / cMoves,
    2408                                 pvUser);
    2409                     /* Note: conversion is non breakable operation, skipping rc here. */
    2410                 }
    2411 
    2412                 cbData -= cbToMove;
    2413                 if (cbData == 0)
    2414                     break;
    2415 
    2416                 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2417                     offFile -= VDIDISK_DEFAULT_BUFFER_SIZE;
    2418                 else
    2419                     offFile = getImageDataOffset(&pImage->Header);
     1590                LogFunc(("Version is not supported!\n"));
     1591                rc = VERR_VDI_UNSUPPORTED_VERSION;
    24201592            }
    2421 
    2422             /* Fill the beginning of file with zeroes to wipe out old headers etc. */
    2423             if (VBOX_SUCCESS(rc))
    2424             {
    2425                 Assert(offFile + off <= VDIDISK_DEFAULT_BUFFER_SIZE);
    2426                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2427                 if (VBOX_SUCCESS(rc))
    2428                 {
    2429                     memset(pvBuf, 0, (unsigned)offFile + off);
    2430                     rc = RTFileWrite(pImage->File, pvBuf, (unsigned)offFile + off, NULL);
    2431                 }
    2432             }
    2433 
    2434             RTMemTmpFree(pvBuf);
    24351593        }
    24361594        else
    2437             rc = VERR_NO_MEMORY;
    2438 
    2439         if (VBOX_FAILURE(rc))
    2440             goto l_conversion_failed;
    2441     }
    2442 
    2443     if (pfnProgress)
    2444     {
    2445         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2446        /* Note: conversion is non breakable operation, skipping rc here. */
    2447     }
    2448 
    2449     /* Data moved, now we need to save new pre header, header and blocks array. */
    2450 
    2451     vdiInitPreHeader(&pImage->PreHeader);
    2452     pImage->Header = Header;
    2453 
    2454     /* Setup image parameters by header. */
    2455     vdiSetupImageDesc(pImage);
    2456 
    2457     /* Write pre-header. */
    2458     rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2459     if (VBOX_FAILURE(rc))
    2460         goto l_conversion_failed;
    2461     rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    2462     if (VBOX_FAILURE(rc))
    2463         goto l_conversion_failed;
    2464 
    2465     /* Write header and blocks array. */
    2466     rc = vdiUpdateBlocks(pImage);
    2467 
    2468 l_conversion_failed:
    2469     vdiCloseImage(pImage);
    2470 
    2471     LogFlow(("VDIConvertImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2472     return rc;
    2473 }
    2474 
    2475 /**
    2476  * Queries the image's UUID and parent UUIDs.
    2477  *
    2478  * @returns VBox status code.
    2479  * @param   pszFilename             Name of the image file to operate on.
    2480  * @param   pUuid                   Where to store image UUID (can be NULL).
    2481  * @param   pModificationUuid       Where to store modification UUID (can be NULL).
    2482  * @param   pParentUuuid            Where to store parent UUID (can be NULL).
    2483  * @param   pParentModificationUuid Where to store parent modification UUID (can be NULL).
    2484  */
    2485 VBOXDDU_DECL(int) VDIGetImageUUIDs(const char *pszFilename,
    2486                                    PRTUUID pUuid, PRTUUID pModificationUuid,
    2487                                    PRTUUID pParentUuid, PRTUUID pParentModificationUuid)
    2488 {
    2489     LogFlow(("VDIGetImageUUIDs:\n"));
    2490 
    2491     /* Check arguments. */
    2492     if (    !pszFilename
    2493         ||  *pszFilename == '\0')
    2494     {
    2495         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2496         return VERR_INVALID_PARAMETER;
    2497     }
    2498 
    2499     /*
    2500      * Try open the specified image.
    2501      */
    2502     PVDIIMAGEDESC pImage;
    2503     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2504     if (VBOX_FAILURE(rc))
    2505     {
    2506         Log(("VDIGetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2507         return rc;
    2508     }
    2509 
    2510     /*
    2511      * Query data.
    2512      */
    2513     if (pUuid)
    2514     {
    2515         PCRTUUID pTmpUuid = getImageCreationUUID(&pImage->Header);
    2516         if (pTmpUuid)
    2517             *pUuid = *pTmpUuid;
    2518         else
    2519             RTUuidClear(pUuid);
    2520     }
    2521     if (pModificationUuid)
    2522     {
    2523         PCRTUUID pTmpUuid = getImageModificationUUID(&pImage->Header);
    2524         if (pTmpUuid)
    2525             *pModificationUuid = *pTmpUuid;
    2526         else
    2527             RTUuidClear(pModificationUuid);
    2528     }
    2529     if (pParentUuid)
    2530     {
    2531         PCRTUUID pTmpUuid = getImageParentUUID(&pImage->Header);
    2532         if (pTmpUuid)
    2533             *pParentUuid = *pTmpUuid;
    2534         else
    2535             RTUuidClear(pParentUuid);
    2536     }
    2537     if (pParentModificationUuid)
    2538     {
    2539         PCRTUUID pTmpUuid = getImageParentModificationUUID(&pImage->Header);
    2540         if (pTmpUuid)
    2541             *pParentModificationUuid = *pTmpUuid;
    2542         else
    2543             RTUuidClear(pParentModificationUuid);
    2544     }
    2545 
    2546     /*
    2547      * Close the image.
    2548      */
    2549     vdiCloseImage(pImage);
    2550 
    2551     return VINF_SUCCESS;
    2552 }
    2553 
    2554 /**
    2555  * Changes the image's UUID and parent UUIDs.
    2556  *
    2557  * @returns VBox status code.
    2558  * @param   pszFilename             Name of the image file to operate on.
    2559  * @param   pUuid                   Optional parameter, new UUID of the image.
    2560  * @param   pModificationUuid       Optional parameter, new modification UUID of the image.
    2561  * @param   pParentUuuid            Optional parameter, new parent UUID of the image.
    2562  * @param   pParentModificationUuid Optional parameter, new parent modification UUID of the image.
    2563  */
    2564 VBOXDDU_DECL(int) VDISetImageUUIDs(const char *pszFilename,
    2565                                    PCRTUUID pUuid, PCRTUUID pModificationUuid,
    2566                                    PCRTUUID pParentUuid, PCRTUUID pParentModificationUuid)
    2567 {
    2568     LogFlow(("VDISetImageUUIDs:\n"));
    2569 
    2570     /* Check arguments. */
    2571     if (    !pszFilename
    2572         ||  *pszFilename == '\0')
    2573     {
    2574         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2575         return VERR_INVALID_PARAMETER;
    2576     }
    2577 
    2578     PVDIIMAGEDESC pImage;
    2579     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2580     if (VBOX_FAILURE(rc))
    2581     {
    2582         Log(("VDISetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2583         return rc;
    2584     }
    2585     if (!pImage->fReadOnly)
    2586     {
    2587         if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
    2588         {
    2589             if (pUuid)
    2590                 pImage->Header.u.v1.uuidCreate = *pUuid;
    2591 
    2592             if (pModificationUuid)
    2593                 pImage->Header.u.v1.uuidModify = *pModificationUuid;
    2594 
    2595             if (pParentUuid)
    2596                 pImage->Header.u.v1.uuidLinkage = *pParentUuid;
    2597 
    2598             if (pParentModificationUuid)
    2599                 pImage->Header.u.v1.uuidParentModify = *pParentModificationUuid;
    2600 
    2601             /* write out new header */
    2602             rc = vdiUpdateHeader(pImage);
    2603             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    2604                              pImage->szFilename, rc));
    2605         }
    2606         /* Make it possible to clone old VDIs. */
    2607         else if (   GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
    2608                  && !pParentUuid
    2609                  && !pParentModificationUuid)
    2610         {
    2611             if (pUuid)
    2612                 pImage->Header.u.v0.uuidCreate = *pUuid;
    2613 
    2614             if (pModificationUuid)
    2615                 pImage->Header.u.v0.uuidModify = *pModificationUuid;
    2616 
    2617             /* write out new header */
    2618             rc = vdiUpdateHeader(pImage);
    2619             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    2620                              pImage->szFilename, rc));
    2621         }
    2622         else
    2623         {
    2624             Log(("VDISetImageUUIDs: Version is not supported!\n"));
    2625             rc = VERR_VDI_UNSUPPORTED_VERSION;
    2626         }
    2627     }
    2628     else
    2629     {
    2630         Log(("VDISetImageUUIDs: image \"%s\" is opened as read-only!\n", pszFilename));
    2631         rc = VERR_VDI_IMAGE_READ_ONLY;
    2632     }
    2633 
    2634     vdiCloseImage(pImage);
    2635     return rc;
    2636 }
    2637 
    2638 /**
    2639  * Merges two images having a parent/child relationship (both directions).
    2640  *
    2641  * @returns VBox status code.
    2642  * @param   pszFilenameFrom         Name of the image file to merge from.
    2643  * @param   pszFilenameTo           Name of the image file to merge into.
    2644  * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
    2645  * @param   pvUser          User argument for the progress callback.
    2646  */
    2647 VBOXDDU_DECL(int) VDIMergeImage(const char *pszFilenameFrom, const char *pszFilenameTo,
    2648                                 PFNVMPROGRESS pfnProgress, void *pvUser)
    2649 {
    2650     LogFlow(("VDIMergeImage:\n"));
    2651 
    2652     /* Check arguments. */
    2653     if (    !pszFilenameFrom
    2654         ||  *pszFilenameFrom == '\0'
    2655         ||  !pszFilenameTo
    2656         ||  *pszFilenameTo   == '\0')
    2657     {
    2658         AssertMsgFailed(("Invalid arguments: pszFilenameFrom=%p, pszFilenameTo=%p\n", pszFilenameFrom, pszFilenameTo));
    2659         return VERR_INVALID_PARAMETER;
    2660     }
    2661 
    2662     PVDIIMAGEDESC pImageFrom;
    2663     int rc = vdiOpenImage(&pImageFrom, pszFilenameFrom, VDI_OPEN_FLAGS_READONLY, NULL);
    2664     if (VBOX_FAILURE(rc))
    2665     {
    2666         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pstFilenameFrom=\"%s\"\n", rc, pszFilenameFrom));
    2667         return rc;
    2668     }
    2669 
    2670     PVDIIMAGEDESC pImageTo;
    2671     rc = vdiOpenImage(&pImageTo, pszFilenameTo, VDI_OPEN_FLAGS_NORMAL, NULL);
    2672     if (VBOX_FAILURE(rc))
    2673     {
    2674         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pszFilenameTo=\"%s\"\n", rc, pszFilenameTo));
    2675         vdiCloseImage(pImageFrom);
    2676         return rc;
    2677     }
    2678     if (pImageTo->fReadOnly)
    2679     {
    2680         Log(("VDIMergeImage: image \"%s\" is opened as read-only!\n", pszFilenameTo));
    2681         vdiCloseImage(pImageFrom);
    2682         vdiCloseImage(pImageTo);
    2683         return VERR_VDI_IMAGE_READ_ONLY;
    2684     }
    2685 
    2686     /*
    2687      *  when merging, we should not update the modification uuid of the target
    2688      *  image, because from the point of view of its children, it hasn't been
    2689      *  logically changed after the successful merge.
    2690      */
    2691     vdiDisableLastModifiedUpdate(pImageTo);
    2692 
    2693     /*
    2694      * Check in which direction we merge
    2695      */
    2696 
    2697     bool bParentToChild = false;
    2698     if (   getImageParentUUID(&pImageFrom->Header)
    2699         && !RTUuidCompare(getImageParentUUID(&pImageFrom->Header),
    2700                           getImageCreationUUID(&pImageTo->Header))
    2701         && !RTUuidCompare(getImageParentModificationUUID(&pImageFrom->Header),
    2702                           getImageModificationUUID(&pImageTo->Header)))
    2703     {
    2704         /* we merge from a child to its parent */
    2705     }
    2706     else
    2707     if (   getImageParentUUID(&pImageTo->Header)
    2708         && !RTUuidCompare(getImageParentUUID(&pImageTo->Header),
    2709                           getImageCreationUUID(&pImageFrom->Header))
    2710         && !RTUuidCompare(getImageParentModificationUUID(&pImageTo->Header),
    2711                           getImageModificationUUID(&pImageFrom->Header)))
    2712     {
    2713         /* we merge from a parent to its child */
    2714         bParentToChild = true;
    2715     }
    2716     else
    2717     {
    2718         /* the images are not related, we can't merge! */
    2719         Log(("VDIMergeImages: images do not have a parent/child or child/parent relationship!\n"));
    2720         rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    2721     }
    2722 
    2723     rc = vdiMergeImages(pImageFrom, pImageTo, bParentToChild, pfnProgress, pvUser);
    2724 
    2725     if (pfnProgress)
    2726     {
    2727         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2728        /* Note: commiting is non breakable operation, skipping rc here. */
    2729     }
    2730 
    2731     /* cleanup */
    2732     vdiCloseImage(pImageFrom);
    2733     vdiCloseImage(pImageTo);
    2734 
    2735     Log(("VDIMergeImage: done, returning with rc = %Vrc\n", rc));
    2736     return rc;
    2737 }
    2738 
    2739 
    2740 /**
    2741  * Initialize the VDIDISK structure.
    2742  */
    2743 void vdiInitVDIDisk(PVDIDISK pDisk)
    2744 {
    2745     Assert(pDisk);
    2746     pDisk->u32Signature = VDIDISK_SIGNATURE;
    2747     pDisk->cImages = 0;
    2748     pDisk->pBase   = NULL;
    2749     pDisk->pLast   = NULL;
    2750     pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
    2751     pDisk->cbBuf   = VDIDISK_DEFAULT_BUFFER_SIZE;
    2752     pDisk->fHonorZeroWrites = false;
    2753 }
    2754 
    2755 /**
    2756  * internal: add image structure to the end of images list.
    2757  */
    2758 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    2759 {
    2760     pImage->pPrev = NULL;
    2761     pImage->pNext = NULL;
    2762 
    2763     if (pDisk->pBase)
    2764     {
    2765         Assert(pDisk->cImages > 0);
    2766         pImage->pPrev = pDisk->pLast;
    2767         pDisk->pLast->pNext = pImage;
    2768         pDisk->pLast = pImage;
    2769     }
    2770     else
    2771     {
    2772         Assert(pDisk->cImages == 0);
    2773         pDisk->pBase = pImage;
    2774         pDisk->pLast = pImage;
    2775     }
    2776 
    2777     pDisk->cImages++;
    2778 }
    2779 
    2780 /**
    2781  * internal: remove image structure from the images list.
    2782  */
    2783 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    2784 {
    2785     Assert(pDisk->cImages > 0);
    2786 
    2787     if (pImage->pPrev)
    2788         pImage->pPrev->pNext = pImage->pNext;
    2789     else
    2790         pDisk->pBase = pImage->pNext;
    2791 
    2792     if (pImage->pNext)
    2793         pImage->pNext->pPrev = pImage->pPrev;
    2794     else
    2795         pDisk->pLast = pImage->pPrev;
    2796 
    2797     pImage->pPrev = NULL;
    2798     pImage->pNext = NULL;
    2799 
    2800     pDisk->cImages--;
    2801 }
    2802 
    2803 /**
    2804  * Allocates and initializes VDI HDD container.
    2805  *
    2806  * @returns Pointer to newly created HDD container with no one opened image file.
    2807  * @returns NULL on failure, typically out of memory.
    2808  */
    2809 VBOXDDU_DECL(PVDIDISK) VDIDiskCreate(void)
    2810 {
    2811     PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
    2812     if (pDisk)
    2813         vdiInitVDIDisk(pDisk);
    2814     LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
    2815     return pDisk;
    2816 }
    2817 
    2818 /**
    2819  * Destroys VDI HDD container. If container has opened image files they will be closed.
    2820  *
    2821  * @param   pDisk           Pointer to VDI HDD container.
    2822  */
    2823 VBOXDDU_DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
    2824 {
    2825     LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
    2826     /* sanity check */
    2827     Assert(pDisk);
    2828     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2829 
    2830     if (pDisk)
    2831     {
    2832         VDIDiskCloseAllImages(pDisk);
    2833         RTMemFree(pDisk);
    2834     }
    2835 }
    2836 
    2837 /**
    2838  * Get working buffer size of VDI HDD container.
    2839  *
    2840  * @returns Working buffer size in bytes.
    2841  */
    2842 VBOXDDU_DECL(unsigned) VDIDiskGetBufferSize(PVDIDISK pDisk)
    2843 {
    2844     /* sanity check */
    2845     Assert(pDisk);
    2846     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2847 
    2848     LogFlow(("VDIDiskGetBufferSize: returns %u\n", pDisk->cbBuf));
    2849     return pDisk->cbBuf;
    2850 }
    2851 
    2852 /**
    2853  * Get read/write mode of VDI HDD.
    2854  *
    2855  * @returns Disk ReadOnly status.
    2856  * @returns true if no one VDI image is opened in HDD container.
    2857  */
    2858 VBOXDDU_DECL(bool) VDIDiskIsReadOnly(PVDIDISK pDisk)
    2859 {
    2860     /* sanity check */
    2861     Assert(pDisk);
    2862     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2863 
    2864     if (pDisk->pLast)
    2865     {
    2866         LogFlow(("VDIDiskIsReadOnly: returns %u\n", pDisk->pLast->fReadOnly));
    2867         return pDisk->pLast->fReadOnly;
    2868     }
    2869 
    2870     AssertMsgFailed(("No disk image is opened!\n"));
    2871     return true;
    2872 }
    2873 
    2874 /**
    2875  * Get disk size of VDI HDD container.
    2876  *
    2877  * @returns Virtual disk size in bytes.
    2878  * @returns 0 if no one VDI image is opened in HDD container.
    2879  */
    2880 VBOXDDU_DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
    2881 {
    2882     /* sanity check */
    2883     Assert(pDisk);
    2884     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2885 
    2886     if (pDisk->pBase)
    2887     {
    2888         LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
    2889         return getImageDiskSize(&pDisk->pBase->Header);
    2890     }
    2891 
    2892     AssertMsgFailed(("No disk image is opened!\n"));
    2893     return 0;
    2894 }
    2895 
    2896 /**
    2897  * Get block size of VDI HDD container.
    2898  *
    2899  * @returns VDI image block size in bytes.
    2900  * @returns 0 if no one VDI image is opened in HDD container.
    2901  */
    2902 VBOXDDU_DECL(unsigned) VDIDiskGetBlockSize(PVDIDISK pDisk)
    2903 {
    2904     /* sanity check */
    2905     Assert(pDisk);
    2906     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2907 
    2908     if (pDisk->pBase)
    2909     {
    2910         LogFlow(("VDIDiskGetBlockSize: returns %u\n", getImageBlockSize(&pDisk->pBase->Header)));
    2911         return getImageBlockSize(&pDisk->pBase->Header);
    2912     }
    2913 
    2914     AssertMsgFailed(("No disk image is opened!\n"));
    2915     return 0;
    2916 }
    2917 
    2918 /**
    2919  * Get virtual disk LCHS geometry stored in image file.
    2920  *
    2921  * @returns VBox status code.
    2922  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    2923  * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
    2924  * @param   pDisk           Pointer to VDI HDD container.
    2925  * @param   pLCHSGeometry   Where to store LCHS geometry. Not NULL.
    2926  */
    2927 VBOXDDU_DECL(int) VDIDiskGetLCHSGeometry(PVDIDISK pDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
    2928 {
    2929     /* sanity check */
    2930     Assert(pDisk);
    2931     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2932 
    2933     if (pDisk->pBase)
    2934     {
    2935         int rc = VINF_SUCCESS;
    2936         VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
    2937         PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pDisk->pBase->Header);
    2938         if (!pGeometry)
    2939             pGeometry = &DummyGeo;
    2940 
    2941         LogFlow(("%s: C/H/S = %u/%u/%u\n",
    2942                  __FUNCTION__, pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
    2943         if (    pGeometry->cCylinders > 0
    2944             &&  pGeometry->cHeads > 0
    2945             &&  pGeometry->cSectors > 0)
    2946         {
    2947             pLCHSGeometry->cCylinders = pGeometry->cCylinders;
    2948             pLCHSGeometry->cHeads = pGeometry->cHeads;
    2949             pLCHSGeometry->cSectors = pGeometry->cSectors;
    2950         }
    2951         else
    2952             rc = VERR_VDI_GEOMETRY_NOT_SET;
    2953 
    2954         LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
    2955         return rc;
    2956     }
    2957 
    2958     AssertMsgFailed(("No disk image is opened!\n"));
    2959     return VERR_VDI_NOT_OPENED;
    2960 }
    2961 
    2962 /**
    2963  * Store virtual disk LCHS geometry into base image file of HDD container.
    2964  *
    2965  * Note that in case of unrecoverable error all images of HDD container will be closed.
    2966  *
    2967  * @returns VBox status code.
    2968  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    2969  * @param   pDisk           Pointer to VDI HDD container.
    2970  * @param   pLCHSGeometry   Where to load LCHS geometry from. Not NULL.
    2971  */
    2972 VBOXDDU_DECL(int) VDIDiskSetLCHSGeometry(PVDIDISK pDisk, PCPDMMEDIAGEOMETRY pLCHSGeometry)
    2973 {
    2974     LogFlow(("%s: C/H/S = %u/%u/%u\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
    2975     /* sanity check */
    2976     Assert(pDisk);
    2977     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    2978 
    2979     if (pDisk->pBase)
    2980     {
    2981         int rc = VINF_SUCCESS;
    2982         PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pDisk->pBase->Header);
    2983         if (pGeometry)
    2984         {
    2985             pGeometry->cCylinders = pLCHSGeometry->cCylinders;
    2986             pGeometry->cHeads = pLCHSGeometry->cHeads;
    2987             pGeometry->cSectors = pLCHSGeometry->cSectors;
    2988             pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    2989 
    2990             /* Update header information in base image file. */
    2991             rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
    2992         }
    2993         LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
    2994         return rc;
    2995     }
    2996 
    2997     AssertMsgFailed(("No disk image is opened!\n"));
    2998     return VERR_VDI_NOT_OPENED;
    2999 }
    3000 
    3001 /**
    3002  * Get number of opened images in HDD container.
    3003  *
    3004  * @returns Number of opened images for HDD container. 0 if no images is opened.
    3005  * @param   pDisk           Pointer to VDI HDD container.
    3006  */
    3007 VBOXDDU_DECL(int) VDIDiskGetImagesCount(PVDIDISK pDisk)
    3008 {
    3009     /* sanity check */
    3010     Assert(pDisk);
    3011     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3012 
    3013     LogFlow(("VDIDiskGetImagesCount: returns %d\n", pDisk->cImages));
    3014     return pDisk->cImages;
    3015 }
    3016 
    3017 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage)
    3018 {
    3019     PVDIIMAGEDESC pImage = pDisk->pBase;
    3020     while (pImage && nImage)
    3021     {
    3022         pImage = pImage->pNext;
    3023         nImage--;
    3024     }
    3025     return pImage;
    3026 }
    3027 
    3028 /**
    3029  * Get version of opened image of HDD container.
    3030  *
    3031  * @returns VBox status code.
    3032  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3033  * @param   pDisk           Pointer to VDI HDD container.
    3034  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3035  * @param   puVersion       Where to store the image version.
    3036  */
    3037 VBOXDDU_DECL(int) VDIDiskGetImageVersion(PVDIDISK pDisk, int nImage, unsigned *puVersion)
    3038 {
    3039     /* sanity check */
    3040     Assert(pDisk);
    3041     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3042     Assert(puVersion);
    3043 
    3044     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3045     Assert(pImage);
    3046 
    3047     if (pImage)
    3048     {
    3049         *puVersion = pImage->PreHeader.u32Version;
    3050         LogFlow(("VDIDiskGetImageVersion: returns %08X\n", pImage->PreHeader.u32Version));
    3051         return VINF_SUCCESS;
    3052     }
    3053 
    3054     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3055     return VERR_VDI_IMAGE_NOT_FOUND;
    3056 }
    3057 
    3058 /**
    3059  * Get filename of opened image of HDD container.
    3060  *
    3061  * @returns VBox status code.
    3062  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3063  * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
    3064  * @param   pDisk           Pointer to VDI HDD container.
    3065  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3066  * @param   pszFilename     Where to store the image file name.
    3067  * @param   cbFilename      Size of buffer pszFilename points to.
    3068  */
    3069 VBOXDDU_DECL(int) VDIDiskGetImageFilename(PVDIDISK pDisk, int nImage, char *pszFilename, unsigned cbFilename)
    3070 {
    3071     /* sanity check */
    3072     Assert(pDisk);
    3073     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3074     Assert(pszFilename);
    3075 
    3076     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3077     Assert(pImage);
    3078 
    3079     if (pImage)
    3080     {
    3081         unsigned cb = strlen(pImage->szFilename);
    3082         if (cb < cbFilename)
    3083         {
    3084             /* memcpy is much better than strncpy. */
    3085             memcpy(pszFilename, pImage->szFilename, cb + 1);
    3086             LogFlow(("VDIDiskGetImageFilename: returns VINF_SUCCESS, filename=\"%s\" nImage=%d\n",
    3087                      pszFilename, nImage));
    3088             return VINF_SUCCESS;
    3089         }
    3090         else
    3091         {
    3092             AssertMsgFailed(("Out of buffer space, cbFilename=%d needed=%d\n", cbFilename, cb + 1));
    3093             return VERR_BUFFER_OVERFLOW;
    3094         }
    3095     }
    3096 
    3097     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3098     return VERR_VDI_IMAGE_NOT_FOUND;
    3099 }
    3100 
    3101 /**
    3102  * Get the comment line of opened image of HDD container.
    3103  *
    3104  * @returns VBox status code.
    3105  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3106  * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
    3107  * @param   pDisk           Pointer to VDI HDD container.
    3108  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3109  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    3110  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    3111  */
    3112 VBOXDDU_DECL(int) VDIDiskGetImageComment(PVDIDISK pDisk, int nImage, char *pszComment, unsigned cbComment)
    3113 {
    3114     /* sanity check */
    3115     Assert(pDisk);
    3116     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3117     Assert(pszComment);
    3118 
    3119     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3120     Assert(pImage);
    3121 
    3122     if (pImage)
    3123     {
    3124         char *pszTmp = getImageComment(&pImage->Header);
    3125         unsigned cb = strlen(pszTmp);
    3126         if (cb < cbComment)
    3127         {
    3128             /* memcpy is much better than strncpy. */
    3129             memcpy(pszComment, pszTmp, cb + 1);
    3130             LogFlow(("VDIDiskGetImageComment: returns VINF_SUCCESS, comment=\"%s\" nImage=%d\n",
    3131                      pszTmp, nImage));
    3132             return VINF_SUCCESS;
    3133         }
    3134         else
    3135         {
    3136             AssertMsgFailed(("Out of buffer space, cbComment=%d needed=%d\n", cbComment, cb + 1));
    3137             return VERR_BUFFER_OVERFLOW;
    3138         }
    3139     }
    3140 
    3141     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3142     return VERR_VDI_IMAGE_NOT_FOUND;
    3143 }
    3144 
    3145 /**
    3146  * Get type of opened image of HDD container.
    3147  *
    3148  * @returns VBox status code.
    3149  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3150  * @param   pDisk           Pointer to VDI HDD container.
    3151  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3152  * @param   penmType        Where to store the image type.
    3153  */
    3154 VBOXDDU_DECL(int) VDIDiskGetImageType(PVDIDISK pDisk, int nImage, PVDIIMAGETYPE penmType)
    3155 {
    3156     /* sanity check */
    3157     Assert(pDisk);
    3158     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3159     Assert(penmType);
    3160 
    3161     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3162     Assert(pImage);
    3163 
    3164     if (pImage)
    3165     {
    3166         *penmType = getImageType(&pImage->Header);
    3167         LogFlow(("VDIDiskGetImageType: returns VINF_SUCCESS, type=%X nImage=%d\n",
    3168                  *penmType, nImage));
    3169         return VINF_SUCCESS;
    3170     }
    3171 
    3172     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3173     return VERR_VDI_IMAGE_NOT_FOUND;
    3174 }
    3175 
    3176 /**
    3177  * Get flags of opened image of HDD container.
    3178  *
    3179  * @returns VBox status code.
    3180  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3181  * @param   pDisk           Pointer to VDI HDD container.
    3182  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3183  * @param   pfFlags         Where to store the image flags.
    3184  */
    3185 VBOXDDU_DECL(int) VDIDiskGetImageFlags(PVDIDISK pDisk, int nImage, unsigned *pfFlags)
    3186 {
    3187     /* sanity check */
    3188     Assert(pDisk);
    3189     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3190     Assert(pfFlags);
    3191 
    3192     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3193     Assert(pImage);
    3194 
    3195     if (pImage)
    3196     {
    3197         *pfFlags = getImageFlags(&pImage->Header);
    3198         LogFlow(("VDIDiskGetImageFlags: returns VINF_SUCCESS, flags=%08X nImage=%d\n",
    3199                  *pfFlags, nImage));
    3200         return VINF_SUCCESS;
    3201     }
    3202 
    3203     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3204     return VERR_VDI_IMAGE_NOT_FOUND;
    3205 }
    3206 
    3207 /**
    3208  * Get Uuid of opened image of HDD container.
    3209  *
    3210  * @returns VBox status code.
    3211  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3212  * @param   pDisk           Pointer to VDI HDD container.
    3213  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3214  * @param   pUuid           Where to store the image creation uuid.
    3215  */
    3216 VBOXDDU_DECL(int) VDIDiskGetImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3217 {
    3218     /* sanity check */
    3219     Assert(pDisk);
    3220     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3221     Assert(pUuid);
    3222 
    3223     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3224     Assert(pImage);
    3225 
    3226     if (pImage)
    3227     {
    3228         *pUuid = *getImageCreationUUID(&pImage->Header);
    3229         LogFlow(("VDIDiskGetImageUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3230                  pUuid, nImage));
    3231         return VINF_SUCCESS;
    3232     }
    3233 
    3234     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3235     return VERR_VDI_IMAGE_NOT_FOUND;
    3236 }
    3237 
    3238 /**
    3239  * Get last modification Uuid of opened image of HDD container.
    3240  *
    3241  * @returns VBox status code.
    3242  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3243  * @param   pDisk           Pointer to VDI HDD container.
    3244  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3245  * @param   pUuid           Where to store the image modification uuid.
    3246  */
    3247 VBOXDDU_DECL(int) VDIDiskGetImageModificationUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3248 {
    3249     /* sanity check */
    3250     Assert(pDisk);
    3251     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3252     Assert(pUuid);
    3253 
    3254     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3255     Assert(pImage);
    3256 
    3257     if (pImage)
    3258     {
    3259         *pUuid = *getImageModificationUUID(&pImage->Header);
    3260         LogFlow(("VDIDiskGetImageModificationUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3261                  pUuid, nImage));
    3262         return VINF_SUCCESS;
    3263     }
    3264 
    3265     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3266     return VERR_VDI_IMAGE_NOT_FOUND;
    3267 }
    3268 
    3269 /**
    3270  * Get Uuid of opened image's parent image.
    3271  *
    3272  * @returns VBox status code.
    3273  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3274  * @param   pDisk           Pointer to VDI HDD container.
    3275  * @param   nImage          Image number, counts from 0. 0 is always base image of the container.
    3276  * @param   pUuid           Where to store the image creation uuid.
    3277  */
    3278 VBOXDDU_DECL(int) VDIDiskGetParentImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3279 {
    3280     /* sanity check */
    3281     Assert(pDisk);
    3282     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3283     Assert(pUuid);
    3284 
    3285     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3286     if (pImage)
    3287     {
    3288         *pUuid = *getImageParentUUID(&pImage->Header);
    3289         LogFlow(("VDIDiskGetParentImageUuid: returns VINF_SUCCESS, *pUuid={%Vuuid} nImage=%d\n",
    3290                  pUuid, nImage));
    3291         return VINF_SUCCESS;
    3292     }
    3293 
    3294     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3295     return VERR_VDI_IMAGE_NOT_FOUND;
    3296 }
    3297 
    3298 /**
    3299  * Relock the image as read/write or read-only.
    3300  */
    3301 int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
    3302 {
    3303     Assert(pImage);
    3304 
    3305     if (    !fReadOnly
    3306         &&  pImage->fOpen & VDI_OPEN_FLAGS_READONLY)
    3307     {
    3308         /* Can't switch read-only opened image to read-write mode. */
    3309         Log(("vdiChangeImageMode: can't switch r/o image to r/w mode, filename=\"%s\" fOpen=%X\n",
    3310              pImage->szFilename, pImage->fOpen));
    3311         return VERR_VDI_IMAGE_READ_ONLY;
    3312     }
    3313 
    3314     /* Flush last image changes if was r/w mode. */
    3315     vdiFlushImage(pImage);
    3316 
    3317     /* Change image locking. */
    3318     uint64_t cbLock = pImage->offStartData
    3319                     + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    3320     int rc = RTFileChangeLock(pImage->File,
    3321                               (fReadOnly) ?
    3322                                   RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    3323                                   RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    3324                               0,
    3325                               cbLock);
    3326     if (VBOX_SUCCESS(rc))
    3327     {
    3328         pImage->fReadOnly = fReadOnly;
    3329         Log(("vdiChangeImageMode: Image \"%s\" mode changed to %s\n",
    3330              pImage->szFilename, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3331         return VINF_SUCCESS;
    3332     }
    3333 
    3334     /* Check for the most bad error in the world. Damn! It must never happens in real life! */
    3335     if (rc == VERR_FILE_LOCK_LOST)
    3336     {
    3337         /* And what we can do now?! */
    3338         AssertMsgFailed(("Image lock has been lost for file=\"%s\"", pImage->szFilename));
    3339         Log(("vdiChangeImageMode: image lock has been lost for file=\"%s\", blocking on r/o lock wait",
    3340              pImage->szFilename));
    3341 
    3342         /* Try to obtain read lock in blocking mode. Maybe it's a very bad method. */
    3343         rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_WAIT, 0, cbLock);
    3344         AssertReleaseRC(rc);
    3345 
    3346         pImage->fReadOnly = false;
    3347         if (pImage->fReadOnly != fReadOnly)
    3348             rc = VERR_FILE_LOCK_VIOLATION;
    3349     }
    3350 
    3351     Log(("vdiChangeImageMode: Image \"%s\" mode change failed with rc=%Vrc, mode is %s\n",
    3352          pImage->szFilename, rc, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3353 
    3354     return rc;
    3355 }
    3356 
    3357 /**
    3358  * internal: try to save header in image file even if image is in read-only mode.
    3359  */
    3360 static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage)
    3361 {
    3362     int rc = VINF_SUCCESS;
    3363 
    3364     if (pImage->fReadOnly)
    3365     {
    3366         rc = vdiChangeImageMode(pImage, false);
    3367         if (VBOX_SUCCESS(rc))
    3368         {
    3369             vdiFlushImage(pImage);
    3370             rc = vdiChangeImageMode(pImage, true);
    3371             AssertReleaseRC(rc);
    3372         }
    3373     }
    3374     else
    3375         vdiFlushImage(pImage);
    3376 
    3377     return rc;
    3378 }
    3379 
    3380 /**
    3381  * Opens an image file.
    3382  *
    3383  * The first opened image file in a HDD container must have a base image type,
    3384  * others (next opened images) must be a differencing or undo images.
    3385  * Linkage is checked for differencing image to be in consistence with the previously opened image.
    3386  * When a next differencing image is opened and the last image was opened in read/write access
    3387  * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
    3388  * other processes to use images in read-only mode too.
    3389  *
    3390  * Note that the image can be opened in read-only mode if a read/write open is not possible.
    3391  * Use VDIDiskIsReadOnly to check open mode.
    3392  *
    3393  * @returns VBox status code.
    3394  * @param   pDisk           Pointer to VDI HDD container.
    3395  * @param   pszFilename     Name of the image file to open.
    3396  * @param   fOpen           Image file open mode, see VDI_OPEN_FLAGS_* constants.
    3397  */
    3398 VBOXDDU_DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
    3399 {
    3400     /* sanity check */
    3401     Assert(pDisk);
    3402     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3403 
    3404     /* Check arguments. */
    3405     if (    !pszFilename
    3406         ||  *pszFilename == '\0'
    3407         ||  (fOpen & ~VDI_OPEN_FLAGS_MASK))
    3408     {
    3409         AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
    3410         return VERR_INVALID_PARAMETER;
    3411     }
    3412     LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
    3413 
    3414     PVDIIMAGEDESC pImage;
    3415     int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
    3416     if (VBOX_SUCCESS(rc))
    3417     {
    3418         if (pDisk->pLast)
    3419         {
    3420             /* Opening differencing image. */
    3421             if (!pDisk->pLast->fReadOnly)
    3422             {
    3423                 /*
    3424                  * Previous image is opened in read/write mode -> switch it into read-only.
    3425                  */
    3426                 rc = vdiChangeImageMode(pDisk->pLast, true);
    3427             }
    3428         }
    3429         else
    3430         {
    3431             /* Opening base image, check its type. */
    3432             if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
    3433                 &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
    3434             {
    3435                 rc = VERR_VDI_INVALID_TYPE;
    3436             }
    3437         }
    3438 
    3439         if (VBOX_SUCCESS(rc))
    3440             vdiAddImageToList(pDisk, pImage);
    3441         else
    3442             vdiCloseImage(pImage);
    3443     }
    3444 
    3445     LogFlow(("VDIDiskOpenImage: returns %Vrc\n", rc));
    3446     return rc;
    3447 }
    3448 
    3449 /**
    3450  * Closes the last opened image file in the HDD container. Leaves all changes inside it.
    3451  * If previous image file was opened in read-only mode (that is normal) and closing image
    3452  * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
    3453  * will be reopened in read/write mode.
    3454  *
    3455  * @param   pDisk           Pointer to VDI HDD container.
    3456  */
    3457 VBOXDDU_DECL(void) VDIDiskCloseImage(PVDIDISK pDisk)
    3458 {
    3459     /* sanity check */
    3460     Assert(pDisk);
    3461     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3462 
    3463     PVDIIMAGEDESC pImage = pDisk->pLast;
    3464     if (pImage)
    3465     {
    3466         LogFlow(("VDIDiskCloseImage: closing image \"%s\"\n", pImage->szFilename));
    3467 
    3468         bool fWasReadOnly = pImage->fReadOnly;
    3469         vdiRemoveImageFromList(pDisk, pImage);
    3470         vdiCloseImage(pImage);
    3471 
    3472         if (    !fWasReadOnly
    3473             &&  pDisk->pLast
    3474             &&  pDisk->pLast->fReadOnly
    3475             &&  !(pDisk->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    3476         {
    3477             /*
    3478              * Closed image was opened in read/write mode, previous image was opened
    3479              * in read-only mode, try to switch it into read/write.
    3480              */
    3481             int rc = vdiChangeImageMode(pDisk->pLast, false);
    3482             NOREF(rc); /* gcc still hates unused variables... */
    3483         }
    3484 
    3485         return;
    3486     }
    3487     AssertMsgFailed(("No images to close\n"));
    3488 }
    3489 
    3490 /**
    3491  * Closes all opened image files in HDD container.
    3492  *
    3493  * @param   pDisk           Pointer to VDI HDD container.
    3494  */
    3495 VBOXDDU_DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
    3496 {
    3497     LogFlow(("VDIDiskCloseAllImages:\n"));
    3498     /* sanity check */
    3499     Assert(pDisk);
    3500     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3501 
    3502     PVDIIMAGEDESC pImage = pDisk->pLast;
    3503     while (pImage)
    3504     {
    3505         PVDIIMAGEDESC pPrev = pImage->pPrev;
    3506         vdiRemoveImageFromList(pDisk, pImage);
    3507         vdiCloseImage(pImage);
    3508         pImage = pPrev;
    3509     }
    3510     Assert(pDisk->pLast == NULL);
    3511 }
    3512 
    3513 /**
    3514  * Commits last opened differencing/undo image file of HDD container to previous one.
    3515  * If previous image file was opened in read-only mode (that must be always so) it is reopened
    3516  * as read/write to do commit operation.
    3517  * After successfull commit the previous image file again reopened in read-only mode, last opened
    3518  * image file is cleared of data and remains open and active in HDD container.
    3519  * If you want to delete image after commit you must do it manually by VDIDiskCloseImage and
    3520  * VDIDeleteImage calls.
    3521  *
    3522  * Note that in case of unrecoverable error all images of HDD container will be closed.
    3523  *
    3524  * @returns VBox status code.
    3525  * @param   pDisk           Pointer to VDI HDD container.
    3526  * @param   pfnProgress     Progress callback. Optional.
    3527  * @param   pvUser          User argument for the progress callback.
    3528  * @remark  Only used by tstVDI.
    3529  */
    3530 VBOXDDU_DECL(int) VDIDiskCommitLastDiff(PVDIDISK pDisk, PFNVMPROGRESS pfnProgress, void *pvUser)
    3531 {
    3532     LogFlow(("VDIDiskCommitLastDiff:\n"));
    3533     /* sanity check */
    3534     Assert(pDisk);
    3535     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3536 
    3537     int rc = VINF_SUCCESS;
    3538     PVDIIMAGEDESC pImage = pDisk->pLast;
    3539     if (!pImage)
    3540     {
    3541         AssertMsgFailed(("No disk image is opened!\n"));
    3542         return VERR_VDI_NOT_OPENED;
    3543     }
    3544 
    3545     if (pImage->fReadOnly)
    3546     {
    3547         AssertMsgFailed(("Image \"%s\" is read-only!\n", pImage->szFilename));
    3548         return VERR_VDI_IMAGE_READ_ONLY;
    3549     }
    3550 
    3551     if (!pImage->pPrev)
    3552     {
    3553         AssertMsgFailed(("No images to commit to!\n"));
    3554         return VERR_VDI_NO_DIFF_IMAGES;
    3555     }
    3556 
    3557     bool fWasReadOnly = pImage->pPrev->fReadOnly;
    3558     if (fWasReadOnly)
    3559     {
    3560         /* Change previous image mode to r/w. */
    3561         rc = vdiChangeImageMode(pImage->pPrev, false);
    3562         if (VBOX_FAILURE(rc))
    3563         {
    3564             Log(("VDIDiskCommitLastDiff: can't switch previous image into r/w mode, rc=%Vrc\n", rc));
    3565             return rc;
    3566         }
    3567     }
    3568 
    3569     rc = vdiCommitToImage(pDisk, pImage->pPrev, pfnProgress, pvUser);
    3570     if (VBOX_SUCCESS(rc) && fWasReadOnly)
    3571     {
    3572         /* Change previous image mode back to r/o. */
    3573         rc = vdiChangeImageMode(pImage->pPrev, true);
    3574     }
    3575 
    3576     if (VBOX_FAILURE(rc))
    3577     {
    3578         /* Failed! Close all images, can't work with VHDD at all. */
    3579         VDIDiskCloseAllImages(pDisk);
    3580         AssertMsgFailed(("Fatal: commit failed, rc=%Vrc\n", rc));
    3581     }
    3582 
    3583     return rc;
    3584 }
    3585 
    3586 /**
    3587  * Creates and opens a new differencing image file in HDD container.
    3588  * See comments for VDIDiskOpenImage function about differencing images.
    3589  *
    3590  * @returns VBox status code.
    3591  * @param   pDisk           Pointer to VDI HDD container.
    3592  * @param   pszFilename     Name of the image file to create and open.
    3593  * @param   pszComment      Pointer to image comment. NULL is ok.
    3594  * @param   pfnProgress     Progress callback. Optional.
    3595  * @param   pvUser          User argument for the progress callback.
    3596  * @remark  Only used by tstVDI.
    3597  */
    3598 VBOXDDU_DECL(int) VDIDiskCreateOpenDifferenceImage(PVDIDISK pDisk, const char *pszFilename,
    3599                                                    const char *pszComment, PFNVMPROGRESS pfnProgress,
    3600                                                    void *pvUser)
    3601 {
    3602     LogFlow(("VDIDiskCreateOpenDifferenceImage:\n"));
    3603     /* sanity check */
    3604     Assert(pDisk);
    3605     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3606     Assert(pszFilename);
    3607 
    3608     if (!pDisk->pLast)
    3609     {
    3610         AssertMsgFailed(("No disk image is opened!\n"));
    3611         return VERR_VDI_NOT_OPENED;
    3612     }
    3613 
    3614     /* Flush last parent image changes if possible. */
    3615     vdiFlushImage(pDisk->pLast);
    3616 
    3617     int rc = vdiCreateImage(pszFilename,
    3618                             VDI_IMAGE_TYPE_DIFF,
    3619                             VDI_IMAGE_FLAGS_DEFAULT,
    3620                             getImageDiskSize(&pDisk->pLast->Header),
    3621                             pszComment,
    3622                             pDisk->pLast,
    3623                             pfnProgress, pvUser);
    3624     if (VBOX_SUCCESS(rc))
    3625     {
    3626         rc = VDIDiskOpenImage(pDisk, pszFilename, VDI_OPEN_FLAGS_NORMAL);
    3627         if (VBOX_FAILURE(rc))
    3628             VDIDeleteImage(pszFilename);
    3629     }
    3630     LogFlow(("VDIDiskCreateOpenDifferenceImage: returns %Vrc, filename=\"%s\"\n", rc, pszFilename));
    3631     return rc;
    3632 }
    3633 
    3634 /**
    3635  * internal: debug image dump.
    3636  *
    3637  * @remark  Only used by tstVDI.
    3638  */
    3639 static void vdiDumpImage(PVDIIMAGEDESC pImage)
    3640 {
    3641     RTLogPrintf("Dumping VDI image \"%s\" mode=%s fOpen=%X File=%08X\n",
    3642                 pImage->szFilename,
    3643                 (pImage->fReadOnly) ? "r/o" : "r/w",
    3644                 pImage->fOpen,
     1595            rc = VERR_VDI_IMAGE_READ_ONLY;
     1596    }
     1597    else
     1598        rc = VERR_VDI_NOT_OPENED;
     1599
     1600    LogFlowFunc(("returns %Vrc\n", rc));
     1601    return rc;
     1602}
     1603
     1604/** @copydoc VBOXHDDBACKEND::pfnDump */
     1605static void vdiDump(void *pBackendData)
     1606{
     1607    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
     1608
     1609    RTLogPrintf("Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%08X\n",
     1610                pImage->pszFilename,
     1611                (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
     1612                pImage->uOpenFlags,
    36451613                pImage->File);
    36461614    RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
     
    36671635        RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
    36681636    RTLogPrintf("Image:  fFlags=%08X offStartBlocks=%u offStartData=%u\n",
    3669                 pImage->fFlags, pImage->offStartBlocks, pImage->offStartData);
     1637                pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
    36701638    RTLogPrintf("Image:  uBlockMask=%08X uShiftIndex2Offset=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
    36711639                pImage->uBlockMask,
     
    36961664}
    36971665
    3698 /**
    3699  * Debug helper - dumps all opened images of HDD container into the log file.
    3700  *
    3701  * @param   pDisk           Pointer to VDI HDD container.
    3702  * @remark  Only used by tstVDI and vditool
    3703  */
    3704 VBOXDDU_DECL(void) VDIDiskDumpImages(PVDIDISK pDisk)
    3705 {
    3706     /* sanity check */
    3707     Assert(pDisk);
    3708     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3709 
    3710     RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
    3711     for (PVDIIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
    3712         vdiDumpImage(pImage);
    3713 }
    3714 
     1666VBOXHDDBACKEND g_VDIBackend =
     1667{
     1668    /* pszBackendName */
     1669    "VDI",
     1670    /* cbSize */
     1671    sizeof(VBOXHDDBACKEND),
     1672    /* pfnCheckIfValid */
     1673    vdiCheckIfValid,
     1674    /* pfnOpen */
     1675    vdiOpen,
     1676    /* pfnCreate */
     1677    vdiCreate,
     1678    /* pfnRename */
     1679    vdiRename,
     1680    /* pfnClose */
     1681    vdiClose,
     1682    /* pfnRead */
     1683    vdiRead,
     1684    /* pfnWrite */
     1685    vdiWrite,
     1686    /* pfnFlush */
     1687    vdiFlush,
     1688    /* pfnGetVersion */
     1689    vdiGetVersion,
     1690    /* pfnGetImageType */
     1691    vdiGetImageType,
     1692    /* pfnGetSize */
     1693    vdiGetSize,
     1694    /* pfnGetFileSize */
     1695    vdiGetFileSize,
     1696    /* pfnGetPCHSGeometry */
     1697    vdiGetPCHSGeometry,
     1698    /* pfnSetPCHSGeometry */
     1699    vdiSetPCHSGeometry,
     1700    /* pfnGetLCHSGeometry */
     1701    vdiGetLCHSGeometry,
     1702    /* pfnSetLCHSGeometry */
     1703    vdiSetLCHSGeometry,
     1704    /* pfnGetImageFlags */
     1705    vdiGetImageFlags,
     1706    /* pfnGetOpenFlags */
     1707    vdiGetOpenFlags,
     1708    /* pfnSetOpenFlags */
     1709    vdiSetOpenFlags,
     1710    /* pfnGetComment */
     1711    vdiGetComment,
     1712    /* pfnSetComment */
     1713    vdiSetComment,
     1714    /* pfnGetUuid */
     1715    vdiGetUuid,
     1716    /* pfnSetUuid */
     1717    vdiSetUuid,
     1718    /* pfnGetModificationUuid */
     1719    vdiGetModificationUuid,
     1720    /* pfnSetModificationUuid */
     1721    vdiSetModificationUuid,
     1722    /* pfnGetParentUuid */
     1723    vdiGetParentUuid,
     1724    /* pfnSetParentUuid */
     1725    vdiSetParentUuid,
     1726    /* pfnGetParentModificationUuid */
     1727    vdiGetParentModificationUuid,
     1728    /* pfnSetParentModificationUuid */
     1729    vdiSetParentModificationUuid,
     1730    /* pfnDump */
     1731    vdiDump
     1732};
     1733
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette