VirtualBox

Changeset 1565 in vbox for trunk/src/VBox/Devices/Storage


Ignore:
Timestamp:
Mar 20, 2007 3:06:26 AM (18 years ago)
Author:
vboxsync
Message:

Split out the bulk of the VDI code from VBoxDD and put it in VBoxDDU (devices and drivers utilities).

Location:
trunk/src/VBox/Devices/Storage
Files:
2 edited
2 copied

Legend:

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

    r860 r1565  
    3838#include <iprt/asm.h>
    3939
     40#include "VDICore.h"
    4041#include "Builtins.h"
    4142
     43
    4244/*******************************************************************************
    43 *   Constants And Macros, Structures and Typedefs                              *
     45*   Defined Constants And Macros                                               *
    4446*******************************************************************************/
    45 /** The Sector size.
    46  * Currently we support only 512 bytes sectors.
    47  */
    48 #define VDI_GEOMETRY_SECTOR_SIZE    (512)
    49 /**  512 = 2^^9 */
    50 #define VDI_GEOMETRY_SECTOR_SHIFT   (9)
    51 
    52 /**
    53  * Harddisk geometry.
    54  */
    55 #pragma pack(1)
    56 typedef struct VDIDISKGEOMETRY
    57 {
    58     /** Cylinders. */
    59     uint32_t    cCylinders;
    60     /** Heads. */
    61     uint32_t    cHeads;
    62     /** Sectors per track. */
    63     uint32_t    cSectors;
    64     /** Sector size. (bytes per sector) */
    65     uint32_t    cbSector;
    66 } VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
    67 #pragma pack()
    68 
    69 /** Image signature. */
    70 #define VDI_IMAGE_SIGNATURE   (0xbeda107f)
    71 
    72 /**
    73  * Pre-Header to be stored in image file - used for version control.
    74  */
    75 #pragma pack(1)
    76 typedef struct VDIPREHEADER
    77 {
    78     /** Just text info about image type, for eyes only. */
    79     char            szFileInfo[64];
    80     /** The image signature (VDI_IMAGE_SIGNATURE). */
    81     uint32_t        u32Signature;
    82     /** The image version (VDI_IMAGE_VERSION). */
    83     uint32_t        u32Version;
    84 } VDIPREHEADER, *PVDIPREHEADER;
    85 #pragma pack()
    86 
    87 /**
    88  * Size of szComment field of HDD image header.
    89  */
    90 #define VDI_IMAGE_COMMENT_SIZE    256
    91 
    92 /**
    93  * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
    94  * Prepended by VDIPREHEADER.
    95  */
    96 #pragma pack(1)
    97 typedef struct VDIHEADER0
    98 {
    99     /** The image type (VDI_IMAGE_TYPE_*). */
    100     uint32_t        u32Type;
    101     /** Image flags (VDI_IMAGE_FLAGS_*). */
    102     uint32_t        fFlags;
    103     /** Image comment. (UTF-8) */
    104     char            szComment[VDI_IMAGE_COMMENT_SIZE];
    105     /** Image geometry. */
    106     VDIDISKGEOMETRY Geometry;
    107     /** Size of disk (in bytes). */
    108     uint64_t        cbDisk;
    109     /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
    110     uint32_t        cbBlock;
    111     /** Number of blocks. */
    112     uint32_t        cBlocks;
    113     /** Number of allocated blocks. */
    114     uint32_t        cBlocksAllocated;
    115     /** UUID of image. */
    116     RTUUID          uuidCreate;
    117     /** UUID of image's last modification. */
    118     RTUUID          uuidModify;
    119     /** Only for secondary images - UUID of primary image. */
    120     RTUUID          uuidLinkage;
    121 } VDIHEADER0, *PVDIHEADER0;
    122 #pragma pack()
    123 
    124 /**
    125  * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1.
    126  * Prepended by VDIPREHEADER.
    127  */
    128 #pragma pack(1)
    129 typedef struct VDIHEADER1
    130 {
    131     /** Size of this structure in bytes. */
    132     uint32_t        cbHeader;
    133     /** The image type (VDI_IMAGE_TYPE_*). */
    134     uint32_t        u32Type;
    135     /** Image flags (VDI_IMAGE_FLAGS_*). */
    136     uint32_t        fFlags;
    137     /** Image comment. (UTF-8) */
    138     char            szComment[VDI_IMAGE_COMMENT_SIZE];
    139     /** Offset of Blocks array from the begining of image file.
    140      * Should be sector-aligned for HDD access optimization. */
    141     uint32_t        offBlocks;
    142     /** Offset of image data from the begining of image file.
    143      * Should be sector-aligned for HDD access optimization. */
    144     uint32_t        offData;
    145     /** Image geometry. */
    146     VDIDISKGEOMETRY Geometry;
    147     /** BIOS HDD translation mode, see PDMBIOSTRANSLATION. */
    148     uint32_t        u32Translation;
    149     /** Size of disk (in bytes). */
    150     uint64_t        cbDisk;
    151     /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
    152     uint32_t        cbBlock;
    153     /** Size of additional service information of every data block.
    154      * Prepended before block data. May be 0.
    155      * Should be a power of 2 and sector-aligned for optimization reasons. */
    156     uint32_t        cbBlockExtra;
    157     /** Number of blocks. */
    158     uint32_t        cBlocks;
    159     /** Number of allocated blocks. */
    160     uint32_t        cBlocksAllocated;
    161     /** UUID of image. */
    162     RTUUID          uuidCreate;
    163     /** UUID of image's last modification. */
    164     RTUUID          uuidModify;
    165     /** Only for secondary images - UUID of previous image. */
    166     RTUUID          uuidLinkage;
    167     /** Only for secondary images - UUID of previous image's last modification. */
    168     RTUUID          uuidParentModify;
    169 } VDIHEADER1, *PVDIHEADER1;
    170 #pragma pack()
    171 
    172 /**
    173  * Header structure for all versions.
    174  */
    175 typedef struct VDIHEADER
    176 {
    177     unsigned        uVersion;
    178     union
    179     {
    180         VDIHEADER0    v0;
    181         VDIHEADER1    v1;
    182     } u;
    183 } VDIHEADER, *PVDIHEADER;
    184 
    185 /** Block 'pointer'. */
    186 typedef uint32_t    VDIIMAGEBLOCKPOINTER;
    187 /** Pointer to a block 'pointer'. */
    188 typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
    189 
    190 /**
    191  * Block marked as free is not allocated in image file, read from this
    192  * block may returns any random data.
    193  */
    194 #define VDI_IMAGE_BLOCK_FREE   ((VDIIMAGEBLOCKPOINTER)~0)
    195 
    196 /**
    197  * Block marked as zero is not allocated in image file, read from this
    198  * block returns zeroes.
    199  */
    200 #define VDI_IMAGE_BLOCK_ZERO   ((VDIIMAGEBLOCKPOINTER)~1)
    201 
    202 /**
    203  * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
    204  * allocated in image file.
    205  */
    206 #define VDI_IMAGE_BLOCK_UNALLOCATED   (VDI_IMAGE_BLOCK_ZERO)
    207 #define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp)   (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
    208 
    209 #define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
    210 #define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
    211 
    212 /*******************************************************************************
    213 *   Internal Functions for header access                                       *
    214 *******************************************************************************/
    215 static inline VDIIMAGETYPE getImageType(PVDIHEADER ph)
    216 {
    217     switch (GET_MAJOR_HEADER_VERSION(ph))
    218     {
    219         case 0: return (VDIIMAGETYPE)ph->u.v0.u32Type;
    220         case 1: return (VDIIMAGETYPE)ph->u.v1.u32Type;
    221     }
    222     AssertFailed();
    223     return (VDIIMAGETYPE)0;
    224 }
    225 
    226 static inline unsigned getImageFlags(PVDIHEADER ph)
    227 {
    228     switch (GET_MAJOR_HEADER_VERSION(ph))
    229     {
    230         case 0: return ph->u.v0.fFlags;
    231         case 1: return ph->u.v1.fFlags;
    232     }
    233     AssertFailed();
    234     return 0;
    235 }
    236 
    237 static inline char *getImageComment(PVDIHEADER ph)
    238 {
    239     switch (GET_MAJOR_HEADER_VERSION(ph))
    240     {
    241         case 0: return &ph->u.v0.szComment[0];
    242         case 1: return &ph->u.v1.szComment[0];
    243     }
    244     AssertFailed();
    245     return NULL;
    246 }
    247 
    248 static inline unsigned getImageBlocksOffset(PVDIHEADER ph)
    249 {
    250     switch (GET_MAJOR_HEADER_VERSION(ph))
    251     {
    252         case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
    253         case 1: return ph->u.v1.offBlocks;
    254     }
    255     AssertFailed();
    256     return 0;
    257 }
    258 
    259 static inline unsigned getImageDataOffset(PVDIHEADER ph)
    260 {
    261     switch (GET_MAJOR_HEADER_VERSION(ph))
    262     {
    263         case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
    264                        (ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
    265         case 1: return ph->u.v1.offData;
    266     }
    267     AssertFailed();
    268     return 0;
    269 }
    270 
    271 static inline PVDIDISKGEOMETRY getImageGeometry(PVDIHEADER ph)
    272 {
    273     switch (GET_MAJOR_HEADER_VERSION(ph))
    274     {
    275         case 0: return &ph->u.v0.Geometry;
    276         case 1: return &ph->u.v1.Geometry;
    277     }
    278     AssertFailed();
    279     return NULL;
    280 }
    281 
    282 static inline PDMBIOSTRANSLATION getImageTranslation(PVDIHEADER ph)
    283 {
    284     switch (GET_MAJOR_HEADER_VERSION(ph))
    285     {
    286         case 0: return PDMBIOSTRANSLATION_AUTO;
    287         case 1: return (PDMBIOSTRANSLATION)ph->u.v1.u32Translation;
    288     }
    289     AssertFailed();
    290     return PDMBIOSTRANSLATION_NONE;
    291 }
    292 
    293 static inline void setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)
    294 {
    295     switch (GET_MAJOR_HEADER_VERSION(ph))
    296     {
    297         case 0:                                                     return;
    298         case 1: ph->u.v1.u32Translation = (uint32_t)enmTranslation; return;
    299     }
    300     AssertFailed();
    301 }
    302 
    303 static inline uint64_t getImageDiskSize(PVDIHEADER ph)
    304 {
    305     switch (GET_MAJOR_HEADER_VERSION(ph))
    306     {
    307         case 0: return ph->u.v0.cbDisk;
    308         case 1: return ph->u.v1.cbDisk;
    309     }
    310     AssertFailed();
    311     return 0;
    312 }
    313 
    314 static inline unsigned getImageBlockSize(PVDIHEADER ph)
    315 {
    316     switch (GET_MAJOR_HEADER_VERSION(ph))
    317     {
    318         case 0: return ph->u.v0.cbBlock;
    319         case 1: return ph->u.v1.cbBlock;
    320     }
    321     AssertFailed();
    322     return 0;
    323 }
    324 
    325 static inline unsigned getImageExtraBlockSize(PVDIHEADER ph)
    326 {
    327     switch (GET_MAJOR_HEADER_VERSION(ph))
    328     {
    329         case 0: return 0;
    330         case 1: return ph->u.v1.cbBlockExtra;
    331     }
    332     AssertFailed();
    333     return 0;
    334 }
    335 
    336 static inline unsigned getImageBlocks(PVDIHEADER ph)
    337 {
    338     switch (GET_MAJOR_HEADER_VERSION(ph))
    339     {
    340         case 0: return ph->u.v0.cBlocks;
    341         case 1: return ph->u.v1.cBlocks;
    342     }
    343     AssertFailed();
    344     return 0;
    345 }
    346 
    347 static inline unsigned getImageBlocksAllocated(PVDIHEADER ph)
    348 {
    349     switch (GET_MAJOR_HEADER_VERSION(ph))
    350     {
    351         case 0: return ph->u.v0.cBlocksAllocated;
    352         case 1: return ph->u.v1.cBlocksAllocated;
    353     }
    354     AssertFailed();
    355     return 0;
    356 }
    357 
    358 static inline void setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
    359 {
    360     switch (GET_MAJOR_HEADER_VERSION(ph))
    361     {
    362         case 0: ph->u.v0.cBlocksAllocated = cBlocks; return;
    363         case 1: ph->u.v1.cBlocksAllocated = cBlocks; return;
    364     }
    365     AssertFailed();
    366 }
    367 
    368 static inline PRTUUID getImageCreationUUID(PVDIHEADER ph)
    369 {
    370     switch (GET_MAJOR_HEADER_VERSION(ph))
    371     {
    372         case 0: return &ph->u.v0.uuidCreate;
    373         case 1: return &ph->u.v1.uuidCreate;
    374     }
    375     AssertFailed();
    376     return NULL;
    377 }
    378 
    379 static inline PRTUUID getImageModificationUUID(PVDIHEADER ph)
    380 {
    381     switch (GET_MAJOR_HEADER_VERSION(ph))
    382     {
    383         case 0: return &ph->u.v0.uuidModify;
    384         case 1: return &ph->u.v1.uuidModify;
    385     }
    386     AssertFailed();
    387     return NULL;
    388 }
    389 
    390 static inline PRTUUID getImageParentUUID(PVDIHEADER ph)
    391 {
    392     switch (GET_MAJOR_HEADER_VERSION(ph))
    393     {
    394         case 0: return &ph->u.v0.uuidLinkage;
    395         case 1: return &ph->u.v1.uuidLinkage;
    396     }
    397     AssertFailed();
    398     return NULL;
    399 }
    400 
    401 static inline PRTUUID getImageParentModificationUUID(PVDIHEADER ph)
    402 {
    403     switch (GET_MAJOR_HEADER_VERSION(ph))
    404     {
    405         case 1: return &ph->u.v1.uuidParentModify;
    406     }
    407     AssertFailed();
    408     return NULL;
    409 }
    410 
    411 /**
    412  * Default image block size, may be changed by setBlockSize/getBlockSize.
    413  *
    414  * Note: for speed reasons block size should be a power of 2 !
    415  */
    416 #define VDI_IMAGE_DEFAULT_BLOCK_SIZE            _1M
    417 
    418 /**
    419  * fModified bit flags.
    420  */
    421 #define VDI_IMAGE_MODIFIED_FLAG                 BIT(0)
    422 #define VDI_IMAGE_MODIFIED_FIRST                BIT(1)
    423 #define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE  BIT(2)
    424 
    425 /**
    426  * Image structure
    427  */
    428 typedef struct VDIIMAGEDESC
    429 {
    430     /** Link to parent image descriptor, if any. */
    431     struct VDIIMAGEDESC    *pPrev;
    432     /** Link to child image descriptor, if any. */
    433     struct VDIIMAGEDESC    *pNext;
    434     /** File handle. */
    435     RTFILE                  File;
    436     /** True if the image is operating in readonly mode. */
    437     bool                    fReadOnly;
    438     /** Image open flags, VDI_OPEN_FLAGS_*. */
    439     unsigned                fOpen;
    440     /** Image pre-header. */
    441     VDIPREHEADER            PreHeader;
    442     /** Image header. */
    443     VDIHEADER               Header;
    444     /** Pointer to a block array. */
    445     PVDIIMAGEBLOCKPOINTER   paBlocks;
    446     /** fFlags copy from image header, for speed optimization. */
    447     unsigned                fFlags;
    448     /** Start offset of block array in image file, here for speed optimization. */
    449     unsigned                offStartBlocks;
    450     /** Start offset of data in image file, here for speed optimization. */
    451     unsigned                offStartData;
    452     /** Block mask for getting the offset into a block from a byte hdd offset. */
    453     unsigned                uBlockMask;
    454     /** Block shift value for converting byte hdd offset into paBlock index. */
    455     unsigned                uShiftOffset2Index;
    456     /** Block shift value for converting block index into offset in image. */
    457     unsigned                uShiftIndex2Offset;
    458     /** Offset of data from the beginning of block. */
    459     unsigned                offStartBlockData;
    460     /** Image is modified flags (VDI_IMAGE_MODIFIED*). */
    461     unsigned                fModified;
    462     /** Container filename. (UTF-8)
    463      * @todo Make this variable length to save a bunch of bytes. (low prio) */
    464     char                    szFilename[RTPATH_MAX];
    465 } VDIIMAGEDESC, *PVDIIMAGEDESC;
    466 
    467 /**
    468  * Default work buffer size, may be changed by setBufferSize() method.
    469  *
    470  * For best speed performance it must be equal to image block size.
    471  */
    472 #define VDIDISK_DEFAULT_BUFFER_SIZE   (VDI_IMAGE_DEFAULT_BLOCK_SIZE)
    473 
    474 /** VDIDISK Signature. */
    475 #define VDIDISK_SIGNATURE (0xbedafeda)
    476 
    477 /**
    478  * VBox HDD Container main structure, private part.
    479  */
    480 struct VDIDISK
    481 {
    482     /** Structure signature (VDIDISK_SIGNATURE). */
    483     uint32_t        u32Signature;
    484 
    485     /** Number of opened images. */
    486     unsigned        cImages;
    487 
    488     /** Base image. */
    489     PVDIIMAGEDESC   pBase;
    490 
    491     /** Last opened image in the chain.
    492      * The same as pBase if only one image is used or the last opened diff image. */
    493     PVDIIMAGEDESC   pLast;
    494 
    495     /** Default block size for newly created images. */
    496     unsigned        cbBlock;
    497 
    498     /** Working buffer size, allocated only while committing data,
    499      * copying block from primary image to secondary and saving previously
    500      * zero block. Buffer deallocated after operation complete.
    501      * @remark  For best performance buffer size must be equal to image's
    502      *          block size, however it may be decreased for memory saving.
    503      */
    504     unsigned        cbBuf;
    505 
    506     /** Flag whether zero writes should be handled normally or optimized
    507      * away if possible. */
    508     bool            fHonorZeroWrites;
    509 
    510     /** The media interface. */
    511     PDMIMEDIA       IMedia;
    512     /** Pointer to the driver instance. */
    513     PPDMDRVINS      pDrvIns;
    514 };
    515 
    516 
    51747/** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */
    51848#define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) )
     
    52555
    52656
    527 /*******************************************************************************
    528 *   Internal Functions                                                         *
    529 *******************************************************************************/
    530 static unsigned getPowerOfTwo(unsigned uNumber);
    531 static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
    532 static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
    533 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
    534                           const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    535                           uint32_t cbBlockExtra);
    536 static int  vdiValidateHeader(PVDIHEADER pHeader);
    537 static int  vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    538                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    539                           PFNVMPROGRESS pfnProgress, void *pvUser);
    540 static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
    541 static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
    542 static int  vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename, unsigned fOpen,
    543                         PVDIIMAGEDESC pParent);
    544 static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
    545 static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
    546 static int  vdiUpdateBlocks(PVDIIMAGEDESC pImage);
    547 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
    548 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
    549 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    550 #if 0 /* unused */
    551 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
     57
     58
     59/** @copydoc PDMIMEDIA::pfnGetSize */
     60static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
     61{
     62    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     63    uint64_t cb = VDIDiskGetSize(pData);
     64    LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
     65    return cb;
     66}
     67
     68
     69/**
     70 * Get stored media geometry - BIOS property.
     71 *
     72 * @see PDMIMEDIA::pfnBiosGetGeometry for details.
     73 */
     74static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
     75{
     76    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     77    int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
     78    if (VBOX_SUCCESS(rc))
     79    {
     80        LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
     81        return VINF_SUCCESS;
     82    }
     83    Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
     84    return VERR_PDM_GEOMETRY_NOT_SET;
     85}
     86
     87
     88/**
     89 * Set stored media geometry - BIOS property.
     90 *
     91 * @see PDMIMEDIA::pfnBiosSetGeometry for details.
     92 */
     93static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
     94{
     95    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     96    int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
     97    LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
     98    return rc;
     99}
     100
     101
     102/**
     103 * Read bits.
     104 *
     105 * @see PDMIMEDIA::pfnRead for details.
     106 */
     107static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
     108{
     109    LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
     110    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     111    int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
     112    if (VBOX_SUCCESS(rc))
     113        Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
     114              "%.*Vhxd\n",
     115              off, pvBuf, cbRead, cbRead, pvBuf));
     116    LogFlow(("vdiRead: returns %Vrc\n", rc));
     117    return rc;
     118}
     119
     120
     121/**
     122 * Write bits.
     123 *
     124 * @see PDMIMEDIA::pfnWrite for details.
     125 */
     126static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
     127{
     128    LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
     129    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     130    Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
     131          "%.*Vhxd\n",
     132          off, pvBuf, cbWrite, cbWrite, pvBuf));
     133    int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
     134    LogFlow(("vdiWrite: returns %Vrc\n", rc));
     135    return rc;
     136}
     137
     138
     139/**
     140 * Flush bits to media.
     141 *
     142 * @see PDMIMEDIA::pfnFlush for details.
     143 */
     144static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
     145{
     146    LogFlow(("vdiFlush:\n"));
     147    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     148    vdiFlushImage(pData->pLast);
     149    int rc = VINF_SUCCESS;
     150    LogFlow(("vdiFlush: returns %Vrc\n", rc));
     151    return rc;
     152}
     153
     154
     155/** @copydoc PDMIMEDIA::pfnGetUuid */
     156static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
     157{
     158    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     159    int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
     160    LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
     161    return rc;
     162}
     163
     164
     165/** @copydoc PDMIMEDIA::pfnIsReadOnly */
     166static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
     167{
     168    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     169    LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
     170    return VDIDiskIsReadOnly(pData);
     171}
     172
     173
     174/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
     175static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface,
     176                                               PPDMBIOSTRANSLATION penmTranslation)
     177{
     178    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     179    int rc = VDIDiskGetTranslation(pData, penmTranslation);
     180    LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
     181    return rc;
     182}
     183
     184
     185/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
     186static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface,
     187                                               PDMBIOSTRANSLATION enmTranslation)
     188{
     189    PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
     190    int rc = VDIDiskSetTranslation(pData, enmTranslation);
     191    LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
     192    return rc;
     193}
     194
     195
     196/**
     197 * Queries an interface to the driver.
     198 *
     199 * @returns Pointer to interface.
     200 * @returns NULL if the interface was not supported by the driver.
     201 * @param   pInterface          Pointer to this interface structure.
     202 * @param   enmInterface        The requested interface identification.
     203 * @thread  Any thread.
     204 */
     205static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
     206{
     207    PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
     208    PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
     209    switch (enmInterface)
     210    {
     211        case PDMINTERFACE_BASE:
     212            return &pDrvIns->IBase;
     213        case PDMINTERFACE_MEDIA:
     214            return &pData->IMedia;
     215        default:
     216            return NULL;
     217    }
     218}
     219
     220
     221/**
     222 * Before the VM resumes we'll have to undo the read-only mode change
     223 * done in vdiSuspend.
     224 *
     225 * @param   pDrvIns     The driver instance data.
     226 */
     227static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
     228{
     229    LogFlow(("vdiSuspend:\n"));
     230#if 1 //#ifdef DEBUG_dmik
     231    PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
     232    if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
     233    {
     234        int rc = vdiChangeImageMode(pData->pLast, false);
     235        AssertRC(rc);
     236    }
    552237#endif
    553 static void vdiFlushImage(PVDIIMAGEDESC pImage);
    554 static void vdiCloseImage(PVDIIMAGEDESC pImage);
    555 static int  vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    556                            unsigned cbToRead, void *pvBuf);
    557 static int  vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    558 static int  vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock,
    559                             unsigned offWrite, unsigned cbToWrite, const void *pvBuf);
    560 static int  vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    561 static int  vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    562                           PFNVMPROGRESS pfnProgress, void *pvUser);
    563 static void vdiInitVDIDisk(PVDIDISK pDisk);
    564 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    565 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    566 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
    567 static int  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
    568 static int  vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
    569 
    570 static int  vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    571                             PFNVMPROGRESS pfnProgress, void *pvUser);
    572 static void vdiDumpImage(PVDIIMAGEDESC pImage);
    573 
    574 static DECLCALLBACK(int)  vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
    575 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns);
    576 static DECLCALLBACK(int)  vdiRead(PPDMIMEDIA pInterface,
    577                                   uint64_t off, void *pvBuf, size_t cbRead);
    578 static DECLCALLBACK(int)  vdiWrite(PPDMIMEDIA pInterface,
    579                                    uint64_t off, const void *pvBuf, size_t cbWrite);
    580 static DECLCALLBACK(int)  vdiFlush(PPDMIMEDIA pInterface);
    581 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface);
    582 static DECLCALLBACK(int)  vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
    583                                              uint32_t *pcHeads, uint32_t *pcSectors);
    584 static DECLCALLBACK(int)  vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
    585                                              uint32_t cHeads, uint32_t cSectors);
    586 static DECLCALLBACK(int)  vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
    587 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface);
    588 static DECLCALLBACK(int)  vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    589                                                 PPDMBIOSTRANSLATION penmTranslation);
    590 static DECLCALLBACK(int)  vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    591                                                 PDMBIOSTRANSLATION enmTranslation);
    592 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
    593 
    594 
    595 /**
    596  * internal: return power of 2 or 0 if num error.
    597  */
    598 static unsigned getPowerOfTwo(unsigned uNumber)
    599 {
    600     if (uNumber == 0)
    601         return 0;
    602     unsigned uPower2 = 0;
    603     while ((uNumber & 1) == 0)
    604     {
    605         uNumber >>= 1;
    606         uPower2++;
    607     }
    608     return uNumber == 1 ? uPower2 : 0;
    609 }
    610 
    611 /**
    612  * internal: init HDD preheader.
    613  */
    614 static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
    615 {
    616     pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
    617     pPreHdr->u32Version = VDI_IMAGE_VERSION;
    618     memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
    619     strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
    620 }
    621 
    622 /**
    623  * internal: check HDD preheader.
    624  */
    625 static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
    626 {
    627     if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
    628         return VERR_VDI_INVALID_SIGNATURE;
    629 
    630     if (    pPreHdr->u32Version != VDI_IMAGE_VERSION
    631         &&  pPreHdr->u32Version != 0x00000002)    /* old version. */
    632         return VERR_VDI_UNSUPPORTED_VERSION;
    633 
    634     return VINF_SUCCESS;
    635 }
    636 
    637 /**
    638  * internal: init HDD header. Always use latest header version.
    639  * @param   pHeader     Assumes it was initially initialized to all zeros.
    640  */
    641 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
    642                           const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    643                           uint32_t cbBlockExtra)
    644 {
    645     pHeader->uVersion = VDI_IMAGE_VERSION;
    646     pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
    647     pHeader->u.v1.u32Type = (uint32_t)enmType;
    648     pHeader->u.v1.fFlags = fFlags;
    649 #ifdef VBOX_STRICT
    650     char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
    651     Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
     238}
     239
     240
     241/**
     242 * When the VM has been suspended we'll change the image mode to read-only
     243 * so that main and others can read the VDIs. This is important when
     244 * saving state and so forth.
     245 *
     246 * @param   pDrvIns     The driver instance data.
     247 */
     248static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
     249{
     250    LogFlow(("vdiSuspend:\n"));
     251#if 1 // #ifdef DEBUG_dmik
     252    PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
     253    if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
     254    {
     255        int rc = vdiChangeImageMode(pData->pLast, true);
     256        AssertRC(rc);
     257    }
    652258#endif
    653     pHeader->u.v1.szComment[0] = '\0';
    654     if (pszComment)
    655     {
    656         AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
    657                   ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
    658         strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
    659     }
    660 
    661     /* Mark the geometry not-calculated. */
    662     pHeader->u.v1.Geometry.cCylinders = 0;
    663     pHeader->u.v1.Geometry.cHeads = 0;
    664     pHeader->u.v1.Geometry.cSectors = 0;
    665     pHeader->u.v1.Geometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    666     pHeader->u.v1.u32Translation = PDMBIOSTRANSLATION_AUTO;
    667 
    668     pHeader->u.v1.cbDisk = cbDisk;
    669     pHeader->u.v1.cbBlock = cbBlock;
    670     pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
    671     if (cbDisk % cbBlock)
    672         pHeader->u.v1.cBlocks++;
    673     pHeader->u.v1.cbBlockExtra = cbBlockExtra;
    674     pHeader->u.v1.cBlocksAllocated = 0;
    675 
    676     /* Init offsets. */
    677     pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
    678     pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
    679 
    680     /* Init uuids. */
    681     RTUuidCreate(&pHeader->u.v1.uuidCreate);
    682     RTUuidClear(&pHeader->u.v1.uuidModify);
    683     RTUuidClear(&pHeader->u.v1.uuidLinkage);
    684     RTUuidClear(&pHeader->u.v1.uuidParentModify);
    685 }
    686 
    687 /**
    688  * internal: check HDD header.
    689  */
    690 static int vdiValidateHeader(PVDIHEADER pHeader)
    691 {
    692     /* Check verion-dependend header parameters. */
    693     switch (GET_MAJOR_HEADER_VERSION(pHeader))
    694     {
    695         case 0:
    696         {
    697             /* Old header version. */
    698             break;
    699         }
    700         case 1:
    701         {
    702             /* Current header version. */
    703 
    704             if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
    705             {
    706                 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
    707                        pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
    708                 return VERR_VDI_INVALID_HEADER;
    709             }
    710 
    711             if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
    712             {
    713                 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
    714                        getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
    715                 return VERR_VDI_INVALID_HEADER;
    716             }
    717 
    718             if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
    719             {
    720                 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
    721                        getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
    722                 return VERR_VDI_INVALID_HEADER;
    723             }
    724 
    725             if (    getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
    726                 ||  getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
    727             {
    728                 if (RTUuidIsNull(getImageParentUUID(pHeader)))
    729                 {
    730                     LogRel(("VDI: v1 uuid of parent is 0)\n"));
    731                     return VERR_VDI_INVALID_HEADER;
    732                 }
    733                 if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
    734                 {
    735                     LogRel(("VDI: v1 uuid of parent modification is 0\n"));
    736                     return VERR_VDI_INVALID_HEADER;
    737                 }
    738             }
    739 
    740             break;
    741         }
    742         default:
    743             /* Unsupported. */
    744             return VERR_VDI_UNSUPPORTED_VERSION;
    745     }
    746 
    747     /* Check common header parameters. */
    748 
    749     bool fFailed = false;
    750 
    751     if (    getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
    752         ||  getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
    753     {
    754         LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
    755         fFailed = true;
    756     }
    757 
    758     if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
    759     {
    760         LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
    761         fFailed = true;
    762     }
    763 
    764     if ((getImageGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
    765     {
    766         LogRel(("VDI: wrong sector size (%d != %d)\n",
    767                (getImageGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
    768         fFailed = true;
    769     }
    770 
    771     if (    getImageDiskSize(pHeader) == 0
    772         ||  getImageBlockSize(pHeader) == 0
    773         ||  getImageBlocks(pHeader) == 0
    774         ||  getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
    775     {
    776         LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
    777               getImageDiskSize(pHeader), getImageBlockSize(pHeader),
    778               getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
    779         fFailed = true;
    780     }
    781 
    782     if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
    783     {
    784         LogRel(("VDI: too many blocks allocated (%d > %d)\n"
    785                 "     blocksize=%d disksize=%lld\n",
    786               getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
    787               getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
    788         fFailed = true;
    789     }
    790 
    791     if (    getImageExtraBlockSize(pHeader) != 0
    792         &&  getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
    793     {
    794         LogRel(("VDI: wrong extra size (%d, %d)\n",
    795                getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
    796         fFailed = true;
    797     }
    798 
    799     if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
    800     {
    801         LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
    802                getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
    803         fFailed = true;
    804     }
    805 
    806     if (RTUuidIsNull(getImageCreationUUID(pHeader)))
    807     {
    808         LogRel(("VDI: uuid of creator is 0\n"));
    809         fFailed = true;
    810     }
    811 
    812     if (RTUuidIsNull(getImageModificationUUID(pHeader)))
    813     {
    814         LogRel(("VDI: uuid of modificator is 0\n"));
    815         fFailed = true;
    816     }
    817 
    818     return fFailed ? VERR_VDI_INVALID_HEADER : VINF_SUCCESS;
    819 }
    820 
    821 /**
    822  * internal: init VDIIMAGEDESC structure.
    823  */
    824 static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
    825 {
    826     pImage->pPrev = NULL;
    827     pImage->pNext = NULL;
    828     pImage->File = NIL_RTFILE;
    829     pImage->paBlocks = NULL;
    830 }
    831 
    832 /**
    833  * internal: setup VDIIMAGEDESC structure by image header.
    834  */
    835 static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
    836 {
    837     pImage->fFlags             = getImageFlags(&pImage->Header);
    838     pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
    839     pImage->offStartData       = getImageDataOffset(&pImage->Header);
    840     pImage->uBlockMask         = getImageBlockSize(&pImage->Header) - 1;
    841     pImage->uShiftIndex2Offset =
    842     pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
    843     pImage->offStartBlockData  = getImageExtraBlockSize(&pImage->Header);
    844     if (pImage->offStartBlockData != 0)
    845         pImage->uShiftIndex2Offset += getPowerOfTwo(pImage->offStartBlockData);
    846 }
    847 
    848 /**
    849  * internal: create image.
    850  */
    851 static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    852                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    853                           PFNVMPROGRESS pfnProgress, void *pvUser)
    854 {
    855     /* Check args. */
    856     Assert(pszFilename);
    857     Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
    858     Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
    859     Assert(cbSize);
    860 
    861     /* Special check for comment length. */
    862     if (    pszComment
    863         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    864     {
    865         Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    866         return VERR_VDI_COMMENT_TOO_LONG;
    867     }
    868 
    869     if (    enmType == VDI_IMAGE_TYPE_UNDO
    870         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    871     {
    872         Assert(pParent);
    873         if ((pParent->PreHeader.u32Version >> 16) != VDI_IMAGE_VERSION_MAJOR)
    874         {
    875             /* Invalid parent image version. */
    876             Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
    877             return VERR_VDI_UNSUPPORTED_VERSION;
    878         }
    879 
    880         /* get image params from the parent image. */
    881         fFlags = getImageFlags(&pParent->Header);
    882         cbSize = getImageDiskSize(&pParent->Header);
    883     }
    884 
    885     PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    886     if (!pImage)
    887         return VERR_NO_MEMORY;
    888     vdiInitImageDesc(pImage);
    889 
    890     vdiInitPreHeader(&pImage->PreHeader);
    891     vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
    892 
    893     if (    enmType == VDI_IMAGE_TYPE_UNDO
    894         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    895     {
    896         /* Set up linkage information. */
    897         pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
    898         pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
    899     }
    900 
    901     pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    902     if (!pImage->paBlocks)
    903     {
    904         RTMemFree(pImage);
    905         return VERR_NO_MEMORY;
    906     }
    907 
    908     if (enmType != VDI_IMAGE_TYPE_FIXED)
    909     {
    910         /* for growing images mark all blocks in paBlocks as free. */
    911         for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
    912             pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
    913     }
    914     else
    915     {
    916         /* for fixed images mark all blocks in paBlocks as allocated */
    917         for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
    918             pImage->paBlocks[i] = i;
    919         pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
    920     }
    921 
    922     /* Setup image parameters. */
    923     vdiSetupImageDesc(pImage);
    924 
    925     /* create file */
    926     int rc = RTFileOpen(&pImage->File,
    927                         pszFilename,
    928                         RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    929     if (VBOX_SUCCESS(rc))
    930     {
    931         /* Lock image exclusively to close any wrong access by VDI API calls. */
    932         uint64_t cbLock = pImage->offStartData
    933                         + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    934         rc = RTFileLock(pImage->File,
    935                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    936         if (VBOX_FAILURE(rc))
    937         {
    938             cbLock = 0;    /* Not locked. */
    939             goto l_create_failed;
    940         }
    941 
    942         if (enmType == VDI_IMAGE_TYPE_FIXED)
    943         {
    944             /*
    945              * Allocate & commit whole file if fixed image, it must be more
    946              * effective than expanding file by write operations.
    947              */
    948             rc = RTFileSetSize(pImage->File,
    949                                pImage->offStartData
    950                              + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
    951         }
    952         else
    953         {
    954             /* Set file size to hold header and blocks array. */
    955             rc = RTFileSetSize(pImage->File, pImage->offStartData);
    956         }
    957         if (VBOX_FAILURE(rc))
    958             goto l_create_failed;
    959 
    960         /* Generate image last-modify uuid */
    961         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    962 
    963         /* Write pre-header. */
    964         rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    965         if (VBOX_FAILURE(rc))
    966             goto l_create_failed;
    967 
    968         /* Write header. */
    969         rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    970         if (VBOX_FAILURE(rc))
    971             goto l_create_failed;
    972 
    973         /* Write blocks array. */
    974         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    975         if (VBOX_FAILURE(rc))
    976             goto l_create_failed;
    977         rc = RTFileWrite(pImage->File,
    978                          pImage->paBlocks,
    979                          getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
    980                          NULL);
    981         if (VBOX_FAILURE(rc))
    982             goto l_create_failed;
    983 
    984         if (    (enmType == VDI_IMAGE_TYPE_FIXED)
    985             &&  (fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND))
    986         {
    987             /* Fill image with zeroes. */
    988 
    989             rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
    990             if (VBOX_FAILURE(rc))
    991                 goto l_create_failed;
    992 
    993             /* alloc tmp zero-filled buffer */
    994             void *pvBuf = RTMemTmpAllocZ(VDIDISK_DEFAULT_BUFFER_SIZE);
    995             if (pvBuf)
    996             {
    997                 uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
    998                 uint64_t cbDisk = cbFill;
    999 
    1000                 /* do loop to fill all image. */
    1001                 while (cbFill > 0)
    1002                 {
    1003                     unsigned to_fill = (unsigned)RT_MIN(cbFill, VDIDISK_DEFAULT_BUFFER_SIZE);
    1004 
    1005                     rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    1006                     if (VBOX_FAILURE(rc))
    1007                         break;
    1008 
    1009                     cbFill -= to_fill;
    1010 
    1011                     if (pfnProgress)
    1012                     {
    1013                         rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1014                                          (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
    1015                                          pvUser);
    1016                         if (VBOX_FAILURE(rc))
    1017                             break;
    1018                     }
    1019                 }
    1020                 RTMemTmpFree(pvBuf);
    1021             }
    1022             else
    1023             {
    1024                 /* alloc error */
    1025                 rc = VERR_NO_MEMORY;
    1026             }
    1027         }
    1028 
    1029     l_create_failed:
    1030 
    1031         if (cbLock)
    1032             RTFileUnlock(pImage->File, 0, cbLock);
    1033 
    1034         RTFileClose(pImage->File);
    1035 
    1036         /* Delete image file if error occured while creating */
    1037         if (VBOX_FAILURE(rc))
    1038             RTFileDelete(pszFilename);
    1039     }
    1040 
    1041     RTMemFree(pImage->paBlocks);
    1042     RTMemFree(pImage);
    1043 
    1044     if (    VBOX_SUCCESS(rc)
    1045         &&  pfnProgress)
    1046         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    1047 
    1048     Log(("vdiCreateImage: done, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
    1049 
    1050     return rc;
    1051 }
    1052 
    1053 /**
    1054  * Open an image.
    1055  * @internal
    1056  */
    1057 static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename,
    1058                         unsigned fOpen, PVDIIMAGEDESC pParent)
    1059 {
    1060     /*
    1061      * Validate input.
    1062      */
    1063     Assert(ppImage);
    1064     Assert(pszFilename);
    1065     Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
    1066 
    1067     PVDIIMAGEDESC   pImage;
    1068     size_t          cchFilename = strlen(pszFilename);
    1069     if (cchFilename >= sizeof(pImage->szFilename))
    1070     {
    1071         AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
    1072         return VERR_FILENAME_TOO_LONG;
    1073     }
    1074 
    1075     pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    1076     if (!pImage)
    1077         return VERR_NO_MEMORY;
    1078     vdiInitImageDesc(pImage);
    1079 
    1080     memcpy(pImage->szFilename, pszFilename, cchFilename);
    1081     pImage->fOpen = fOpen;
    1082 
    1083     /*
    1084      * Open the image.
    1085      */
    1086     int rc = RTFileOpen(&pImage->File,
    1087                         pImage->szFilename,
    1088                         fOpen & VDI_OPEN_FLAGS_READONLY
    1089                         ? RTFILE_O_READ      | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
    1090                         : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    1091     if (VBOX_FAILURE(rc))
    1092     {
    1093         if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
    1094         {
    1095             /* Try to open image for reading only. */
    1096             rc = RTFileOpen(&pImage->File,
    1097                             pImage->szFilename,
    1098                             RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    1099             if (VBOX_SUCCESS(rc))
    1100                 pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
    1101         }
    1102         if (VBOX_FAILURE(rc))
    1103         {
    1104             RTMemFree(pImage);
    1105             return rc;
    1106         }
    1107     }
    1108     /* Set up current image r/w state. */
    1109     pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
    1110 
    1111     /*
    1112      * Set initial file lock for reading header only.
    1113      * Length of lock doesn't matter, it just must include image header.
    1114      */
    1115     uint64_t cbLock = _1M;
    1116     rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    1117     if (VBOX_FAILURE(rc))
    1118     {
    1119         cbLock = 0;
    1120         goto l_open_failed;
    1121     }
    1122 
    1123     /* Read pre-header. */
    1124     rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    1125     if (VBOX_FAILURE(rc))
    1126         goto l_open_failed;
    1127     rc = vdiValidatePreHeader(&pImage->PreHeader);
    1128     if (VBOX_FAILURE(rc))
    1129         goto l_open_failed;
    1130 
    1131     /* Read header. */
    1132     pImage->Header.uVersion = pImage->PreHeader.u32Version;
    1133     switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    1134     {
    1135         case 0:
    1136             rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
    1137             break;
    1138         case 1:
    1139             rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    1140             break;
    1141         default:
    1142             rc = VERR_VDI_UNSUPPORTED_VERSION;
    1143             break;
    1144     }
    1145     if (VBOX_FAILURE(rc))
    1146         goto l_open_failed;
    1147 
    1148     rc = vdiValidateHeader(&pImage->Header);
    1149     if (VBOX_FAILURE(rc))
    1150         goto l_open_failed;
    1151 
    1152     /* Check diff image correctness. */
    1153     if (pParent)
    1154     {
    1155         if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
    1156         {
    1157             rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
    1158             goto l_open_failed;
    1159         }
    1160 
    1161         if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
    1162             &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
    1163         {
    1164             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    1165             goto l_open_failed;
    1166         }
    1167 
    1168         if (    getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
    1169             ||  getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
    1170             ||  getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
    1171             ||  getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
    1172         {
    1173             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    1174             goto l_open_failed;
    1175         }
    1176 
    1177         /* Check linkage data. */
    1178         if (    RTUuidCompare(getImageParentUUID(&pImage->Header),
    1179                               getImageCreationUUID(&pParent->Header))
    1180             ||  RTUuidCompare(getImageParentModificationUUID(&pImage->Header),
    1181                               getImageModificationUUID(&pParent->Header)))
    1182         {
    1183             rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    1184             goto l_open_failed;
    1185         }
    1186     }
    1187 
    1188     /* Setup image parameters by header. */
    1189     vdiSetupImageDesc(pImage);
    1190 
    1191     /* reset modified flag into first-modified state. */
    1192     pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
    1193 
    1194     /* Image is validated, set working file lock on it. */
    1195     rc = RTFileUnlock(pImage->File, 0, cbLock);
    1196     AssertRC(rc);
    1197     cbLock = pImage->offStartData
    1198            + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    1199     rc = RTFileLock(pImage->File,
    1200                     (pImage->fReadOnly) ?
    1201                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    1202                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    1203                     0,
    1204                     cbLock);
    1205     if (    VBOX_FAILURE(rc)
    1206         &&  !pImage->fReadOnly)
    1207     {
    1208         /* Failed to lock image for writing, try read-only lock. */
    1209         rc = RTFileLock(pImage->File,
    1210                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    1211         if (VBOX_SUCCESS(rc))
    1212             pImage->fReadOnly = true;
    1213     }
    1214     if (VBOX_FAILURE(rc))
    1215     {
    1216         cbLock = 0;    /* Not locked. */
    1217         goto l_open_failed;
    1218     }
    1219 
    1220     /* Allocate memory for blocks array. */
    1221     pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    1222     if (!pImage->paBlocks)
    1223     {
    1224         rc = VERR_NO_MEMORY;
    1225         goto l_open_failed;
    1226     }
    1227 
    1228     /* Read blocks array. */
    1229     rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    1230     if (VBOX_FAILURE(rc))
    1231         goto l_open_failed;
    1232     rc = RTFileRead(pImage->File, pImage->paBlocks,
    1233                     getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), NULL);
    1234     if (VBOX_FAILURE(rc))
    1235         goto l_open_failed;
    1236 
    1237     /* all done. */
    1238     *ppImage = pImage;
    1239     return VINF_SUCCESS;
    1240 
    1241 l_open_failed:
    1242     /* Clean up. */
    1243     if (pImage->paBlocks)
    1244         RTMemFree(pImage->paBlocks);
    1245     if (cbLock)
    1246         RTFileUnlock(pImage->File, 0, cbLock);
    1247     RTFileClose(pImage->File);
    1248     RTMemFree(pImage);
    1249     Log(("vdiOpenImage: failed, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
    1250     return rc;
    1251 }
    1252 
    1253 /**
    1254  * internal: save header to file.
    1255  */
    1256 static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
    1257 {
    1258     /* Seek to header start. */
    1259     int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
    1260     if (VBOX_SUCCESS(rc))
    1261     {
    1262         switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    1263         {
    1264             case 0:
    1265                 rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
    1266                 break;
    1267             case 1:
    1268                 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    1269                 break;
    1270             default:
    1271                 rc = VERR_VDI_UNSUPPORTED_VERSION;
    1272                 break;
    1273         }
    1274     }
    1275     AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->szFilename, rc));
    1276     return rc;
    1277 }
    1278 
    1279 /**
    1280  * internal: save block pointer to file, save header to file.
    1281  */
    1282 static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
    1283 {
    1284     /* Update image header. */
    1285     int rc = vdiUpdateHeader(pImage);
    1286     if (VBOX_SUCCESS(rc))
    1287     {
    1288         /* write only one block pointer. */
    1289         rc = RTFileSeek(pImage->File,
    1290                         pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
    1291                         RTFILE_SEEK_BEGIN,
    1292                         NULL);
    1293         if (VBOX_SUCCESS(rc))
    1294             rc = RTFileWrite(pImage->File,
    1295                              &pImage->paBlocks[uBlock],
    1296                              sizeof(VDIIMAGEBLOCKPOINTER),
    1297                              NULL);
    1298         AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
    1299                          uBlock, pImage->szFilename, rc));
    1300     }
    1301     return rc;
    1302 }
    1303 
    1304 /**
    1305  * internal: save blocks array to file, save header to file.
    1306  */
    1307 static int vdiUpdateBlocks(PVDIIMAGEDESC pImage)
    1308 {
    1309     /* Update image header. */
    1310     int rc = vdiUpdateHeader(pImage);
    1311     if (VBOX_SUCCESS(rc))
    1312     {
    1313         /* write the block pointers array. */
    1314         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    1315         if (VBOX_SUCCESS(rc))
    1316             rc = RTFileWrite(pImage->File,
    1317                              pImage->paBlocks,
    1318                              sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header),
    1319                              NULL);
    1320         AssertMsgRC(rc, ("vdiUpdateBlocks failed, filename=\"%s\", rc=%Vrc\n",
    1321                          pImage->szFilename, rc));
    1322     }
    1323     return rc;
    1324 }
    1325 
    1326 /**
    1327  * internal: mark image as modified, if this is the first change - update image header
    1328  * on disk with a new uuidModify value.
    1329  */
    1330 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
    1331 {
    1332     pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
    1333     if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
    1334     {
    1335         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
    1336 
    1337         /* first modify - generate uuidModify and save to file. */
    1338         vdiResetModifiedFlag(pImage);
    1339 
    1340         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    1341         {
    1342             /* save header to file,
    1343              * note: no rc checking.
    1344              */
    1345             vdiUpdateHeader(pImage);
    1346         }
    1347     }
    1348 }
    1349 
    1350 /**
    1351  * internal: generate new uuidModify if the image was changed.
    1352  */
    1353 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
    1354 {
    1355     if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
    1356     {
    1357         /* generate new last-modified uuid */
    1358         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    1359             RTUuidCreate(getImageModificationUUID(&pImage->Header));
    1360 
    1361         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
    1362     }
    1363 }
    1364 
    1365 /**
    1366  * internal: disables updates of the last-modified UUID
    1367  * when performing image writes.
    1368  */
    1369 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    1370 {
    1371     pImage->fModified |= VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    1372 }
    1373 
    1374 #if 0 /* unused */
    1375 /**
    1376  * internal: enables updates of the last-modified UUID
    1377  * when performing image writes.
    1378  */
    1379 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    1380 {
    1381     pImage->fModified &= ~VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    1382 }
    1383 #endif
    1384 
    1385 /**
    1386  * internal: flush image file to disk.
    1387  */
    1388 static void vdiFlushImage(PVDIIMAGEDESC pImage)
    1389 {
    1390     if (!pImage->fReadOnly)
    1391     {
    1392         /* Update last-modified uuid if need. */
    1393         vdiResetModifiedFlag(pImage);
    1394 
    1395         /* Save header. */
    1396         int rc = vdiUpdateHeader(pImage);
    1397         AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    1398                          pImage->szFilename, rc));
    1399         RTFileFlush(pImage->File);
    1400     }
    1401 }
    1402 
    1403 /**
    1404  * internal: close image file.
    1405  */
    1406 static void vdiCloseImage(PVDIIMAGEDESC pImage)
    1407 {
    1408     /* Params checking. */
    1409     Assert(pImage);
    1410     Assert(pImage->File != NIL_RTFILE);
    1411 
    1412     vdiFlushImage(pImage);
    1413     RTFileUnlock(pImage->File,
    1414                  0,
    1415                  pImage->offStartData
    1416                + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
    1417     RTFileClose(pImage->File);
    1418 
    1419     /* free image resources */
    1420     RTMemFree(pImage->paBlocks);
    1421     RTMemFree(pImage);
    1422 }
    1423 
    1424 /**
    1425  * internal: read data inside image block.
    1426  *
    1427  * note: uBlock must be valid, readed data must not overlap block bounds.
    1428  */
    1429 static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    1430                           unsigned cbToRead, void *pvBuf)
    1431 {
    1432     if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    1433     {
    1434         /* block present in image file */
    1435         uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1436                            + (pImage->offStartData + pImage->offStartBlockData + offRead);
    1437         int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1438         if (VBOX_SUCCESS(rc))
    1439             rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
    1440         if (VBOX_FAILURE(rc))
    1441             Log(("vdiReadInBlock: rc=%Vrc filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
    1442                  rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
    1443         return rc;
    1444     }
    1445 
    1446     /* Returns zeroes for both free and zero block types. */
    1447     memset(pvBuf, 0, cbToRead);
    1448     return VINF_SUCCESS;
    1449 }
    1450 
    1451 /**
    1452  * Read data from virtual HDD.
    1453  *
    1454  * @returns VBox status code.
    1455  * @param   pDisk           Pointer to VDI HDD container.
    1456  * @param   offStart        Offset of first reading byte from start of disk.
    1457  * @param   pvBuf           Pointer to buffer for reading data.
    1458  * @param   cbToRead        Number of bytes to read.
    1459  */
    1460 IDER3DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, unsigned cbToRead)
    1461 {
    1462     /* sanity check */
    1463     Assert(pDisk);
    1464     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1465 
    1466     PVDIIMAGEDESC pImage = pDisk->pLast;
    1467     Assert(pImage);
    1468 
    1469     /* Check params. */
    1470     if (    offStart + cbToRead > getImageDiskSize(&pImage->Header)
    1471         ||  cbToRead == 0)
    1472     {
    1473         AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
    1474         return VERR_INVALID_PARAMETER;
    1475     }
    1476 
    1477     /* Calculate starting block number and offset inside it. */
    1478     unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1479     unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
    1480 
    1481     /* Save block size here for speed optimization. */
    1482     unsigned cbBlock = getImageBlockSize(&pImage->Header);
    1483 
    1484     /* loop through blocks */
    1485     int rc;
    1486     for (;;)
    1487     {
    1488         unsigned to_read;
    1489         if ((offRead + cbToRead) <= cbBlock)
    1490             to_read = cbToRead;
    1491         else
    1492             to_read = cbBlock - offRead;
    1493 
    1494         if (pDisk->cImages > 1)
    1495         {
    1496             /* Differencing images are used, handle them. */
    1497             pImage = pDisk->pLast;
    1498 
    1499             /* Search for image with allocated block. */
    1500             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1501             {
    1502                 pImage = pImage->pPrev;
    1503                 if (!pImage)
    1504                 {
    1505                     /* Block is not allocated in all images of chain. */
    1506                     pImage = pDisk->pLast;
    1507                     break;
    1508                 }
    1509             }
    1510         }
    1511 
    1512         rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
    1513 
    1514         cbToRead -= to_read;
    1515         if (    cbToRead == 0
    1516             ||  VBOX_FAILURE(rc))
    1517             break;
    1518 
    1519         /* goto next block */
    1520         uBlock++;
    1521         offRead = 0;
    1522         pvBuf = (char *)pvBuf + to_read;
    1523     }
    1524 
    1525     return rc;
    1526 }
    1527 
    1528 /**
    1529  * internal: fill the whole block with zeroes.
    1530  *
    1531  * note: block id must be valid, block must be already allocated in file.
    1532  * note: if pDisk is NULL, the default buffer size is used
    1533  */
    1534 static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1535 {
    1536     int rc;
    1537 
    1538     /* seek to start of block in file. */
    1539     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1540                        + (pImage->offStartData + pImage->offStartBlockData);
    1541     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1542     if (VBOX_FAILURE(rc))
    1543     {
    1544         Log(("vdiFillBlockByZeroes: seek rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu\n",
    1545              rc, pImage->szFilename, uBlock, u64Offset));
    1546         return rc;
    1547     }
    1548 
    1549     /* alloc tmp zero-filled buffer */
    1550     void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1551     if (!pvBuf)
    1552         return VERR_NO_MEMORY;
    1553 
    1554     unsigned cbFill = getImageBlockSize(&pImage->Header);
    1555 
    1556     /* do loop, because buffer size may be less then block size */
    1557     while (cbFill > 0)
    1558     {
    1559         unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1560         rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    1561         if (VBOX_FAILURE(rc))
    1562         {
    1563             Log(("vdiFillBlockByZeroes: write rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
    1564                  rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
    1565             break;
    1566         }
    1567 
    1568         cbFill -= to_fill;
    1569     }
    1570 
    1571     RTMemTmpFree(pvBuf);
    1572     return rc;
    1573 }
    1574 
    1575 /**
    1576  * internal: write data inside image block.
    1577  *
    1578  * note: uBlock must be valid, written data must not overlap block bounds.
    1579  */
    1580 static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, unsigned cbToWrite, const void *pvBuf)
    1581 {
    1582     int rc;
    1583 
    1584     /* Check if we can write into file. */
    1585     if (pImage->fReadOnly)
    1586     {
    1587         Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
    1588         return VERR_WRITE_PROTECT;
    1589     }
    1590 
    1591     /* This could be optimized a little (not setting it when writing zeroes
    1592      * to a zeroed block). Won't buy us much, because it's very unlikely
    1593      * that only such zero data block writes occur while the VDI is opened. */
    1594     vdiSetModifiedFlag(pImage);
    1595 
    1596     if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    1597     {
    1598         if (!pDisk || !pDisk->fHonorZeroWrites)
    1599         {
    1600             /* If the destination block is unallocated at this point, it's either
    1601              * a zero block or a block which hasn't been used so far (which also
    1602              * means that it's a zero block. Don't need to write anything to this
    1603              * block if the data consists of just zeroes. */
    1604             Assert(cbToWrite % 4 == 0);
    1605             if (ASMBitFirstSet((volatile void *)pvBuf, cbToWrite * 8) == -1)
    1606             {
    1607                 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1608                 return VINF_SUCCESS;
    1609             }
    1610         }
    1611 
    1612         /* need to allocate a new block in image file */
    1613 
    1614         /* expand file by one block */
    1615         uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
    1616                          + pImage->offStartData;
    1617         rc = RTFileSetSize(pImage->File, u64Size);
    1618         if (VBOX_FAILURE(rc))
    1619         {
    1620             Log(("vdiWriteInBlock: set size rc=%Vrc filename=\"%s\" uBlock=%u u64Size=%llu\n",
    1621                  rc, pImage->szFilename, uBlock, u64Size));
    1622             return rc;
    1623         }
    1624 
    1625         unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    1626         pImage->paBlocks[uBlock] = cBlocksAllocated;
    1627         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
    1628 
    1629         if (    pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
    1630             ||  pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1631         {
    1632             /* Fill newly allocated block by zeroes. */
    1633 
    1634             if (offWrite || cbToWrite != getImageBlockSize(&pImage->Header))
    1635             {
    1636                 rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
    1637                 if (VBOX_FAILURE(rc))
    1638                     return rc;
    1639             }
    1640         }
    1641 
    1642         rc = vdiUpdateBlockInfo(pImage, uBlock);
    1643         if (VBOX_FAILURE(rc))
    1644             return rc;
    1645     }
    1646 
    1647     /* Now block present in image file, write data inside it. */
    1648     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1649                        + (pImage->offStartData + pImage->offStartBlockData + offWrite);
    1650     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1651     if (VBOX_SUCCESS(rc))
    1652     {
    1653         rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
    1654         if (VBOX_FAILURE(rc))
    1655             Log(("vdiWriteInBlock: write rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
    1656                  rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
    1657     }
    1658     else
    1659         Log(("vdiWriteInBlock: seek rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
    1660              rc, pImage->szFilename, uBlock, offWrite, u64Offset));
    1661 
    1662     return rc;
    1663 }
    1664 
    1665 /**
    1666  * internal: copy data block from one (parent) image to last image.
    1667  */
    1668 static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1669 {
    1670     Assert(pImage != pDisk->pLast);
    1671 
    1672     if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1673     {
    1674         /*
    1675          * if src block is zero, set dst block to zero too.
    1676          */
    1677         pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1678         return VINF_SUCCESS;
    1679     }
    1680 
    1681     /* alloc tmp buffer */
    1682     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1683     if (!pvBuf)
    1684         return VERR_NO_MEMORY;
    1685 
    1686     int rc = VINF_SUCCESS;
    1687 
    1688     unsigned cbCopy = getImageBlockSize(&pImage->Header);
    1689     unsigned offCopy = 0;
    1690 
    1691     /* do loop, because buffer size may be less then block size */
    1692     while (cbCopy > 0)
    1693     {
    1694         unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
    1695         rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
    1696         if (VBOX_FAILURE(rc))
    1697             break;
    1698 
    1699         rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
    1700         if (VBOX_FAILURE(rc))
    1701             break;
    1702 
    1703         cbCopy -= to_copy;
    1704         offCopy += to_copy;
    1705     }
    1706 
    1707     RTMemTmpFree(pvBuf);
    1708     return rc;
    1709 }
    1710 
    1711 /**
    1712  * Write data to virtual HDD.
    1713  *
    1714  * @returns VBox status code.
    1715  * @param   pDisk           Pointer to VDI HDD container.
    1716  * @param   offStart        Offset of first writing byte from start of HDD.
    1717  * @param   pvBuf           Pointer to buffer of writing data.
    1718  * @param   cbToWrite       Number of bytes to write.
    1719  */
    1720 IDER3DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, unsigned cbToWrite)
    1721 {
    1722     /* sanity check */
    1723     Assert(pDisk);
    1724     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1725 
    1726     PVDIIMAGEDESC pImage = pDisk->pLast;
    1727     Assert(pImage);
    1728 
    1729     /* Check params. */
    1730     if (    offStart + cbToWrite > getImageDiskSize(&pImage->Header)
    1731         ||  cbToWrite == 0)
    1732     {
    1733         AssertMsgFailed(("offStart=%llu cbToWrite=%u\n", offStart, cbToWrite));
    1734         return VERR_INVALID_PARAMETER;
    1735     }
    1736 
    1737     /* Calculate starting block number and offset inside it. */
    1738     unsigned uBlock   = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1739     unsigned offWrite = (unsigned)offStart   & pImage->uBlockMask;
    1740     unsigned cbBlock  = getImageBlockSize(&pImage->Header);
    1741 
    1742     /* loop through blocks */
    1743     int rc;
    1744     for (;;)
    1745     {
    1746         unsigned to_write;
    1747         if (offWrite + cbToWrite <= cbBlock)
    1748             to_write = cbToWrite;
    1749         else
    1750             to_write = cbBlock - offWrite;
    1751 
    1752         /* All callers write less than a VDI block right now (assuming
    1753          * default VDI block size). So not worth optimizing for the case
    1754          * where a full block is overwritten (no copying required).
    1755          * Checking whether a block is all zeroes after the write is too
    1756          * expensive (would require reading the rest of the block). */
    1757 
    1758         if (pDisk->cImages > 1)
    1759         {
    1760             /* Differencing images are used, handle them. */
    1761 
    1762             /* Search for image with allocated block. */
    1763             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1764             {
    1765                 pImage = pImage->pPrev;
    1766                 if (!pImage)
    1767                 {
    1768                     /* Block is not allocated in all images of chain. */
    1769                     pImage = pDisk->pLast;
    1770                     break;
    1771                 }
    1772             }
    1773 
    1774             if (pImage != pDisk->pLast)
    1775             {
    1776                 /* One of parent image has a block data, copy it into last image. */
    1777                 rc = vdiCopyBlock(pDisk, pImage, uBlock);
    1778                 if (VBOX_FAILURE(rc))
    1779                     break;
    1780                 pImage = pDisk->pLast;
    1781             }
    1782         }
    1783 
    1784         /* Actually write the data into block. */
    1785         rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
    1786 
    1787         cbToWrite -= to_write;
    1788         if (    cbToWrite == 0
    1789             || VBOX_FAILURE(rc))
    1790             break;
    1791 
    1792         /* goto next block */
    1793         uBlock++;
    1794         offWrite = 0;
    1795         pvBuf = (char *)pvBuf + to_write;
    1796     }
    1797 
    1798     return rc;
    1799 }
    1800 
    1801 /**
    1802  * internal: commit one image to another, no changes to header, just
    1803  * plain copy operation. Blocks that are not allocated in the source
    1804  * image (i.e. inherited by its parent(s)) are not merged.
    1805  *
    1806  * @param pImageFrom        source image
    1807  * @param pImageTo          target image (will receive all the modifications)
    1808  * @param fParentToChild    true if the source image is parent of the target one,
    1809  *                          false of the target image is the parent of the source.
    1810  * @param pfnProgress       progress callback (NULL if not to be used)
    1811  * @param pvUser            user argument for the progress callback
    1812  *
    1813  * @note the target image has to be opened read/write
    1814  * @note this method does not check whether merging is possible!
    1815  */
    1816 static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    1817                           PFNVMPROGRESS pfnProgress, void *pvUser)
    1818 {
    1819     Assert(pImageFrom);
    1820     Assert(pImageTo);
    1821 
    1822     Log(("vdiMergeImages: merging from image \"%s\" to image \"%s\" (fParentToChild=%d)\n",
    1823          pImageFrom->szFilename, pImageTo->szFilename, fParentToChild));
    1824 
    1825     /* alloc tmp buffer */
    1826     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    1827     if (!pvBuf)
    1828         return VERR_NO_MEMORY;
    1829 
    1830     int rc = VINF_SUCCESS;
    1831 
    1832     if (!fParentToChild)
    1833     {
    1834         /*
    1835          *  Commit the child image to the parent image.
    1836          *  Child is the source (from), parent is the target (to).
    1837          */
    1838 
    1839         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1840 
    1841         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1842         {
    1843             /* only process blocks that are allocated in the source image */
    1844             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE)
    1845             {
    1846                 /* Found used block in source image, commit it. */
    1847                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    1848                     &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pImageTo->paBlocks[uBlock]))
    1849                 {
    1850                     /* Block is zero in the source image and not allocated in the target image. */
    1851                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1852                     vdiSetModifiedFlag(pImageTo);
    1853                 }
    1854                 else
    1855                 {
    1856                     /* Block is not zero / allocated in source image. */
    1857                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1858                     unsigned offCommit = 0;
    1859 
    1860                     /* do loop, because buffer size may be less then block size */
    1861                     while (cbCommit > 0)
    1862                     {
    1863                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1864 
    1865                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1866                         if (VBOX_FAILURE(rc))
    1867                             break;
    1868 
    1869                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1870                         if (VBOX_FAILURE(rc))
    1871                             break;
    1872 
    1873                         cbCommit -= cbToCopy;
    1874                         offCommit += cbToCopy;
    1875                     }
    1876                     if (VBOX_FAILURE(rc))
    1877                         break;
    1878                 }
    1879             }
    1880 
    1881             if (pfnProgress)
    1882             {
    1883                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1884                             (uBlock * 100) / cBlocks,
    1885                             pvUser);
    1886                 /* Note: commiting is non breakable operation, skipping rc here. */
    1887             }
    1888         }
    1889     }
    1890     else
    1891     {
    1892         /*
    1893          *  Commit the parent image to the child image.
    1894          *  Parent is the source (from), child is the target (to).
    1895          */
    1896 
    1897         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1898 
    1899         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1900         {
    1901             /*
    1902              *  only process blocks that are allocated or zero in the source image
    1903              *  and NEITHER allocated NOR zero in the target image
    1904              */
    1905             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE &&
    1906                 pImageTo->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1907             {
    1908                 /* Found used block in source image (but unused in target), commit it. */
    1909                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1910                 {
    1911                     /* Block is zero in the source image and not allocated in the target image. */
    1912                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1913                     vdiSetModifiedFlag(pImageTo);
    1914                 }
    1915                 else
    1916                 {
    1917                     /* Block is not zero / allocated in source image. */
    1918                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1919                     unsigned offCommit = 0;
    1920 
    1921                     /* do loop, because buffer size may be less then block size */
    1922                     while (cbCommit > 0)
    1923                     {
    1924                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1925 
    1926                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1927                         if (VBOX_FAILURE(rc))
    1928                             break;
    1929 
    1930                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1931                         if (VBOX_FAILURE(rc))
    1932                             break;
    1933 
    1934                         cbCommit -= cbToCopy;
    1935                         offCommit += cbToCopy;
    1936                     }
    1937                     if (VBOX_FAILURE(rc))
    1938                         break;
    1939                 }
    1940             }
    1941 
    1942             if (pfnProgress)
    1943             {
    1944                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1945                             (uBlock * 100) / cBlocks,
    1946                             pvUser);
    1947                 /* Note: commiting is non breakable operation, skipping rc here. */
    1948             }
    1949         }
    1950     }
    1951 
    1952     RTMemTmpFree(pvBuf);
    1953     return rc;
    1954 }
    1955 
    1956 /**
    1957  * internal: commit last image(s) to selected previous image.
    1958  * note: all images accessed across this call must be opened in R/W mode.
    1959  * @remark    Only used by tstVDI.
    1960  */
    1961 static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    1962                             PFNVMPROGRESS pfnProgress, void *pvUser)
    1963 {
    1964     /* sanity check */
    1965     Assert(pDisk);
    1966     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1967     Assert(pDstImage);
    1968 
    1969     PVDIIMAGEDESC pImage = pDisk->pLast;
    1970     Assert(pImage);
    1971     Log(("vdiCommitToImage: commiting from image \"%s\" to image \"%s\"\n",
    1972          pImage->szFilename, pDstImage->szFilename));
    1973     if (pDstImage == pImage)
    1974     {
    1975         Log(("vdiCommitToImage: attempt to commit to the same image!\n"));
    1976         return VERR_VDI_NO_DIFF_IMAGES;
    1977     }
    1978 
    1979     /* Scan images for pDstImage. */
    1980     while (pImage && pImage != pDstImage)
    1981         pImage = pImage->pPrev;
    1982     if (!pImage)
    1983     {
    1984         AssertMsgFailed(("Invalid arguments: pDstImage is not in images chain\n"));
    1985         return VERR_INVALID_PARAMETER;
    1986     }
    1987     pImage = pDisk->pLast;
    1988 
    1989     /* alloc tmp buffer */
    1990     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1991     if (!pvBuf)
    1992         return VERR_NO_MEMORY;
    1993 
    1994     int rc = VINF_SUCCESS;
    1995     unsigned cBlocks = getImageBlocks(&pImage->Header);
    1996 
    1997     for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1998     {
    1999         pImage = pDisk->pLast;
    2000 
    2001         /* Find allocated block to commit. */
    2002         while (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE
    2003                &&  pImage != pDstImage)
    2004             pImage = pImage->pPrev;
    2005 
    2006         if (pImage != pDstImage)
    2007         {
    2008             /* Found used block in diff image (pImage), commit it. */
    2009             if (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    2010                 &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pDstImage->paBlocks[uBlock]))
    2011             {
    2012                 /* Block is zero in difference image and not allocated in primary image. */
    2013                 pDstImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    2014                 vdiSetModifiedFlag(pDstImage);
    2015             }
    2016             else
    2017             {
    2018                 /* Block is not zero / allocated in primary image. */
    2019                 unsigned cbCommit = getImageBlockSize(&pImage->Header);
    2020                 unsigned offCommit = 0;
    2021 
    2022                 /* do loop, because buffer size may be less then block size */
    2023                 while (cbCommit > 0)
    2024                 {
    2025                     unsigned cbToCopy = RT_MIN(cbCommit, pDisk->cbBuf);
    2026 
    2027                     rc = vdiReadInBlock(pImage, uBlock, offCommit, cbToCopy, pvBuf);
    2028                     if (VBOX_FAILURE(rc))
    2029                         break;
    2030 
    2031                     rc = vdiWriteInBlock(pDisk, pDstImage, uBlock, offCommit, cbToCopy, pvBuf);
    2032                     if (VBOX_FAILURE(rc))
    2033                         break;
    2034 
    2035                     cbCommit -= cbToCopy;
    2036                     offCommit += cbToCopy;
    2037                 }
    2038                 if (VBOX_FAILURE(rc))
    2039                     break;
    2040             }
    2041             pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_FREE;
    2042         }
    2043 
    2044         if (pfnProgress)
    2045         {
    2046             pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2047                         (uBlock * 100) / cBlocks,
    2048                         pvUser);
    2049             /* Note: commiting is non breakable operation, skipping rc here. */
    2050         }
    2051     }
    2052 
    2053     RTMemTmpFree(pvBuf);
    2054 
    2055     /* Go forward and update linkage information. */
    2056     for (pImage = pDstImage; pImage; pImage = pImage->pNext)
    2057     {
    2058         /* generate new last-modified uuid. */
    2059         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    2060 
    2061         /* fix up linkage. */
    2062         if (pImage != pDstImage)
    2063             *getImageParentModificationUUID(&pImage->Header) = *getImageModificationUUID(&pImage->pPrev->Header);
    2064 
    2065         /* reset modified flag. */
    2066         pImage->fModified = 0;
    2067     }
    2068 
    2069     /* Process committed images - truncate them. */
    2070     for (pImage = pDisk->pLast; pImage != pDstImage; pImage = pImage->pPrev)
    2071     {
    2072         /* note: can't understand how to do error works here? */
    2073 
    2074         setImageBlocksAllocated(&pImage->Header, 0);
    2075 
    2076         /* Truncate file. */
    2077         int rc2 = RTFileSetSize(pImage->File, pImage->offStartData);
    2078         if (VBOX_FAILURE(rc2))
    2079         {
    2080             rc = rc2;
    2081             Log(("vdiCommitToImage: set size (truncate) rc=%Vrc filename=\"%s\"\n",
    2082                  rc, pImage->szFilename));
    2083         }
    2084 
    2085         /* Save header and blocks array. */
    2086         rc2 = vdiUpdateBlocks(pImage);
    2087         if (VBOX_FAILURE(rc2))
    2088         {
    2089             rc = rc2;
    2090             Log(("vdiCommitToImage: update blocks and header rc=%Vrc filename=\"%s\"\n",
    2091                  rc, pImage->szFilename));
    2092         }
    2093     }
    2094 
    2095     if (pfnProgress)
    2096     {
    2097         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2098        /* Note: commiting is non breakable operation, skipping rc here. */
    2099     }
    2100 
    2101     Log(("vdiCommitToImage: done, rc=%Vrc\n", rc));
    2102 
    2103     return rc;
    2104 }
    2105 
    2106 /**
    2107  * Checks if image is available and not broken, returns some useful image parameters if requested.
    2108  *
    2109  * @returns VBox status code.
    2110  * @param   pszFilename     Name of the image file to check.
    2111  * @param   puVersion       Where to store the version of image. NULL is ok.
    2112  * @param   penmType        Where to store the type of image. NULL is ok.
    2113  * @param   pcbSize         Where to store the size of image in bytes. NULL is ok.
    2114  * @param   pUuid           Where to store the uuid of image creation. NULL is ok.
    2115  * @param   pParentUuid     Where to store the UUID of the parent image. NULL is ok.
    2116  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    2117  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    2118  */
    2119 IDER3DECL(int) VDICheckImage(const char *pszFilename, unsigned *puVersion, PVDIIMAGETYPE penmType,
    2120                              uint64_t *pcbSize, PRTUUID pUuid, PRTUUID pParentUuid,
    2121                              char *pszComment, unsigned cbComment)
    2122 {
    2123     LogFlow(("VDICheckImage:\n"));
    2124 
    2125     /* Check arguments. */
    2126     if (    !pszFilename
    2127         ||  *pszFilename == '\0')
    2128     {
    2129         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2130         return VERR_INVALID_PARAMETER;
    2131     }
    2132 
    2133     PVDIIMAGEDESC pImage;
    2134     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    2135     if (VBOX_SUCCESS(rc))
    2136     {
    2137         Log(("VDICheckImage: filename=\"%s\" version=%08X type=%X cbDisk=%llu uuid={%Vuuid}\n",
    2138              pszFilename,
    2139              pImage->PreHeader.u32Version,
    2140              getImageType(&pImage->Header),
    2141              getImageDiskSize(&pImage->Header),
    2142              getImageCreationUUID(&pImage->Header)));
    2143 
    2144         if (    pszComment
    2145             &&  cbComment > 0)
    2146         {
    2147             char *pszTmp = getImageComment(&pImage->Header);
    2148             unsigned cb = strlen(pszTmp);
    2149             if (cbComment > cb)
    2150                 memcpy(pszComment, pszTmp, cb + 1);
    2151             else
    2152                 rc = VERR_BUFFER_OVERFLOW;
    2153         }
    2154         if (VBOX_SUCCESS(rc))
    2155         {
    2156             if (puVersion)
    2157                 *puVersion = pImage->PreHeader.u32Version;
    2158             if (penmType)
    2159                 *penmType = getImageType(&pImage->Header);
    2160             if (pcbSize)
    2161                 *pcbSize = getImageDiskSize(&pImage->Header);
    2162             if (pUuid)
    2163                 *pUuid = *getImageCreationUUID(&pImage->Header);
    2164             if (pParentUuid)
    2165                 *pParentUuid = *getImageParentUUID(&pImage->Header);
    2166         }
    2167         vdiCloseImage(pImage);
    2168     }
    2169 
    2170     LogFlow(("VDICheckImage: returns %Vrc\n", rc));
    2171     return rc;
    2172 }
    2173 
    2174 /**
    2175  * Changes an image's comment string.
    2176  *
    2177  * @returns VBox status code.
    2178  * @param   pszFilename     Name of the image file to operate on.
    2179  * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
    2180  */
    2181 IDER3DECL(int) VDISetImageComment(const char *pszFilename, const char *pszComment)
    2182 {
    2183     LogFlow(("VDISetImageComment:\n"));
    2184 
    2185     /*
    2186      * Validate arguments.
    2187      */
    2188     if (    !pszFilename
    2189         ||  *pszFilename == '\0')
    2190     {
    2191         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2192         return VERR_INVALID_PARAMETER;
    2193     }
    2194 
    2195     const size_t cchComment = pszComment ? strlen(pszComment) : 0;
    2196     if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
    2197     {
    2198         Log(("VDISetImageComment: pszComment is too long, %d bytes!\n", cchComment));
    2199         return VERR_VDI_COMMENT_TOO_LONG;
    2200     }
    2201 
    2202     /*
    2203      * Open the image for updating.
    2204      */
    2205     PVDIIMAGEDESC pImage;
    2206     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2207     if (VBOX_FAILURE(rc))
    2208     {
    2209         Log(("VDISetImageComment: vdiOpenImage rc=%Vrc filename=\"%s\"!\n", rc, pszFilename));
    2210         return rc;
    2211     }
    2212     if (!pImage->fReadOnly)
    2213     {
    2214         /* we don't support old style images */
    2215         if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
    2216         {
    2217             /*
    2218              * Update the comment field, making sure to zero out all of the previous comment.
    2219              */
    2220             memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
    2221             memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
    2222 
    2223             /* write out new the header */
    2224             rc = vdiUpdateHeader(pImage);
    2225             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    2226                              pImage->szFilename, rc));
    2227         }
    2228         else
    2229         {
    2230             Log(("VDISetImageComment: Unsupported version!\n"));
    2231             rc = VERR_VDI_UNSUPPORTED_VERSION;
    2232         }
    2233     }
    2234     else
    2235     {
    2236         Log(("VDISetImageComment: image \"%s\" is opened as read-only!\n", pszFilename));
    2237         rc = VERR_VDI_IMAGE_READ_ONLY;
    2238     }
    2239 
    2240     vdiCloseImage(pImage);
    2241     return rc;
    2242 }
    2243 
    2244 /**
    2245  * Creates a new base image file.
    2246  *
    2247  * @returns VBox status code.
    2248  * @param   pszFilename     Name of the creating image file.
    2249  * @param   enmType         Image type, only base image types are acceptable.
    2250  * @param   cbSize          Image size in bytes.
    2251  * @param   pszComment      Pointer to image comment. NULL is ok.
    2252  * @param   pfnProgress     Progress callback. Optional.
    2253  * @param   pvUser          User argument for the progress callback.
    2254  */
    2255 IDER3DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize,
    2256                                   const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    2257 {
    2258     LogFlow(("VDICreateBaseImage:\n"));
    2259 
    2260     /* Check arguments. */
    2261     if (    !pszFilename
    2262         ||  *pszFilename == '\0'
    2263         ||  (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
    2264         ||  cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
    2265     {
    2266         AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
    2267                          pszFilename, enmType, cbSize));
    2268         return VERR_INVALID_PARAMETER;
    2269     }
    2270 
    2271     int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
    2272                             pfnProgress, pvUser);
    2273     LogFlow(("VDICreateBaseImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2274     return rc;
    2275 }
    2276 
    2277 /**
    2278  * Creates a differencing dynamically growing image file for specified parent image.
    2279  *
    2280  * @returns VBox status code.
    2281  * @param   pszFilename     Name of the creating differencing image file.
    2282  * @param   pszParent       Name of the parent image file. May be base or diff image type.
    2283  * @param   pszComment      Pointer to image comment. NULL is ok.
    2284  * @param   pfnProgress     Progress callback. Optional.
    2285  * @param   pvUser          User argument for the progress callback.
    2286  */
    2287 IDER3DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent,
    2288                                         const char *pszComment, PFNVMPROGRESS pfnProgress,
    2289                                         void *pvUser)
    2290 {
    2291     LogFlow(("VDICreateDifferenceImage:\n"));
    2292 
    2293     /* Check arguments. */
    2294     if (    !pszFilename
    2295         ||  *pszFilename == '\0'
    2296         ||  !pszParent
    2297         ||  *pszParent == '\0')
    2298     {
    2299         AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
    2300                          pszFilename, pszParent));
    2301         return VERR_INVALID_PARAMETER;
    2302     }
    2303 
    2304     PVDIIMAGEDESC pParent;
    2305     int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
    2306     if (VBOX_SUCCESS(rc))
    2307     {
    2308         rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
    2309                             getImageDiskSize(&pParent->Header), pszComment, pParent,
    2310                             pfnProgress, pvUser);
    2311         vdiCloseImage(pParent);
    2312     }
    2313     LogFlow(("VDICreateDifferenceImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2314     return rc;
    2315 }
    2316 
    2317 /**
    2318  * Deletes an image. Only valid image files can be deleted by this call.
    2319  *
    2320  * @returns VBox status code.
    2321  * @param   pszFilename     Name of the image file to check.
    2322  */
    2323 IDER3DECL(int) VDIDeleteImage(const char *pszFilename)
    2324 {
    2325     LogFlow(("VDIDeleteImage:\n"));
    2326     /* Check arguments. */
    2327     if (    !pszFilename
    2328         ||  *pszFilename == '\0')
    2329     {
    2330         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2331         return VERR_INVALID_PARAMETER;
    2332     }
    2333 
    2334     int rc = VDICheckImage(pszFilename, NULL, NULL, NULL, NULL, NULL, NULL, 0);
    2335     if (VBOX_SUCCESS(rc))
    2336         rc = RTFileDelete(pszFilename);
    2337 
    2338     LogFlow(("VDIDeleteImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2339     return rc;
    2340 }
    2341 
    2342 /**
    2343  * Makes a copy of image file with a new (other) creation uuid.
    2344  *
    2345  * @returns VBox status code.
    2346  * @param   pszDstFilename  Name of the image file to create.
    2347  * @param   pszSrcFilename  Name of the image file to copy from.
    2348  * @param   pszComment      Pointer to image comment. If NULL specified comment
    2349  *                          will be copied from source image.
    2350  * @param   pfnProgress     Progress callback. Optional.
    2351  * @param   pvUser          User argument for the progress callback.
    2352  */
    2353 IDER3DECL(int) VDICopyImage(const char *pszDstFilename, const char *pszSrcFilename,
    2354                             const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    2355 {
    2356     LogFlow(("VDICopyImage:\n"));
    2357 
    2358     /* Check arguments. */
    2359     if (    !pszDstFilename
    2360         ||  *pszDstFilename == '\0'
    2361         ||  !pszSrcFilename
    2362         ||  *pszSrcFilename == '\0')
    2363     {
    2364         AssertMsgFailed(("Invalid arguments: pszDstFilename=%p pszSrcFilename=%p\n",
    2365                          pszDstFilename, pszSrcFilename));
    2366         return VERR_INVALID_PARAMETER;
    2367     }
    2368 
    2369     /* Special check for comment length. */
    2370     if (    pszComment
    2371         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    2372     {
    2373         Log(("VDICopyImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    2374         return VERR_VDI_COMMENT_TOO_LONG;
    2375     }
    2376 
    2377     PVDIIMAGEDESC pImage;
    2378     int rc = vdiOpenImage(&pImage, pszSrcFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    2379     if (VBOX_FAILURE(rc))
    2380     {
    2381         Log(("VDICopyImage: src image \"%s\" open failed rc=%Vrc\n", pszSrcFilename, rc));
    2382         return rc;
    2383     }
    2384 
    2385     uint64_t cbFile = pImage->offStartData
    2386                     + ((uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset);
    2387 
    2388     /* create file */
    2389     RTFILE File;
    2390     rc = RTFileOpen(&File,
    2391                     pszDstFilename,
    2392                     RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    2393     if (VBOX_SUCCESS(rc))
    2394     {
    2395         /* lock new image exclusively to close any wrong access by VDI API calls. */
    2396         rc = RTFileLock(File, RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbFile);
    2397         if (VBOX_SUCCESS(rc))
    2398         {
    2399             /* Set the size of a new file. */
    2400             rc = RTFileSetSize(File, cbFile);
    2401             if (VBOX_SUCCESS(rc))
    2402             {
    2403                 /* A dirty trick - use original image data to fill the new image. */
    2404                 RTFILE oldFileHandle = pImage->File;
    2405                 pImage->File = File;
    2406                 pImage->fReadOnly = false;
    2407 
    2408                 /* generate a new image creation uuid. */
    2409                 RTUuidCreate(getImageCreationUUID(&pImage->Header));
    2410                 /* generate a new image last-modified uuid. */
    2411                 RTUuidCreate(getImageModificationUUID(&pImage->Header));
    2412                 /* set image comment, if present. */
    2413                 if (pszComment)
    2414                     strncpy(getImageComment(&pImage->Header), pszComment, VDI_IMAGE_COMMENT_SIZE);
    2415 
    2416                 /* Write the pre-header to new image. */
    2417                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2418                 if (VBOX_SUCCESS(rc))
    2419                     rc = RTFileWrite(pImage->File,
    2420                                      &pImage->PreHeader,
    2421                                      sizeof(pImage->PreHeader),
    2422                                      NULL);
    2423 
    2424                 /* Write the header and the blocks array to new image. */
    2425                 if (VBOX_SUCCESS(rc))
    2426                     rc = vdiUpdateBlocks(pImage);
    2427 
    2428                 pImage->File = oldFileHandle;
    2429                 pImage->fReadOnly = true;
    2430 
    2431                 /* Seek to the data start in both images. */
    2432                 if (VBOX_SUCCESS(rc))
    2433                     rc = RTFileSeek(pImage->File,
    2434                                     pImage->offStartData,
    2435                                     RTFILE_SEEK_BEGIN,
    2436                                     NULL);
    2437                 if (VBOX_SUCCESS(rc))
    2438                     rc = RTFileSeek(File,
    2439                                     pImage->offStartData,
    2440                                     RTFILE_SEEK_BEGIN,
    2441                                     NULL);
    2442 
    2443                 if (VBOX_SUCCESS(rc))
    2444                 {
    2445                     /* alloc tmp buffer */
    2446                     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    2447                     if (pvBuf)
    2448                     {
    2449                         /* Main copy loop. */
    2450                         uint64_t cbData = cbFile - pImage->offStartData;
    2451                         unsigned cBlocks = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2452                         unsigned c = 0;
    2453 
    2454                         while (cbData)
    2455                         {
    2456                             unsigned cbToCopy = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2457 
    2458                             /* Read. */
    2459                             rc = RTFileRead(pImage->File, pvBuf, cbToCopy, NULL);
    2460                             if (VBOX_FAILURE(rc))
    2461                                 break;
    2462 
    2463                             /* Write. */
    2464                             rc = RTFileWrite(File, pvBuf, cbToCopy, NULL);
    2465                             if (VBOX_FAILURE(rc))
    2466                                 break;
    2467 
    2468                             if (pfnProgress)
    2469                             {
    2470                                 c++;
    2471                                 rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2472                                                  (c * 100) / cBlocks,
    2473                                                  pvUser);
    2474                                 if (VBOX_FAILURE(rc))
    2475                                     break;
    2476                             }
    2477                             cbData -= cbToCopy;
    2478                         }
    2479 
    2480                         RTMemTmpFree(pvBuf);
    2481                     }
    2482                     else
    2483                         rc = VERR_NO_MEMORY;
    2484                 }
    2485             }
    2486 
    2487             RTFileUnlock(File, 0, cbFile);
    2488         }
    2489 
    2490         RTFileClose(File);
    2491 
    2492         if (VBOX_FAILURE(rc))
    2493             RTFileDelete(pszDstFilename);
    2494 
    2495         if (pfnProgress)
    2496             pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2497     }
    2498 
    2499     vdiCloseImage(pImage);
    2500 
    2501     LogFlow(("VDICopyImage: returns %Vrc for pszSrcFilename=\"%s\" pszDstFilename=\"%s\"\n",
    2502              rc, pszSrcFilename, pszDstFilename));
    2503     return rc;
    2504 }
    2505 
    2506 /**
    2507  * Shrinks growing image file by removing zeroed data blocks.
    2508  *
    2509  * @returns VBox status code.
    2510  * @param   pszFilename     Name of the image file to shrink.
    2511  * @param   pfnProgress     Progress callback. Optional.
    2512  * @param   pvUser          User argument for the progress callback.
    2513  */
    2514 IDER3DECL(int) VDIShrinkImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2515 {
    2516     LogFlow(("VDIShrinkImage:\n"));
    2517 
    2518     /* Check arguments. */
    2519     if (    !pszFilename
    2520         ||  *pszFilename == '\0')
    2521     {
    2522         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2523         return VERR_INVALID_PARAMETER;
    2524     }
    2525 
    2526     PVDIIMAGEDESC pImage;
    2527     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2528     if (VBOX_FAILURE(rc))
    2529     {
    2530         Log(("VDIShrinkImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2531         return rc;
    2532     }
    2533     if (pImage->fReadOnly)
    2534     {
    2535         Log(("VDIShrinkImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2536         vdiCloseImage(pImage);
    2537         return VERR_VDI_IMAGE_READ_ONLY;
    2538     }
    2539 
    2540     /* Do debug dump. */
    2541     vdiDumpImage(pImage);
    2542 
    2543     /* Working data. */
    2544     unsigned cbBlock          = getImageBlockSize(&pImage->Header);
    2545     unsigned cBlocks          = getImageBlocks(&pImage->Header);
    2546     unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    2547 
    2548     uint64_t cbFile;
    2549     rc = RTFileGetSize(pImage->File, &cbFile);
    2550     if (VBOX_FAILURE(rc))
    2551     {
    2552         Log(("VDIShrinkImage: RTFileGetSize rc=%Vrc for file=\"%s\"\n", rc, pszFilename));
    2553         vdiCloseImage(pImage);
    2554         return rc;
    2555     }
    2556 
    2557     uint64_t cbData = cbFile - pImage->offStartData;
    2558     unsigned cBlocksAllocated2 = (unsigned)(cbData >> pImage->uShiftIndex2Offset);
    2559     if (cbData != (uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
    2560         Log(("VDIShrinkImage: invalid image file length, cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2561              cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2562 
    2563     /* Allocate second blocks array for back resolving. */
    2564     PVDIIMAGEBLOCKPOINTER paBlocks2 =
    2565                 (PVDIIMAGEBLOCKPOINTER)RTMemTmpAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks);
    2566     if (!paBlocks2)
    2567     {
    2568         Log(("VDIShrinkImage: failed to allocate paBlocks2 buffer (%u bytes)\n", sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks));
    2569         vdiCloseImage(pImage);
    2570         return VERR_NO_MEMORY;
    2571     }
    2572 
    2573     /* Init second blocks array. */
    2574     for (unsigned n = 0; n < cBlocks; n++)
    2575         paBlocks2[n] = VDI_IMAGE_BLOCK_FREE;
    2576 
    2577     /* Fill second blocks array, check for allocational errors. */
    2578     for (unsigned n = 0; n < cBlocks; n++)
    2579     {
    2580         if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[n]))
    2581         {
    2582             unsigned uBlock = pImage->paBlocks[n];
    2583             if (uBlock < cBlocksAllocated2)
    2584             {
    2585                 if (paBlocks2[uBlock] == VDI_IMAGE_BLOCK_FREE)
    2586                     paBlocks2[uBlock] = n;
    2587                 else
    2588                 {
    2589                     Log(("VDIShrinkImage: block n=%u -> uBlock=%u is already in use!\n", n, uBlock));
    2590                     /* free second link to block. */
    2591                     pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
    2592                 }
    2593             }
    2594             else
    2595             {
    2596                 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is out of blocks range! (cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu)\n",
    2597                      n, uBlock, cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2598                 /* free link to invalid block. */
    2599                 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
    2600             }
    2601         }
    2602     }
    2603 
    2604     /* Allocate a working buffer for one block. */
    2605     void *pvBuf = RTMemTmpAlloc(cbBlock);
    2606     if (pvBuf)
    2607     {
    2608         /* Main voodoo loop, search holes and fill it. */
    2609         unsigned uBlockWrite = 0;
    2610         for (unsigned uBlock = 0; uBlock < cBlocksAllocated2; uBlock++)
    2611         {
    2612             if (paBlocks2[uBlock] != VDI_IMAGE_BLOCK_FREE)
    2613             {
    2614                 /* Read the block from file and check for zeroes. */
    2615                 uint64_t u64Offset = ((uint64_t)uBlock << pImage->uShiftIndex2Offset)
    2616                                    + (pImage->offStartData + pImage->offStartBlockData);
    2617                 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2618                 if (VBOX_FAILURE(rc))
    2619                 {
    2620                     Log(("VDIShrinkImage: seek rc=%Vrc filename=\"%s\" uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2621                          rc, pImage->szFilename, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2622                     break;
    2623                 }
    2624                 rc = RTFileRead(pImage->File, pvBuf, cbBlock, NULL);
    2625                 if (VBOX_FAILURE(rc))
    2626                 {
    2627                     Log(("VDIShrinkImage: read rc=%Vrc filename=\"%s\" cbBlock=%u uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2628                          rc, pImage->szFilename, cbBlock, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2629                     break;
    2630                 }
    2631 
    2632                 /* Check block for data. */
    2633                 Assert(cbBlock % 4 == 0);
    2634                 if (ASMBitFirstSet(pvBuf, cbBlock * 8) != -1)
    2635                 {
    2636                     /* Block has a data, may be it must be moved. */
    2637                     if (uBlockWrite < uBlock)
    2638                     {
    2639                         /* Move the block. */
    2640                         u64Offset = ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)
    2641                                   + (pImage->offStartData + pImage->offStartBlockData);
    2642                         rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2643                         if (VBOX_FAILURE(rc))
    2644                         {
    2645                             Log(("VDIShrinkImage: seek(2) rc=%Vrc filename=\"%s\" uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2646                                  rc, pImage->szFilename, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2647                             break;
    2648                         }
    2649                         rc = RTFileWrite(pImage->File, pvBuf, cbBlock, NULL);
    2650                         if (VBOX_FAILURE(rc))
    2651                         {
    2652                             Log(("VDIShrinkImage: write rc=%Vrc filename=\"%s\" cbBlock=%u uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2653                                  rc, pImage->szFilename, cbBlock, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2654                             break;
    2655                         }
    2656                     }
    2657                     /* Fix the block pointer. */
    2658                     pImage->paBlocks[paBlocks2[uBlock]] = uBlockWrite;
    2659                     uBlockWrite++;
    2660                 }
    2661                 else
    2662                 {
    2663                     Log(("VDIShrinkImage: found a zeroed block, uBlock=%u\n", uBlock));
    2664 
    2665                     /* Fix the block pointer. */
    2666                     pImage->paBlocks[paBlocks2[uBlock]] = VDI_IMAGE_BLOCK_ZERO;
    2667                 }
    2668             }
    2669             else
    2670                 Log(("VDIShrinkImage: found an unused block, uBlock=%u\n", uBlock));
    2671 
    2672             if (pfnProgress)
    2673             {
    2674                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2675                             (uBlock * 100) / cBlocksAllocated2,
    2676                             pvUser);
    2677                 /* Shrink is unbreakable operation! */
    2678             }
    2679         }
    2680 
    2681         RTMemTmpFree(pvBuf);
    2682 
    2683         if (    VBOX_SUCCESS(rc)
    2684             &&  uBlockWrite < cBlocksAllocated2)
    2685         {
    2686             /* File size must be shrinked. */
    2687             Log(("VDIShrinkImage: shrinking file size from %llu to %llu bytes\n",
    2688                  cbFile,
    2689                  pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)));
    2690             rc = RTFileSetSize(pImage->File,
    2691                                pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset));
    2692             if (VBOX_FAILURE(rc))
    2693                 Log(("VDIShrinkImage: RTFileSetSize rc=%Vrc\n", rc));
    2694         }
    2695         cBlocksAllocated2 = uBlockWrite;
    2696     }
    2697     else
    2698     {
    2699         Log(("VDIShrinkImage: failed to allocate working buffer (%u bytes)\n", cbBlock));
    2700         rc = VERR_NO_MEMORY;
    2701     }
    2702 
    2703     /* Save header and blocks array. */
    2704     if (VBOX_SUCCESS(rc))
    2705     {
    2706         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated2);
    2707         rc = vdiUpdateBlocks(pImage);
    2708         if (pfnProgress)
    2709             pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2710     }
    2711 
    2712     /* Do debug dump. */
    2713     vdiDumpImage(pImage);
    2714 
    2715     /* Clean up. */
    2716     RTMemTmpFree(paBlocks2);
    2717     vdiCloseImage(pImage);
    2718 
    2719     LogFlow(("VDIShrinkImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2720     return rc;
    2721 }
    2722 
    2723 /**
    2724  * Converts image file from older VDI formats to current one.
    2725  *
    2726  * @returns VBox status code.
    2727  * @param   pszFilename     Name of the image file to convert.
    2728  * @param   pfnProgress     Progress callback. Optional.
    2729  * @param   pvUser          User argument for the progress callback.
    2730  * @remark  Only used by vditool
    2731  */
    2732 IDER3DECL(int) VDIConvertImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2733 {
    2734     LogFlow(("VDIConvertImage:\n"));
    2735 
    2736     /* Check arguments. */
    2737     if (    !pszFilename
    2738         ||  *pszFilename == '\0')
    2739     {
    2740         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2741         return VERR_INVALID_PARAMETER;
    2742     }
    2743 
    2744     PVDIIMAGEDESC pImage;
    2745     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2746     if (VBOX_FAILURE(rc))
    2747     {
    2748         Log(("VDIConvertImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2749         return rc;
    2750     }
    2751 
    2752     VDIHEADER Header = {0};
    2753     int off;
    2754     uint64_t cbFile;
    2755     uint64_t cbData;
    2756 
    2757     if (pImage->fReadOnly)
    2758     {
    2759         Log(("VDIConvertImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2760         rc = VERR_VDI_IMAGE_READ_ONLY;
    2761         goto l_conversion_failed;
    2762     }
    2763 
    2764     if (pImage->PreHeader.u32Version != 0x00000002)
    2765     {
    2766         Log(("VDIConvertImage: unsupported version=%08X filename=\"%s\"\n",
    2767              pImage->PreHeader.u32Version, pszFilename));
    2768         rc = VERR_VDI_UNSUPPORTED_VERSION;
    2769         goto l_conversion_failed;
    2770     }
    2771 
    2772     /* Build new version header from old one. */
    2773     vdiInitHeader(&Header,
    2774                   getImageType(&pImage->Header),
    2775                   VDI_IMAGE_FLAGS_DEFAULT,    /* Safety issue: Always use default flags. */
    2776                   getImageComment(&pImage->Header),
    2777                   getImageDiskSize(&pImage->Header),
    2778                   getImageBlockSize(&pImage->Header),
    2779                   0);
    2780     setImageBlocksAllocated(&Header, getImageBlocksAllocated(&pImage->Header));
    2781     *getImageGeometry(&Header) = *getImageGeometry(&pImage->Header);
    2782     setImageTranslation(&Header, getImageTranslation(&pImage->Header));
    2783     *getImageCreationUUID(&Header) = *getImageCreationUUID(&pImage->Header);
    2784     *getImageModificationUUID(&Header) = *getImageModificationUUID(&pImage->Header);
    2785 
    2786     /* Calc data offset. */
    2787     off = getImageDataOffset(&Header) - getImageDataOffset(&pImage->Header);
    2788     if (off <= 0)
    2789     {
    2790         rc = VERR_VDI_INVALID_HEADER;
    2791         goto l_conversion_failed;
    2792     }
    2793 
    2794     rc = RTFileGetSize(pImage->File, &cbFile);
    2795     if (VBOX_FAILURE(rc))
    2796         goto l_conversion_failed;
    2797 
    2798     /* Check file size. */
    2799     cbData = cbFile - getImageDataOffset(&pImage->Header);
    2800     if (cbData != (uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset)
    2801     {
    2802         AssertMsgFailed(("Invalid file size, broken image?\n"));
    2803         rc = VERR_VDI_INVALID_HEADER;
    2804         goto l_conversion_failed;
    2805     }
    2806 
    2807     /* Expand file. */
    2808     rc = RTFileSetSize(pImage->File, cbFile + off);
    2809     if (VBOX_FAILURE(rc))
    2810         goto l_conversion_failed;
    2811 
    2812     if (cbData > 0)
    2813     {
    2814         /* Calc current file position to move data from. */
    2815         uint64_t offFile;
    2816         if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2817             offFile = cbFile - VDIDISK_DEFAULT_BUFFER_SIZE;
    2818         else
    2819             offFile = getImageDataOffset(&pImage->Header);
    2820 
    2821         unsigned cMoves = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2822         unsigned c = 0;
    2823 
    2824         /* alloc tmp buffer */
    2825         void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    2826         if (pvBuf)
    2827         {
    2828             /* Move data. */
    2829             for (;;)
    2830             {
    2831                 unsigned cbToMove = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2832 
    2833                 /* Read. */
    2834                 rc = RTFileSeek(pImage->File, offFile, RTFILE_SEEK_BEGIN, NULL);
    2835                 if (VBOX_FAILURE(rc))
    2836                     break;
    2837                 rc = RTFileRead(pImage->File, pvBuf, cbToMove, NULL);
    2838                 if (VBOX_FAILURE(rc))
    2839                     break;
    2840 
    2841                 /* Write. */
    2842                 rc = RTFileSeek(pImage->File, offFile + off, RTFILE_SEEK_BEGIN, NULL);
    2843                 if (VBOX_FAILURE(rc))
    2844                     break;
    2845                 rc = RTFileWrite(pImage->File, pvBuf, cbToMove, NULL);
    2846                 if (VBOX_FAILURE(rc))
    2847                     break;
    2848 
    2849                 if (pfnProgress)
    2850                 {
    2851                     c++;
    2852                     pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2853                                 (c * 100) / cMoves,
    2854                                 pvUser);
    2855                     /* Note: conversion is non breakable operation, skipping rc here. */
    2856                 }
    2857 
    2858                 cbData -= cbToMove;
    2859                 if (cbData == 0)
    2860                     break;
    2861 
    2862                 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2863                     offFile -= VDIDISK_DEFAULT_BUFFER_SIZE;
    2864                 else
    2865                     offFile = getImageDataOffset(&pImage->Header);
    2866             }
    2867 
    2868             /* Fill the beginning of file with zeroes to wipe out old headers etc. */
    2869             if (VBOX_SUCCESS(rc))
    2870             {
    2871                 Assert(offFile + off <= VDIDISK_DEFAULT_BUFFER_SIZE);
    2872                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2873                 if (VBOX_SUCCESS(rc))
    2874                 {
    2875                     memset(pvBuf, 0, (unsigned)offFile + off);
    2876                     rc = RTFileWrite(pImage->File, pvBuf, (unsigned)offFile + off, NULL);
    2877                 }
    2878             }
    2879 
    2880             RTMemTmpFree(pvBuf);
    2881         }
    2882         else
    2883             rc = VERR_NO_MEMORY;
    2884 
    2885         if (VBOX_FAILURE(rc))
    2886             goto l_conversion_failed;
    2887     }
    2888 
    2889     if (pfnProgress)
    2890     {
    2891         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2892        /* Note: conversion is non breakable operation, skipping rc here. */
    2893     }
    2894 
    2895     /* Data moved, now we need to save new pre header, header and blocks array. */
    2896 
    2897     vdiInitPreHeader(&pImage->PreHeader);
    2898     pImage->Header = Header;
    2899 
    2900     /* Setup image parameters by header. */
    2901     vdiSetupImageDesc(pImage);
    2902 
    2903     /* Write pre-header. */
    2904     rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2905     if (VBOX_FAILURE(rc))
    2906         goto l_conversion_failed;
    2907     rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    2908     if (VBOX_FAILURE(rc))
    2909         goto l_conversion_failed;
    2910 
    2911     /* Write header and blocks array. */
    2912     rc = vdiUpdateBlocks(pImage);
    2913 
    2914 l_conversion_failed:
    2915     vdiCloseImage(pImage);
    2916 
    2917     LogFlow(("VDIConvertImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2918     return rc;
    2919 }
    2920 
    2921 /**
    2922  * Queries the image's UUID and parent UUIDs.
    2923  *
    2924  * @returns VBox status code.
    2925  * @param   pszFilename             Name of the image file to operate on.
    2926  * @param   pUuid                   Where to store image UUID (can be NULL).
    2927  * @param   pModificationUuid       Where to store modification UUID (can be NULL).
    2928  * @param   pParentUuuid            Where to store parent UUID (can be NULL).
    2929  * @param   pParentModificationUuid Where to store parent modification UUID (can be NULL).
    2930  */
    2931 IDER3DECL(int) VDIGetImageUUIDs(const char *pszFilename,
    2932                                 PRTUUID pUuid, PRTUUID pModificationUuid,
    2933                                 PRTUUID pParentUuid, PRTUUID pParentModificationUuid)
    2934 {
    2935     LogFlow(("VDIGetImageUUIDs:\n"));
    2936 
    2937     /* Check arguments. */
    2938     if (    !pszFilename
    2939         ||  *pszFilename == '\0')
    2940     {
    2941         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2942         return VERR_INVALID_PARAMETER;
    2943     }
    2944 
    2945     /*
    2946      * Try open the specified image.
    2947      */
    2948     PVDIIMAGEDESC pImage;
    2949     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2950     if (VBOX_FAILURE(rc))
    2951     {
    2952         Log(("VDIGetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2953         return rc;
    2954     }
    2955 
    2956     /*
    2957      * Query data.
    2958      */
    2959     if (pUuid)
    2960     {
    2961         PCRTUUID pTmpUuid = getImageCreationUUID(&pImage->Header);
    2962         if (pTmpUuid)
    2963             *pUuid = *pTmpUuid;
    2964         else
    2965             RTUuidClear(pUuid);
    2966     }
    2967     if (pModificationUuid)
    2968     {
    2969         PCRTUUID pTmpUuid = getImageModificationUUID(&pImage->Header);
    2970         if (pTmpUuid)
    2971             *pModificationUuid = *pTmpUuid;
    2972         else
    2973             RTUuidClear(pModificationUuid);
    2974     }
    2975     if (pParentUuid)
    2976     {
    2977         PCRTUUID pTmpUuid = getImageParentUUID(&pImage->Header);
    2978         if (pTmpUuid)
    2979             *pParentUuid = *pTmpUuid;
    2980         else
    2981             RTUuidClear(pParentUuid);
    2982     }
    2983     if (pParentModificationUuid)
    2984     {
    2985         PCRTUUID pTmpUuid = getImageParentModificationUUID(&pImage->Header);
    2986         if (pTmpUuid)
    2987             *pParentModificationUuid = *pTmpUuid;
    2988         else
    2989             RTUuidClear(pParentModificationUuid);
    2990     }
    2991 
    2992     /*
    2993      * Close the image.
    2994      */
    2995     vdiCloseImage(pImage);
    2996 
    2997     return VINF_SUCCESS;
    2998 }
    2999 
    3000 /**
    3001  * Changes the image's UUID and parent UUIDs.
    3002  *
    3003  * @returns VBox status code.
    3004  * @param   pszFilename             Name of the image file to operate on.
    3005  * @param   pUuid                   Optional parameter, new UUID of the image.
    3006  * @param   pModificationUuid       Optional parameter, new modification UUID of the image.
    3007  * @param   pParentUuuid            Optional parameter, new parent UUID of the image.
    3008  * @param   pParentModificationUuid Optional parameter, new parent modification UUID of the image.
    3009  */
    3010 IDER3DECL(int) VDISetImageUUIDs(const char *pszFilename,
    3011                                 PCRTUUID pUuid, PCRTUUID pModificationUuid,
    3012                                 PCRTUUID pParentUuid, PCRTUUID pParentModificationUuid)
    3013 {
    3014     LogFlow(("VDISetImageUUIDs:\n"));
    3015 
    3016     /* Check arguments. */
    3017     if (    !pszFilename
    3018         ||  *pszFilename == '\0')
    3019     {
    3020         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    3021         return VERR_INVALID_PARAMETER;
    3022     }
    3023 
    3024     PVDIIMAGEDESC pImage;
    3025     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    3026     if (VBOX_FAILURE(rc))
    3027     {
    3028         Log(("VDISetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    3029         return rc;
    3030     }
    3031     if (!pImage->fReadOnly)
    3032     {
    3033         /* we only support new images */
    3034         if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
    3035         {
    3036             if (pUuid)
    3037                 pImage->Header.u.v1.uuidCreate = *pUuid;
    3038 
    3039             if (pModificationUuid)
    3040                 pImage->Header.u.v1.uuidModify = *pModificationUuid;
    3041 
    3042             if (pParentUuid)
    3043                 pImage->Header.u.v1.uuidLinkage = *pParentUuid;
    3044 
    3045             if (pParentModificationUuid)
    3046                 pImage->Header.u.v1.uuidParentModify = *pParentModificationUuid;
    3047 
    3048             /* write out new header */
    3049             rc = vdiUpdateHeader(pImage);
    3050             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    3051                              pImage->szFilename, rc));
    3052         }
    3053         else
    3054         {
    3055             Log(("VDISetImageUUIDs: Version is not supported!\n"));
    3056             rc = VERR_VDI_UNSUPPORTED_VERSION;
    3057         }
    3058     }
    3059     else
    3060     {
    3061         Log(("VDISetImageUUIDs: image \"%s\" is opened as read-only!\n", pszFilename));
    3062         rc = VERR_VDI_IMAGE_READ_ONLY;
    3063     }
    3064 
    3065     vdiCloseImage(pImage);
    3066     return rc;
    3067 }
    3068 
    3069 /**
    3070  * Merges two images having a parent/child relationship (both directions).
    3071  *
    3072  * @returns VBox status code.
    3073  * @param   pszFilenameFrom         Name of the image file to merge from.
    3074  * @param   pszFilenameTo           Name of the image file to merge into.
    3075  * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
    3076  * @param   pvUser          User argument for the progress callback.
    3077  */
    3078 IDER3DECL(int) VDIMergeImage(const char *pszFilenameFrom, const char *pszFilenameTo,
    3079                              PFNVMPROGRESS pfnProgress, void *pvUser)
    3080 {
    3081     LogFlow(("VDIMergeImage:\n"));
    3082 
    3083     /* Check arguments. */
    3084     if (    !pszFilenameFrom
    3085         ||  *pszFilenameFrom == '\0'
    3086         ||  !pszFilenameTo
    3087         ||  *pszFilenameTo   == '\0')
    3088     {
    3089         AssertMsgFailed(("Invalid arguments: pszFilenameFrom=%p, pszFilenameTo=%p\n", pszFilenameFrom, pszFilenameTo));
    3090         return VERR_INVALID_PARAMETER;
    3091     }
    3092 
    3093     PVDIIMAGEDESC pImageFrom;
    3094     int rc = vdiOpenImage(&pImageFrom, pszFilenameFrom, VDI_OPEN_FLAGS_READONLY, NULL);
    3095     if (VBOX_FAILURE(rc))
    3096     {
    3097         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pstFilenameFrom=\"%s\"\n", rc, pszFilenameFrom));
    3098         return rc;
    3099     }
    3100 
    3101     PVDIIMAGEDESC pImageTo;
    3102     rc = vdiOpenImage(&pImageTo, pszFilenameTo, VDI_OPEN_FLAGS_NORMAL, NULL);
    3103     if (VBOX_FAILURE(rc))
    3104     {
    3105         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pszFilenameTo=\"%s\"\n", rc, pszFilenameTo));
    3106         vdiCloseImage(pImageFrom);
    3107         return rc;
    3108     }
    3109     if (pImageTo->fReadOnly)
    3110     {
    3111         Log(("VDIMergeImage: image \"%s\" is opened as read-only!\n", pszFilenameTo));
    3112         vdiCloseImage(pImageFrom);
    3113         vdiCloseImage(pImageTo);
    3114         return VERR_VDI_IMAGE_READ_ONLY;
    3115     }
    3116 
    3117     /*
    3118      *  when merging, we should not update the modification uuid of the target
    3119      *  image, because from the point of view of its children, it hasn't been
    3120      *  logically changed after the successful merge.
    3121      */
    3122     vdiDisableLastModifiedUpdate(pImageTo);
    3123 
    3124     /*
    3125      * Check in which direction we merge
    3126      */
    3127 
    3128     bool bParentToChild = false;
    3129     if (   getImageParentUUID(&pImageFrom->Header)
    3130         && !RTUuidCompare(getImageParentUUID(&pImageFrom->Header),
    3131                           getImageCreationUUID(&pImageTo->Header))
    3132         && !RTUuidCompare(getImageParentModificationUUID(&pImageFrom->Header),
    3133                           getImageModificationUUID(&pImageTo->Header)))
    3134     {
    3135         /* we merge from a child to its parent */
    3136     }
    3137     else
    3138     if (   getImageParentUUID(&pImageTo->Header)
    3139         && !RTUuidCompare(getImageParentUUID(&pImageTo->Header),
    3140                           getImageCreationUUID(&pImageFrom->Header))
    3141         && !RTUuidCompare(getImageParentModificationUUID(&pImageTo->Header),
    3142                           getImageModificationUUID(&pImageFrom->Header)))
    3143     {
    3144         /* we merge from a parent to its child */
    3145         bParentToChild = true;
    3146     }
    3147     else
    3148     {
    3149         /* the images are not related, we can't merge! */
    3150         Log(("VDIMergeImages: images do not have a parent/child or child/parent relationship!\n"));
    3151         rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    3152     }
    3153 
    3154     rc = vdiMergeImages(pImageFrom, pImageTo, bParentToChild, pfnProgress, pvUser);
    3155 
    3156     if (pfnProgress)
    3157     {
    3158         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    3159        /* Note: commiting is non breakable operation, skipping rc here. */
    3160     }
    3161 
    3162     /* cleanup */
    3163     vdiCloseImage(pImageFrom);
    3164     vdiCloseImage(pImageTo);
    3165 
    3166     Log(("VDIMergeImage: done, returning with rc = %Vrc\n", rc));
    3167     return rc;
    3168 }
    3169 
    3170 
    3171 /**
    3172  * internal: initialize VDIDISK structure.
    3173  */
    3174 static void vdiInitVDIDisk(PVDIDISK pDisk)
    3175 {
    3176     Assert(pDisk);
    3177     pDisk->u32Signature = VDIDISK_SIGNATURE;
    3178     pDisk->cImages = 0;
    3179     pDisk->pBase   = NULL;
    3180     pDisk->pLast   = NULL;
    3181     pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
    3182     pDisk->cbBuf   = VDIDISK_DEFAULT_BUFFER_SIZE;
    3183     pDisk->fHonorZeroWrites = false;
    3184 }
    3185 
    3186 /**
    3187  * internal: add image structure to the end of images list.
    3188  */
    3189 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    3190 {
    3191     pImage->pPrev = NULL;
    3192     pImage->pNext = NULL;
    3193 
    3194     if (pDisk->pBase)
    3195     {
    3196         Assert(pDisk->cImages > 0);
    3197         pImage->pPrev = pDisk->pLast;
    3198         pDisk->pLast->pNext = pImage;
    3199         pDisk->pLast = pImage;
    3200     }
    3201     else
    3202     {
    3203         Assert(pDisk->cImages == 0);
    3204         pDisk->pBase = pImage;
    3205         pDisk->pLast = pImage;
    3206     }
    3207 
    3208     pDisk->cImages++;
    3209 }
    3210 
    3211 /**
    3212  * internal: remove image structure from the images list.
    3213  */
    3214 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    3215 {
    3216     Assert(pDisk->cImages > 0);
    3217 
    3218     if (pImage->pPrev)
    3219         pImage->pPrev->pNext = pImage->pNext;
    3220     else
    3221         pDisk->pBase = pImage->pNext;
    3222 
    3223     if (pImage->pNext)
    3224         pImage->pNext->pPrev = pImage->pPrev;
    3225     else
    3226         pDisk->pLast = pImage->pPrev;
    3227 
    3228     pImage->pPrev = NULL;
    3229     pImage->pNext = NULL;
    3230 
    3231     pDisk->cImages--;
    3232 }
    3233 
    3234 /**
    3235  * Allocates and initializes VDI HDD container.
    3236  *
    3237  * @returns Pointer to newly created HDD container with no one opened image file.
    3238  * @returns NULL on failure, typically out of memory.
    3239  */
    3240 IDER3DECL(PVDIDISK) VDIDiskCreate(void)
    3241 {
    3242     PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
    3243     if (pDisk)
    3244         vdiInitVDIDisk(pDisk);
    3245     LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
    3246     return pDisk;
    3247 }
    3248 
    3249 /**
    3250  * Destroys VDI HDD container. If container has opened image files they will be closed.
    3251  *
    3252  * @param   pDisk           Pointer to VDI HDD container.
    3253  */
    3254 IDER3DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
    3255 {
    3256     LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
    3257     /* sanity check */
    3258     Assert(pDisk);
    3259     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3260 
    3261     if (pDisk)
    3262     {
    3263         VDIDiskCloseAllImages(pDisk);
    3264         RTMemFree(pDisk);
    3265     }
    3266 }
    3267 
    3268 /**
    3269  * Get working buffer size of VDI HDD container.
    3270  *
    3271  * @returns Working buffer size in bytes.
    3272  */
    3273 IDER3DECL(unsigned) VDIDiskGetBufferSize(PVDIDISK pDisk)
    3274 {
    3275     /* sanity check */
    3276     Assert(pDisk);
    3277     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3278 
    3279     LogFlow(("VDIDiskGetBufferSize: returns %u\n", pDisk->cbBuf));
    3280     return pDisk->cbBuf;
    3281 }
    3282 
    3283 /**
    3284  * Get read/write mode of VDI HDD.
    3285  *
    3286  * @returns Disk ReadOnly status.
    3287  * @returns true if no one VDI image is opened in HDD container.
    3288  */
    3289 IDER3DECL(bool) VDIDiskIsReadOnly(PVDIDISK pDisk)
    3290 {
    3291     /* sanity check */
    3292     Assert(pDisk);
    3293     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3294 
    3295     if (pDisk->pLast)
    3296     {
    3297         LogFlow(("VDIDiskIsReadOnly: returns %u\n", pDisk->pLast->fReadOnly));
    3298         return pDisk->pLast->fReadOnly;
    3299     }
    3300 
    3301     AssertMsgFailed(("No one disk image is opened!\n"));
    3302     return true;
    3303 }
    3304 
    3305 /**
    3306  * Get disk size of VDI HDD container.
    3307  *
    3308  * @returns Virtual disk size in bytes.
    3309  * @returns 0 if no one VDI image is opened in HDD container.
    3310  */
    3311 IDER3DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
    3312 {
    3313     /* sanity check */
    3314     Assert(pDisk);
    3315     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3316 
    3317     if (pDisk->pBase)
    3318     {
    3319         LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
    3320         return getImageDiskSize(&pDisk->pBase->Header);
    3321     }
    3322 
    3323     AssertMsgFailed(("No one disk image is opened!\n"));
    3324     return 0;
    3325 }
    3326 
    3327 /**
    3328  * Get block size of VDI HDD container.
    3329  *
    3330  * @returns VDI image block size in bytes.
    3331  * @returns 0 if no one VDI image is opened in HDD container.
    3332  */
    3333 IDER3DECL(unsigned) VDIDiskGetBlockSize(PVDIDISK pDisk)
    3334 {
    3335     /* sanity check */
    3336     Assert(pDisk);
    3337     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3338 
    3339     if (pDisk->pBase)
    3340     {
    3341         LogFlow(("VDIDiskGetBlockSize: returns %u\n", getImageBlockSize(&pDisk->pBase->Header)));
    3342         return getImageBlockSize(&pDisk->pBase->Header);
    3343     }
    3344 
    3345     AssertMsgFailed(("No one disk image is opened!\n"));
    3346     return 0;
    3347 }
    3348 
    3349 /**
    3350  * Get virtual disk geometry stored in image file.
    3351  *
    3352  * @returns VBox status code.
    3353  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3354  * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
    3355  * @param   pDisk           Pointer to VDI HDD container.
    3356  * @param   pcCylinders     Where to store the number of cylinders. NULL is ok.
    3357  * @param   pcHeads         Where to store the number of heads. NULL is ok.
    3358  * @param   pcSectors       Where to store the number of sectors. NULL is ok.
    3359  */
    3360 IDER3DECL(int) VDIDiskGetGeometry(PVDIDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
    3361 {
    3362     /* sanity check */
    3363     Assert(pDisk);
    3364     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3365 
    3366     if (pDisk->pBase)
    3367     {
    3368         int rc = VINF_SUCCESS;
    3369         PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
    3370         LogFlow(("VDIDiskGetGeometry: C/H/S = %u/%u/%u\n",
    3371                  pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
    3372         if (    pGeometry->cCylinders > 0
    3373             &&  pGeometry->cHeads > 0
    3374             &&  pGeometry->cSectors > 0)
    3375         {
    3376             if (pcCylinders)
    3377                 *pcCylinders = pGeometry->cCylinders;
    3378             if (pcHeads)
    3379                 *pcHeads = pGeometry->cHeads;
    3380             if (pcSectors)
    3381                 *pcSectors = pGeometry->cSectors;
    3382         }
    3383         else
    3384             rc = VERR_VDI_GEOMETRY_NOT_SET;
    3385 
    3386         LogFlow(("VDIDiskGetGeometry: returns %Vrc\n", rc));
    3387         return rc;
    3388     }
    3389 
    3390     AssertMsgFailed(("No one disk image is opened!\n"));
    3391     return VERR_VDI_NOT_OPENED;
    3392 }
    3393 
    3394 /**
    3395  * Store virtual disk geometry into base image file of HDD container.
    3396  *
    3397  * Note that in case of unrecoverable error all images of HDD container will be closed.
    3398  *
    3399  * @returns VBox status code.
    3400  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3401  * @param   pDisk           Pointer to VDI HDD container.
    3402  * @param   cCylinders      Number of cylinders.
    3403  * @param   cHeads          Number of heads.
    3404  * @param   cSectors        Number of sectors.
    3405  */
    3406 IDER3DECL(int) VDIDiskSetGeometry(PVDIDISK pDisk, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
    3407 {
    3408     LogFlow(("VDIDiskSetGeometry: C/H/S = %u/%u/%u\n", cCylinders, cHeads, cSectors));
    3409     /* sanity check */
    3410     Assert(pDisk);
    3411     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3412 
    3413     if (pDisk->pBase)
    3414     {
    3415         PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
    3416         pGeometry->cCylinders = cCylinders;
    3417         pGeometry->cHeads = cHeads;
    3418         pGeometry->cSectors = cSectors;
    3419         pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    3420 
    3421         /* Update header information in base image file. */
    3422         int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
    3423         LogFlow(("VDIDiskSetGeometry: returns %Vrc\n", rc));
    3424         return rc;
    3425     }
    3426 
    3427     AssertMsgFailed(("No one disk image is opened!\n"));
    3428     return VERR_VDI_NOT_OPENED;
    3429 }
    3430 
    3431 /**
    3432  * Get virtual disk translation mode stored in image file.
    3433  *
    3434  * @returns VBox status code.
    3435  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3436  * @param   pDisk           Pointer to VDI HDD container.
    3437  * @param   penmTranslation Where to store the translation mode (see pdm.h).
    3438  */
    3439 IDER3DECL(int) VDIDiskGetTranslation(PVDIDISK pDisk, PPDMBIOSTRANSLATION penmTranslation)
    3440 {
    3441     /* sanity check */
    3442     Assert(pDisk);
    3443     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3444     Assert(penmTranslation);
    3445 
    3446     if (pDisk->pBase)
    3447     {
    3448         *penmTranslation = getImageTranslation(&pDisk->pBase->Header);
    3449         LogFlow(("VDIDiskGetTranslation: translation=%d\n", *penmTranslation));
    3450         return VINF_SUCCESS;
    3451     }
    3452 
    3453     AssertMsgFailed(("No one disk image is opened!\n"));
    3454     return VERR_VDI_NOT_OPENED;
    3455 }
    3456 
    3457 /**
    3458  * Store virtual disk translation mode into base image file of HDD container.
    3459  *
    3460  * Note that in case of unrecoverable error all images of HDD container will be closed.
    3461  *
    3462  * @returns VBox status code.
    3463  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3464  * @param   pDisk           Pointer to VDI HDD container.
    3465  * @param   enmTranslation  Translation mode (see pdm.h).
    3466  */
    3467 IDER3DECL(int) VDIDiskSetTranslation(PVDIDISK pDisk, PDMBIOSTRANSLATION enmTranslation)
    3468 {
    3469     LogFlow(("VDIDiskSetTranslation: enmTranslation=%d\n", enmTranslation));
    3470     /* sanity check */
    3471     Assert(pDisk);
    3472     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3473 
    3474     if (pDisk->pBase)
    3475     {
    3476         setImageTranslation(&pDisk->pBase->Header, enmTranslation);
    3477 
    3478         /* Update header information in base image file. */
    3479         int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
    3480         LogFlow(("VDIDiskSetTranslation: returns %Vrc\n", rc));
    3481         return rc;
    3482     }
    3483 
    3484     AssertMsgFailed(("No one disk image is opened!\n"));
    3485     return VERR_VDI_NOT_OPENED;
    3486 }
    3487 
    3488 /**
    3489  * Get number of opened images in HDD container.
    3490  *
    3491  * @returns Number of opened images for HDD container. 0 if no images is opened.
    3492  * @param   pDisk           Pointer to VDI HDD container.
    3493  */
    3494 IDER3DECL(int) VDIDiskGetImagesCount(PVDIDISK pDisk)
    3495 {
    3496     /* sanity check */
    3497     Assert(pDisk);
    3498     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3499 
    3500     LogFlow(("VDIDiskGetImagesCount: returns %d\n", pDisk->cImages));
    3501     return pDisk->cImages;
    3502 }
    3503 
    3504 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage)
    3505 {
    3506     PVDIIMAGEDESC pImage = pDisk->pBase;
    3507     while (pImage && nImage)
    3508     {
    3509         pImage = pImage->pNext;
    3510         nImage--;
    3511     }
    3512     return pImage;
    3513 }
    3514 
    3515 /**
    3516  * Get version of opened image of HDD container.
    3517  *
    3518  * @returns VBox status code.
    3519  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3520  * @param   pDisk           Pointer to VDI HDD container.
    3521  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3522  * @param   puVersion       Where to store the image version.
    3523  */
    3524 IDER3DECL(int) VDIDiskGetImageVersion(PVDIDISK pDisk, int nImage, unsigned *puVersion)
    3525 {
    3526     /* sanity check */
    3527     Assert(pDisk);
    3528     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3529     Assert(puVersion);
    3530 
    3531     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3532     Assert(pImage);
    3533 
    3534     if (pImage)
    3535     {
    3536         *puVersion = pImage->PreHeader.u32Version;
    3537         LogFlow(("VDIDiskGetImageVersion: returns %08X\n", pImage->PreHeader.u32Version));
    3538         return VINF_SUCCESS;
    3539     }
    3540 
    3541     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3542     return VERR_VDI_IMAGE_NOT_FOUND;
    3543 }
    3544 
    3545 /**
    3546  * Get filename of opened image of HDD container.
    3547  *
    3548  * @returns VBox status code.
    3549  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3550  * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
    3551  * @param   pDisk           Pointer to VDI HDD container.
    3552  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3553  * @param   pszFilename     Where to store the image file name.
    3554  * @param   cbFilename      Size of buffer pszFilename points to.
    3555  */
    3556 IDER3DECL(int) VDIDiskGetImageFilename(PVDIDISK pDisk, int nImage, char *pszFilename, unsigned cbFilename)
    3557 {
    3558     /* sanity check */
    3559     Assert(pDisk);
    3560     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3561     Assert(pszFilename);
    3562 
    3563     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3564     Assert(pImage);
    3565 
    3566     if (pImage)
    3567     {
    3568         unsigned cb = strlen(pImage->szFilename);
    3569         if (cb < cbFilename)
    3570         {
    3571             /* memcpy is much better than strncpy. */
    3572             memcpy(pszFilename, pImage->szFilename, cb + 1);
    3573             LogFlow(("VDIDiskGetImageFilename: returns VINF_SUCCESS, filename=\"%s\" nImage=%d\n",
    3574                      pszFilename, nImage));
    3575             return VINF_SUCCESS;
    3576         }
    3577         else
    3578         {
    3579             AssertMsgFailed(("Out of buffer space, cbFilename=%d needed=%d\n", cbFilename, cb + 1));
    3580             return VERR_BUFFER_OVERFLOW;
    3581         }
    3582     }
    3583 
    3584     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3585     return VERR_VDI_IMAGE_NOT_FOUND;
    3586 }
    3587 
    3588 /**
    3589  * Get the comment line of opened image of HDD container.
    3590  *
    3591  * @returns VBox status code.
    3592  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3593  * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
    3594  * @param   pDisk           Pointer to VDI HDD container.
    3595  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3596  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    3597  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    3598  */
    3599 IDER3DECL(int) VDIDiskGetImageComment(PVDIDISK pDisk, int nImage, char *pszComment, unsigned cbComment)
    3600 {
    3601     /* sanity check */
    3602     Assert(pDisk);
    3603     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3604     Assert(pszComment);
    3605 
    3606     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3607     Assert(pImage);
    3608 
    3609     if (pImage)
    3610     {
    3611         char *pszTmp = getImageComment(&pImage->Header);
    3612         unsigned cb = strlen(pszTmp);
    3613         if (cb < cbComment)
    3614         {
    3615             /* memcpy is much better than strncpy. */
    3616             memcpy(pszComment, pszTmp, cb + 1);
    3617             LogFlow(("VDIDiskGetImageComment: returns VINF_SUCCESS, comment=\"%s\" nImage=%d\n",
    3618                      pszTmp, nImage));
    3619             return VINF_SUCCESS;
    3620         }
    3621         else
    3622         {
    3623             AssertMsgFailed(("Out of buffer space, cbComment=%d needed=%d\n", cbComment, cb + 1));
    3624             return VERR_BUFFER_OVERFLOW;
    3625         }
    3626     }
    3627 
    3628     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3629     return VERR_VDI_IMAGE_NOT_FOUND;
    3630 }
    3631 
    3632 /**
    3633  * Get type of opened image of HDD container.
    3634  *
    3635  * @returns VBox status code.
    3636  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3637  * @param   pDisk           Pointer to VDI HDD container.
    3638  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3639  * @param   penmType        Where to store the image type.
    3640  */
    3641 IDER3DECL(int) VDIDiskGetImageType(PVDIDISK pDisk, int nImage, PVDIIMAGETYPE penmType)
    3642 {
    3643     /* sanity check */
    3644     Assert(pDisk);
    3645     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3646     Assert(penmType);
    3647 
    3648     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3649     Assert(pImage);
    3650 
    3651     if (pImage)
    3652     {
    3653         *penmType = getImageType(&pImage->Header);
    3654         LogFlow(("VDIDiskGetImageType: returns VINF_SUCCESS, type=%X nImage=%d\n",
    3655                  *penmType, nImage));
    3656         return VINF_SUCCESS;
    3657     }
    3658 
    3659     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3660     return VERR_VDI_IMAGE_NOT_FOUND;
    3661 }
    3662 
    3663 /**
    3664  * Get flags of opened image of HDD container.
    3665  *
    3666  * @returns VBox status code.
    3667  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3668  * @param   pDisk           Pointer to VDI HDD container.
    3669  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3670  * @param   pfFlags         Where to store the image flags.
    3671  */
    3672 IDER3DECL(int) VDIDiskGetImageFlags(PVDIDISK pDisk, int nImage, unsigned *pfFlags)
    3673 {
    3674     /* sanity check */
    3675     Assert(pDisk);
    3676     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3677     Assert(pfFlags);
    3678 
    3679     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3680     Assert(pImage);
    3681 
    3682     if (pImage)
    3683     {
    3684         *pfFlags = getImageFlags(&pImage->Header);
    3685         LogFlow(("VDIDiskGetImageFlags: returns VINF_SUCCESS, flags=%08X nImage=%d\n",
    3686                  *pfFlags, nImage));
    3687         return VINF_SUCCESS;
    3688     }
    3689 
    3690     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3691     return VERR_VDI_IMAGE_NOT_FOUND;
    3692 }
    3693 
    3694 /**
    3695  * Get Uuid of opened image of HDD container.
    3696  *
    3697  * @returns VBox status code.
    3698  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3699  * @param   pDisk           Pointer to VDI HDD container.
    3700  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3701  * @param   pUuid           Where to store the image creation uuid.
    3702  */
    3703 IDER3DECL(int) VDIDiskGetImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3704 {
    3705     /* sanity check */
    3706     Assert(pDisk);
    3707     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3708     Assert(pUuid);
    3709 
    3710     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3711     Assert(pImage);
    3712 
    3713     if (pImage)
    3714     {
    3715         *pUuid = *getImageCreationUUID(&pImage->Header);
    3716         LogFlow(("VDIDiskGetImageUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3717                  pUuid, nImage));
    3718         return VINF_SUCCESS;
    3719     }
    3720 
    3721     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3722     return VERR_VDI_IMAGE_NOT_FOUND;
    3723 }
    3724 
    3725 /**
    3726  * Get last modification Uuid of opened image of HDD container.
    3727  *
    3728  * @returns VBox status code.
    3729  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3730  * @param   pDisk           Pointer to VDI HDD container.
    3731  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3732  * @param   pUuid           Where to store the image modification uuid.
    3733  */
    3734 IDER3DECL(int) VDIDiskGetImageModificationUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3735 {
    3736     /* sanity check */
    3737     Assert(pDisk);
    3738     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3739     Assert(pUuid);
    3740 
    3741     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3742     Assert(pImage);
    3743 
    3744     if (pImage)
    3745     {
    3746         *pUuid = *getImageModificationUUID(&pImage->Header);
    3747         LogFlow(("VDIDiskGetImageModificationUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3748                  pUuid, nImage));
    3749         return VINF_SUCCESS;
    3750     }
    3751 
    3752     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3753     return VERR_VDI_IMAGE_NOT_FOUND;
    3754 }
    3755 
    3756 /**
    3757  * Get Uuid of opened image's parent image.
    3758  *
    3759  * @returns VBox status code.
    3760  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3761  * @param   pDisk           Pointer to VDI HDD container.
    3762  * @param   nImage          Image number, counts from 0. 0 is always base image of the container.
    3763  * @param   pUuid           Where to store the image creation uuid.
    3764  */
    3765 IDER3DECL(int) VDIDiskGetParentImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3766 {
    3767     /* sanity check */
    3768     Assert(pDisk);
    3769     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3770     Assert(pUuid);
    3771 
    3772     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3773     if (pImage)
    3774     {
    3775         *pUuid = *getImageParentUUID(&pImage->Header);
    3776         LogFlow(("VDIDiskGetParentImageUuid: returns VINF_SUCCESS, *pUuid={%Vuuid} nImage=%d\n",
    3777                  pUuid, nImage));
    3778         return VINF_SUCCESS;
    3779     }
    3780 
    3781     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3782     return VERR_VDI_IMAGE_NOT_FOUND;
    3783 }
    3784 
    3785 /**
    3786  * internal: Relock image as read/write or read-only.
    3787  */
    3788 static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
    3789 {
    3790     Assert(pImage);
    3791 
    3792     if (    !fReadOnly
    3793         &&  pImage->fOpen & VDI_OPEN_FLAGS_READONLY)
    3794     {
    3795         /* Can't switch read-only opened image to read-write mode. */
    3796         Log(("vdiChangeImageMode: can't switch r/o image to r/w mode, filename=\"%s\" fOpen=%X\n",
    3797              pImage->szFilename, pImage->fOpen));
    3798         return VERR_VDI_IMAGE_READ_ONLY;
    3799     }
    3800 
    3801     /* Flush last image changes if was r/w mode. */
    3802     vdiFlushImage(pImage);
    3803 
    3804     /* Change image locking. */
    3805     uint64_t cbLock = pImage->offStartData
    3806                     + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    3807     int rc = RTFileChangeLock(pImage->File,
    3808                               (fReadOnly) ?
    3809                                   RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    3810                                   RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    3811                               0,
    3812                               cbLock);
    3813     if (VBOX_SUCCESS(rc))
    3814     {
    3815         pImage->fReadOnly = fReadOnly;
    3816         Log(("vdiChangeImageMode: Image \"%s\" mode changed to %s\n",
    3817              pImage->szFilename, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3818         return VINF_SUCCESS;
    3819     }
    3820 
    3821     /* Check for the most bad error in the world. Damn! It must never happens in real life! */
    3822     if (rc == VERR_FILE_LOCK_LOST)
    3823     {
    3824         /* And what we can do now?! */
    3825         AssertMsgFailed(("Image lock has been lost for file=\"%s\"", pImage->szFilename));
    3826         Log(("vdiChangeImageMode: image lock has been lost for file=\"%s\", blocking on r/o lock wait",
    3827              pImage->szFilename));
    3828 
    3829         /* Try to obtain read lock in blocking mode. Maybe it's a very bad method. */
    3830         rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_WAIT, 0, cbLock);
    3831         AssertReleaseRC(rc);
    3832 
    3833         pImage->fReadOnly = false;
    3834         if (pImage->fReadOnly != fReadOnly)
    3835             rc = VERR_FILE_LOCK_VIOLATION;
    3836     }
    3837 
    3838     Log(("vdiChangeImageMode: Image \"%s\" mode change failed with rc=%Vrc, mode is %s\n",
    3839          pImage->szFilename, rc, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3840 
    3841     return rc;
    3842 }
    3843 
    3844 /**
    3845  * internal: try to save header in image file even if image is in read-only mode.
    3846  */
    3847 static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage)
    3848 {
    3849     int rc = VINF_SUCCESS;
    3850 
    3851     if (pImage->fReadOnly)
    3852     {
    3853         rc = vdiChangeImageMode(pImage, false);
    3854         if (VBOX_SUCCESS(rc))
    3855         {
    3856             vdiFlushImage(pImage);
    3857             rc = vdiChangeImageMode(pImage, true);
    3858             AssertReleaseRC(rc);
    3859         }
    3860     }
    3861     else
    3862         vdiFlushImage(pImage);
    3863 
    3864     return rc;
    3865 }
    3866 
    3867 /**
    3868  * Opens an image file.
    3869  *
    3870  * The first opened image file in a HDD container must have a base image type,
    3871  * others (next opened images) must be a differencing or undo images.
    3872  * Linkage is checked for differencing image to be in consistence with the previously opened image.
    3873  * When a next differencing image is opened and the last image was opened in read/write access
    3874  * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
    3875  * other processes to use images in read-only mode too.
    3876  *
    3877  * Note that the image can be opened in read-only mode if a read/write open is not possible.
    3878  * Use VDIDiskIsReadOnly to check open mode.
    3879  *
    3880  * @returns VBox status code.
    3881  * @param   pDisk           Pointer to VDI HDD container.
    3882  * @param   pszFilename     Name of the image file to open.
    3883  * @param   fOpen           Image file open mode, see VDI_OPEN_FLAGS_* constants.
    3884  */
    3885 IDER3DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
    3886 {
    3887     /* sanity check */
    3888     Assert(pDisk);
    3889     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3890 
    3891     /* Check arguments. */
    3892     if (    !pszFilename
    3893         ||  *pszFilename == '\0'
    3894         ||  (fOpen & ~VDI_OPEN_FLAGS_MASK))
    3895     {
    3896         AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
    3897         return VERR_INVALID_PARAMETER;
    3898     }
    3899     LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
    3900 
    3901     PVDIIMAGEDESC pImage;
    3902     int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
    3903     if (VBOX_SUCCESS(rc))
    3904     {
    3905         if (pDisk->pLast)
    3906         {
    3907             /* Opening differencing image. */
    3908             if (!pDisk->pLast->fReadOnly)
    3909             {
    3910                 /*
    3911                  * Previous image is opened in read/write mode -> switch it into read-only.
    3912                  */
    3913                 rc = vdiChangeImageMode(pDisk->pLast, true);
    3914             }
    3915         }
    3916         else
    3917         {
    3918             /* Opening base image, check its type. */
    3919             if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
    3920                 &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
    3921             {
    3922                 rc = VERR_VDI_INVALID_TYPE;
    3923             }
    3924         }
    3925 
    3926         if (VBOX_SUCCESS(rc))
    3927             vdiAddImageToList(pDisk, pImage);
    3928         else
    3929             vdiCloseImage(pImage);
    3930     }
    3931 
    3932     LogFlow(("VDIDiskOpenImage: returns %Vrc\n", rc));
    3933     return rc;
    3934 }
    3935 
    3936 /**
    3937  * Closes the last opened image file in the HDD container. Leaves all changes inside it.
    3938  * If previous image file was opened in read-only mode (that is normal) and closing image
    3939  * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
    3940  * will be reopened in read/write mode.
    3941  *
    3942  * @param   pDisk           Pointer to VDI HDD container.
    3943  */
    3944 IDER3DECL(void) VDIDiskCloseImage(PVDIDISK pDisk)
    3945 {
    3946     /* sanity check */
    3947     Assert(pDisk);
    3948     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3949 
    3950     PVDIIMAGEDESC pImage = pDisk->pLast;
    3951     if (pImage)
    3952     {
    3953         LogFlow(("VDIDiskCloseImage: closing image \"%s\"\n", pImage->szFilename));
    3954 
    3955         bool fWasReadOnly = pImage->fReadOnly;
    3956         vdiRemoveImageFromList(pDisk, pImage);
    3957         vdiCloseImage(pImage);
    3958 
    3959         if (    !fWasReadOnly
    3960             &&  pDisk->pLast
    3961             &&  pDisk->pLast->fReadOnly
    3962             &&  !(pDisk->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    3963         {
    3964             /*
    3965              * Closed image was opened in read/write mode, previous image was opened
    3966              * in read-only mode, try to switch it into read/write.
    3967              */
    3968             int rc = vdiChangeImageMode(pDisk->pLast, false);
    3969             NOREF(rc); /* gcc still hates unused variables... */
    3970         }
    3971 
    3972         return;
    3973     }
    3974     AssertMsgFailed(("No images to close\n"));
    3975 }
    3976 
    3977 /**
    3978  * Closes all opened image files in HDD container.
    3979  *
    3980  * @param   pDisk           Pointer to VDI HDD container.
    3981  */
    3982 IDER3DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
    3983 {
    3984     LogFlow(("VDIDiskCloseAllImages:\n"));
    3985     /* sanity check */
    3986     Assert(pDisk);
    3987     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3988 
    3989     PVDIIMAGEDESC pImage = pDisk->pLast;
    3990     while (pImage)
    3991     {
    3992         PVDIIMAGEDESC pPrev = pImage->pPrev;
    3993         vdiRemoveImageFromList(pDisk, pImage);
    3994         vdiCloseImage(pImage);
    3995         pImage = pPrev;
    3996     }
    3997     Assert(pDisk->pLast == NULL);
    3998 }
    3999 
    4000 /**
    4001  * Commits last opened differencing/undo image file of HDD container to previous one.
    4002  * If previous image file was opened in read-only mode (that must be always so) it is reopened
    4003  * as read/write to do commit operation.
    4004  * After successfull commit the previous image file again reopened in read-only mode, last opened
    4005  * image file is cleared of data and remains open and active in HDD container.
    4006  * If you want to delete image after commit you must do it manually by VDIDiskCloseImage and
    4007  * VDIDeleteImage calls.
    4008  *
    4009  * Note that in case of unrecoverable error all images of HDD container will be closed.
    4010  *
    4011  * @returns VBox status code.
    4012  * @param   pDisk           Pointer to VDI HDD container.
    4013  * @param   pfnProgress     Progress callback. Optional.
    4014  * @param   pvUser          User argument for the progress callback.
    4015  * @remark  Only used by tstVDI.
    4016  */
    4017 IDER3DECL(int) VDIDiskCommitLastDiff(PVDIDISK pDisk, PFNVMPROGRESS pfnProgress, void *pvUser)
    4018 {
    4019     LogFlow(("VDIDiskCommitLastDiff:\n"));
    4020     /* sanity check */
    4021     Assert(pDisk);
    4022     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4023 
    4024     int rc = VINF_SUCCESS;
    4025     PVDIIMAGEDESC pImage = pDisk->pLast;
    4026     if (!pImage)
    4027     {
    4028         AssertMsgFailed(("No one disk image is opened!\n"));
    4029         return VERR_VDI_NOT_OPENED;
    4030     }
    4031 
    4032     if (pImage->fReadOnly)
    4033     {
    4034         AssertMsgFailed(("Image \"%s\" is read-only!\n", pImage->szFilename));
    4035         return VERR_VDI_IMAGE_READ_ONLY;
    4036     }
    4037 
    4038     if (!pImage->pPrev)
    4039     {
    4040         AssertMsgFailed(("No images to commit to!\n"));
    4041         return VERR_VDI_NO_DIFF_IMAGES;
    4042     }
    4043 
    4044     bool fWasReadOnly = pImage->pPrev->fReadOnly;
    4045     if (fWasReadOnly)
    4046     {
    4047         /* Change previous image mode to r/w. */
    4048         rc = vdiChangeImageMode(pImage->pPrev, false);
    4049         if (VBOX_FAILURE(rc))
    4050         {
    4051             Log(("VDIDiskCommitLastDiff: can't switch previous image into r/w mode, rc=%Vrc\n", rc));
    4052             return rc;
    4053         }
    4054     }
    4055 
    4056     rc = vdiCommitToImage(pDisk, pImage->pPrev, pfnProgress, pvUser);
    4057     if (VBOX_SUCCESS(rc) && fWasReadOnly)
    4058     {
    4059         /* Change previous image mode back to r/o. */
    4060         rc = vdiChangeImageMode(pImage->pPrev, true);
    4061     }
    4062 
    4063     if (VBOX_FAILURE(rc))
    4064     {
    4065         /* Failed! Close all images, can't work with VHDD at all. */
    4066         VDIDiskCloseAllImages(pDisk);
    4067         AssertMsgFailed(("Fatal: commit failed, rc=%Vrc\n", rc));
    4068     }
    4069 
    4070     return rc;
    4071 }
    4072 
    4073 /**
    4074  * Creates and opens a new differencing image file in HDD container.
    4075  * See comments for VDIDiskOpenImage function about differencing images.
    4076  *
    4077  * @returns VBox status code.
    4078  * @param   pDisk           Pointer to VDI HDD container.
    4079  * @param   pszFilename     Name of the image file to create and open.
    4080  * @param   pszComment      Pointer to image comment. NULL is ok.
    4081  * @param   pfnProgress     Progress callback. Optional.
    4082  * @param   pvUser          User argument for the progress callback.
    4083  * @remark  Only used by tstVDI.
    4084  */
    4085 IDER3DECL(int) VDIDiskCreateOpenDifferenceImage(PVDIDISK pDisk, const char *pszFilename,
    4086                                                 const char *pszComment, PFNVMPROGRESS pfnProgress,
    4087                                                 void *pvUser)
    4088 {
    4089     LogFlow(("VDIDiskCreateOpenDifferenceImage:\n"));
    4090     /* sanity check */
    4091     Assert(pDisk);
    4092     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4093     Assert(pszFilename);
    4094 
    4095     if (!pDisk->pLast)
    4096     {
    4097         AssertMsgFailed(("No one disk image is opened!\n"));
    4098         return VERR_VDI_NOT_OPENED;
    4099     }
    4100 
    4101     /* Flush last parent image changes if possible. */
    4102     vdiFlushImage(pDisk->pLast);
    4103 
    4104     int rc = vdiCreateImage(pszFilename,
    4105                             VDI_IMAGE_TYPE_DIFF,
    4106                             VDI_IMAGE_FLAGS_DEFAULT,
    4107                             getImageDiskSize(&pDisk->pLast->Header),
    4108                             pszComment,
    4109                             pDisk->pLast,
    4110                             pfnProgress, pvUser);
    4111     if (VBOX_SUCCESS(rc))
    4112     {
    4113         rc = VDIDiskOpenImage(pDisk, pszFilename, VDI_OPEN_FLAGS_NORMAL);
    4114         if (VBOX_FAILURE(rc))
    4115             VDIDeleteImage(pszFilename);
    4116     }
    4117     LogFlow(("VDIDiskCreateOpenDifferenceImage: returns %Vrc, filename=\"%s\"\n", rc, pszFilename));
    4118     return rc;
    4119 }
    4120 
    4121 /**
    4122  * internal: debug image dump.
    4123  *
    4124  * @remark  Only used by tstVDI.
    4125  */
    4126 static void vdiDumpImage(PVDIIMAGEDESC pImage)
    4127 {
    4128     RTLogPrintf("Dumping VDI image \"%s\" mode=%s fOpen=%X File=%08X\n",
    4129                 pImage->szFilename,
    4130                 (pImage->fReadOnly) ? "r/o" : "r/w",
    4131                 pImage->fOpen,
    4132                 pImage->File);
    4133     RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
    4134                 pImage->PreHeader.u32Version,
    4135                 getImageType(&pImage->Header),
    4136                 getImageFlags(&pImage->Header),
    4137                 getImageDiskSize(&pImage->Header));
    4138     RTLogPrintf("Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
    4139                 getImageBlockSize(&pImage->Header),
    4140                 getImageExtraBlockSize(&pImage->Header),
    4141                 getImageBlocks(&pImage->Header),
    4142                 getImageBlocksAllocated(&pImage->Header));
    4143     RTLogPrintf("Header: offBlocks=%u offData=%u\n",
    4144                 getImageBlocksOffset(&pImage->Header),
    4145                 getImageDataOffset(&pImage->Header));
    4146     PVDIDISKGEOMETRY pg = getImageGeometry(&pImage->Header);
    4147     RTLogPrintf("Header: Geometry: C/H/S=%u/%u/%u cbSector=%u Mode=%u\n",
    4148                 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector,
    4149                 getImageTranslation(&pImage->Header));
    4150     RTLogPrintf("Header: uuidCreation={%Vuuid}\n", getImageCreationUUID(&pImage->Header));
    4151     RTLogPrintf("Header: uuidModification={%Vuuid}\n", getImageModificationUUID(&pImage->Header));
    4152     RTLogPrintf("Header: uuidParent={%Vuuid}\n", getImageParentUUID(&pImage->Header));
    4153     if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
    4154         RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
    4155     RTLogPrintf("Image:  fFlags=%08X offStartBlocks=%u offStartData=%u\n",
    4156                 pImage->fFlags, pImage->offStartBlocks, pImage->offStartData);
    4157     RTLogPrintf("Image:  uBlockMask=%08X uShiftIndex2Offset=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
    4158                 pImage->uBlockMask,
    4159                 pImage->uShiftIndex2Offset,
    4160                 pImage->uShiftOffset2Index,
    4161                 pImage->offStartBlockData);
    4162 
    4163     unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
    4164     for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
    4165     {
    4166         if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    4167         {
    4168             cBlocksNotFree++;
    4169             if (pImage->paBlocks[uBlock] >= cBlocks)
    4170                 cBadBlocks++;
    4171         }
    4172     }
    4173     if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
    4174     {
    4175         RTLogPrintf("!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
    4176                 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
    4177     }
    4178     if (cBadBlocks)
    4179     {
    4180         RTLogPrintf("!! WARNING: %u bad blocks found !!\n",
    4181                 cBadBlocks);
    4182     }
    4183 }
    4184 
    4185 /**
    4186  * Debug helper - dumps all opened images of HDD container into the log file.
    4187  *
    4188  * @param   pDisk           Pointer to VDI HDD container.
    4189  * @remark  Only used by tstVDI and vditool
    4190  */
    4191 IDER3DECL(void) VDIDiskDumpImages(PVDIDISK pDisk)
    4192 {
    4193     /* sanity check */
    4194     Assert(pDisk);
    4195     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4196 
    4197     RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
    4198     for (PVDIIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
    4199         vdiDumpImage(pImage);
    4200 }
    4201 
    4202 
    4203 /*******************************************************************************
    4204 *   PDM interface                                                              *
    4205 *******************************************************************************/
     259}
     260
     261
     262/**
     263 * Destruct a driver instance.
     264 *
     265 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
     266 * resources can be freed correctly.
     267 *
     268 * @param   pDrvIns     The driver instance data.
     269 */
     270static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
     271{
     272    LogFlow(("vdiDestruct:\n"));
     273    PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
     274    VDIDiskCloseAllImages(pData);
     275}
     276
    4206277
    4207278/**
     
    4329400
    4330401    return rc;
    4331 }
    4332 
    4333 /**
    4334  * Destruct a driver instance.
    4335  *
    4336  * Most VM resources are freed by the VM. This callback is provided so that any non-VM
    4337  * resources can be freed correctly.
    4338  *
    4339  * @param   pDrvIns     The driver instance data.
    4340  */
    4341 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
    4342 {
    4343     LogFlow(("vdiDestruct:\n"));
    4344     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4345     VDIDiskCloseAllImages(pData);
    4346 }
    4347 
    4348 /**
    4349  * When the VM has been suspended we'll change the image mode to read-only
    4350  * so that main and others can read the VDIs. This is important when
    4351  * saving state and so forth.
    4352  *
    4353  * @param   pDrvIns     The driver instance data.
    4354  */
    4355 static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
    4356 {
    4357     LogFlow(("vdiSuspend:\n"));
    4358 #if 1 // #ifdef DEBUG_dmik
    4359     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4360     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4361     {
    4362         int rc = vdiChangeImageMode(pData->pLast, true);
    4363         AssertRC(rc);
    4364     }
    4365 #endif
    4366 }
    4367 
    4368 /**
    4369  * Before the VM resumes we'll have to undo the read-only mode change
    4370  * done in vdiSuspend.
    4371  *
    4372  * @param   pDrvIns     The driver instance data.
    4373  */
    4374 static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
    4375 {
    4376     LogFlow(("vdiSuspend:\n"));
    4377 #if 1 //#ifdef DEBUG_dmik
    4378     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4379     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4380     {
    4381         int rc = vdiChangeImageMode(pData->pLast, false);
    4382         AssertRC(rc);
    4383     }
    4384 #endif
    4385 }
    4386 
    4387 
    4388 /** @copydoc PDMIMEDIA::pfnGetSize */
    4389 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
    4390 {
    4391     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4392     uint64_t cb = VDIDiskGetSize(pData);
    4393     LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
    4394     return cb;
    4395 }
    4396 
    4397 /**
    4398  * Get stored media geometry - BIOS property.
    4399  *
    4400  * @see PDMIMEDIA::pfnBiosGetGeometry for details.
    4401  */
    4402 static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
    4403 {
    4404     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4405     int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
    4406     if (VBOX_SUCCESS(rc))
    4407     {
    4408         LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
    4409         return VINF_SUCCESS;
    4410     }
    4411     Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
    4412     return VERR_PDM_GEOMETRY_NOT_SET;
    4413 }
    4414 
    4415 /**
    4416  * Set stored media geometry - BIOS property.
    4417  *
    4418  * @see PDMIMEDIA::pfnBiosSetGeometry for details.
    4419  */
    4420 static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
    4421 {
    4422     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4423     int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
    4424     LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
    4425     return rc;
    4426 }
    4427 
    4428 /**
    4429  * Read bits.
    4430  *
    4431  * @see PDMIMEDIA::pfnRead for details.
    4432  */
    4433 static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
    4434 {
    4435     LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
    4436     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4437     int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
    4438     if (VBOX_SUCCESS(rc))
    4439         Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
    4440               "%.*Vhxd\n",
    4441               off, pvBuf, cbRead, cbRead, pvBuf));
    4442     LogFlow(("vdiRead: returns %Vrc\n", rc));
    4443     return rc;
    4444 }
    4445 
    4446 /**
    4447  * Write bits.
    4448  *
    4449  * @see PDMIMEDIA::pfnWrite for details.
    4450  */
    4451 static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
    4452 {
    4453     LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
    4454     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4455     Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
    4456           "%.*Vhxd\n",
    4457           off, pvBuf, cbWrite, cbWrite, pvBuf));
    4458     int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
    4459     LogFlow(("vdiWrite: returns %Vrc\n", rc));
    4460     return rc;
    4461 }
    4462 
    4463 /**
    4464  * Flush bits to media.
    4465  *
    4466  * @see PDMIMEDIA::pfnFlush for details.
    4467  */
    4468 static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
    4469 {
    4470     LogFlow(("vdiFlush:\n"));
    4471     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4472     vdiFlushImage(pData->pLast);
    4473     int rc = VINF_SUCCESS;
    4474     LogFlow(("vdiFlush: returns %Vrc\n", rc));
    4475     return rc;
    4476 }
    4477 
    4478 /** @copydoc PDMIMEDIA::pfnGetUuid */
    4479 static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
    4480 {
    4481     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4482     int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
    4483     LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
    4484     return rc;
    4485 }
    4486 
    4487 /** @copydoc PDMIMEDIA::pfnIsReadOnly */
    4488 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
    4489 {
    4490     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4491     LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
    4492     return VDIDiskIsReadOnly(pData);
    4493 }
    4494 
    4495 /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
    4496 static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    4497                                                PPDMBIOSTRANSLATION penmTranslation)
    4498 {
    4499     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4500     int rc = VDIDiskGetTranslation(pData, penmTranslation);
    4501     LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
    4502     return rc;
    4503 }
    4504 
    4505 /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
    4506 static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    4507                                                PDMBIOSTRANSLATION enmTranslation)
    4508 {
    4509     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4510     int rc = VDIDiskSetTranslation(pData, enmTranslation);
    4511     LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
    4512     return rc;
    4513 }
    4514 
    4515 
    4516 /**
    4517  * Queries an interface to the driver.
    4518  *
    4519  * @returns Pointer to interface.
    4520  * @returns NULL if the interface was not supported by the driver.
    4521  * @param   pInterface          Pointer to this interface structure.
    4522  * @param   enmInterface        The requested interface identification.
    4523  * @thread  Any thread.
    4524  */
    4525 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
    4526 {
    4527     PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
    4528     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4529     switch (enmInterface)
    4530     {
    4531         case PDMINTERFACE_BASE:
    4532             return &pDrvIns->IBase;
    4533         case PDMINTERFACE_MEDIA:
    4534             return &pData->IMedia;
    4535         default:
    4536             return NULL;
    4537     }
    4538402}
    4539403
     
    4575439    NULL
    4576440};
     441
  • trunk/src/VBox/Devices/Storage/VDICore.cpp

    r1562 r1565  
    11/** @file
    2  *
    3  * VBox storage devices:
    4  * VBox HDD container implementation
     2 * Virtual Disk Image (VDI), Core Code.
    53 */
    64
     
    2624#define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
    2725#include <VBox/VBoxHDD.h>
    28 #include <VBox/pdm.h>
    29 #include <VBox/mm.h>
     26#include "VDICore.h"
    3027#include <VBox/err.h>
    3128
     
    3734#include <iprt/string.h>
    3835#include <iprt/asm.h>
    39 
    40 #include "Builtins.h"
    41 
    42 /*******************************************************************************
    43 *   Constants And Macros, Structures and Typedefs                              *
    44 *******************************************************************************/
    45 /** The Sector size.
    46  * Currently we support only 512 bytes sectors.
    47  */
    48 #define VDI_GEOMETRY_SECTOR_SIZE    (512)
    49 /**  512 = 2^^9 */
    50 #define VDI_GEOMETRY_SECTOR_SHIFT   (9)
    51 
    52 /**
    53  * Harddisk geometry.
    54  */
    55 #pragma pack(1)
    56 typedef struct VDIDISKGEOMETRY
    57 {
    58     /** Cylinders. */
    59     uint32_t    cCylinders;
    60     /** Heads. */
    61     uint32_t    cHeads;
    62     /** Sectors per track. */
    63     uint32_t    cSectors;
    64     /** Sector size. (bytes per sector) */
    65     uint32_t    cbSector;
    66 } VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
    67 #pragma pack()
    68 
    69 /** Image signature. */
    70 #define VDI_IMAGE_SIGNATURE   (0xbeda107f)
    71 
    72 /**
    73  * Pre-Header to be stored in image file - used for version control.
    74  */
    75 #pragma pack(1)
    76 typedef struct VDIPREHEADER
    77 {
    78     /** Just text info about image type, for eyes only. */
    79     char            szFileInfo[64];
    80     /** The image signature (VDI_IMAGE_SIGNATURE). */
    81     uint32_t        u32Signature;
    82     /** The image version (VDI_IMAGE_VERSION). */
    83     uint32_t        u32Version;
    84 } VDIPREHEADER, *PVDIPREHEADER;
    85 #pragma pack()
    86 
    87 /**
    88  * Size of szComment field of HDD image header.
    89  */
    90 #define VDI_IMAGE_COMMENT_SIZE    256
    91 
    92 /**
    93  * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
    94  * Prepended by VDIPREHEADER.
    95  */
    96 #pragma pack(1)
    97 typedef struct VDIHEADER0
    98 {
    99     /** The image type (VDI_IMAGE_TYPE_*). */
    100     uint32_t        u32Type;
    101     /** Image flags (VDI_IMAGE_FLAGS_*). */
    102     uint32_t        fFlags;
    103     /** Image comment. (UTF-8) */
    104     char            szComment[VDI_IMAGE_COMMENT_SIZE];
    105     /** Image geometry. */
    106     VDIDISKGEOMETRY Geometry;
    107     /** Size of disk (in bytes). */
    108     uint64_t        cbDisk;
    109     /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
    110     uint32_t        cbBlock;
    111     /** Number of blocks. */
    112     uint32_t        cBlocks;
    113     /** Number of allocated blocks. */
    114     uint32_t        cBlocksAllocated;
    115     /** UUID of image. */
    116     RTUUID          uuidCreate;
    117     /** UUID of image's last modification. */
    118     RTUUID          uuidModify;
    119     /** Only for secondary images - UUID of primary image. */
    120     RTUUID          uuidLinkage;
    121 } VDIHEADER0, *PVDIHEADER0;
    122 #pragma pack()
    123 
    124 /**
    125  * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1.
    126  * Prepended by VDIPREHEADER.
    127  */
    128 #pragma pack(1)
    129 typedef struct VDIHEADER1
    130 {
    131     /** Size of this structure in bytes. */
    132     uint32_t        cbHeader;
    133     /** The image type (VDI_IMAGE_TYPE_*). */
    134     uint32_t        u32Type;
    135     /** Image flags (VDI_IMAGE_FLAGS_*). */
    136     uint32_t        fFlags;
    137     /** Image comment. (UTF-8) */
    138     char            szComment[VDI_IMAGE_COMMENT_SIZE];
    139     /** Offset of Blocks array from the begining of image file.
    140      * Should be sector-aligned for HDD access optimization. */
    141     uint32_t        offBlocks;
    142     /** Offset of image data from the begining of image file.
    143      * Should be sector-aligned for HDD access optimization. */
    144     uint32_t        offData;
    145     /** Image geometry. */
    146     VDIDISKGEOMETRY Geometry;
    147     /** BIOS HDD translation mode, see PDMBIOSTRANSLATION. */
    148     uint32_t        u32Translation;
    149     /** Size of disk (in bytes). */
    150     uint64_t        cbDisk;
    151     /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
    152     uint32_t        cbBlock;
    153     /** Size of additional service information of every data block.
    154      * Prepended before block data. May be 0.
    155      * Should be a power of 2 and sector-aligned for optimization reasons. */
    156     uint32_t        cbBlockExtra;
    157     /** Number of blocks. */
    158     uint32_t        cBlocks;
    159     /** Number of allocated blocks. */
    160     uint32_t        cBlocksAllocated;
    161     /** UUID of image. */
    162     RTUUID          uuidCreate;
    163     /** UUID of image's last modification. */
    164     RTUUID          uuidModify;
    165     /** Only for secondary images - UUID of previous image. */
    166     RTUUID          uuidLinkage;
    167     /** Only for secondary images - UUID of previous image's last modification. */
    168     RTUUID          uuidParentModify;
    169 } VDIHEADER1, *PVDIHEADER1;
    170 #pragma pack()
    171 
    172 /**
    173  * Header structure for all versions.
    174  */
    175 typedef struct VDIHEADER
    176 {
    177     unsigned        uVersion;
    178     union
    179     {
    180         VDIHEADER0    v0;
    181         VDIHEADER1    v1;
    182     } u;
    183 } VDIHEADER, *PVDIHEADER;
    184 
    185 /** Block 'pointer'. */
    186 typedef uint32_t    VDIIMAGEBLOCKPOINTER;
    187 /** Pointer to a block 'pointer'. */
    188 typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
    189 
    190 /**
    191  * Block marked as free is not allocated in image file, read from this
    192  * block may returns any random data.
    193  */
    194 #define VDI_IMAGE_BLOCK_FREE   ((VDIIMAGEBLOCKPOINTER)~0)
    195 
    196 /**
    197  * Block marked as zero is not allocated in image file, read from this
    198  * block returns zeroes.
    199  */
    200 #define VDI_IMAGE_BLOCK_ZERO   ((VDIIMAGEBLOCKPOINTER)~1)
    201 
    202 /**
    203  * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
    204  * allocated in image file.
    205  */
    206 #define VDI_IMAGE_BLOCK_UNALLOCATED   (VDI_IMAGE_BLOCK_ZERO)
    207 #define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp)   (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
    208 
    209 #define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
    210 #define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
    211 
    212 /*******************************************************************************
    213 *   Internal Functions for header access                                       *
    214 *******************************************************************************/
    215 static inline VDIIMAGETYPE getImageType(PVDIHEADER ph)
    216 {
    217     switch (GET_MAJOR_HEADER_VERSION(ph))
    218     {
    219         case 0: return (VDIIMAGETYPE)ph->u.v0.u32Type;
    220         case 1: return (VDIIMAGETYPE)ph->u.v1.u32Type;
    221     }
    222     AssertFailed();
    223     return (VDIIMAGETYPE)0;
    224 }
    225 
    226 static inline unsigned getImageFlags(PVDIHEADER ph)
    227 {
    228     switch (GET_MAJOR_HEADER_VERSION(ph))
    229     {
    230         case 0: return ph->u.v0.fFlags;
    231         case 1: return ph->u.v1.fFlags;
    232     }
    233     AssertFailed();
    234     return 0;
    235 }
    236 
    237 static inline char *getImageComment(PVDIHEADER ph)
    238 {
    239     switch (GET_MAJOR_HEADER_VERSION(ph))
    240     {
    241         case 0: return &ph->u.v0.szComment[0];
    242         case 1: return &ph->u.v1.szComment[0];
    243     }
    244     AssertFailed();
    245     return NULL;
    246 }
    247 
    248 static inline unsigned getImageBlocksOffset(PVDIHEADER ph)
    249 {
    250     switch (GET_MAJOR_HEADER_VERSION(ph))
    251     {
    252         case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
    253         case 1: return ph->u.v1.offBlocks;
    254     }
    255     AssertFailed();
    256     return 0;
    257 }
    258 
    259 static inline unsigned getImageDataOffset(PVDIHEADER ph)
    260 {
    261     switch (GET_MAJOR_HEADER_VERSION(ph))
    262     {
    263         case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
    264                        (ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
    265         case 1: return ph->u.v1.offData;
    266     }
    267     AssertFailed();
    268     return 0;
    269 }
    270 
    271 static inline PVDIDISKGEOMETRY getImageGeometry(PVDIHEADER ph)
    272 {
    273     switch (GET_MAJOR_HEADER_VERSION(ph))
    274     {
    275         case 0: return &ph->u.v0.Geometry;
    276         case 1: return &ph->u.v1.Geometry;
    277     }
    278     AssertFailed();
    279     return NULL;
    280 }
    281 
    282 static inline PDMBIOSTRANSLATION getImageTranslation(PVDIHEADER ph)
    283 {
    284     switch (GET_MAJOR_HEADER_VERSION(ph))
    285     {
    286         case 0: return PDMBIOSTRANSLATION_AUTO;
    287         case 1: return (PDMBIOSTRANSLATION)ph->u.v1.u32Translation;
    288     }
    289     AssertFailed();
    290     return PDMBIOSTRANSLATION_NONE;
    291 }
    292 
    293 static inline void setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)
    294 {
    295     switch (GET_MAJOR_HEADER_VERSION(ph))
    296     {
    297         case 0:                                                     return;
    298         case 1: ph->u.v1.u32Translation = (uint32_t)enmTranslation; return;
    299     }
    300     AssertFailed();
    301 }
    302 
    303 static inline uint64_t getImageDiskSize(PVDIHEADER ph)
    304 {
    305     switch (GET_MAJOR_HEADER_VERSION(ph))
    306     {
    307         case 0: return ph->u.v0.cbDisk;
    308         case 1: return ph->u.v1.cbDisk;
    309     }
    310     AssertFailed();
    311     return 0;
    312 }
    313 
    314 static inline unsigned getImageBlockSize(PVDIHEADER ph)
    315 {
    316     switch (GET_MAJOR_HEADER_VERSION(ph))
    317     {
    318         case 0: return ph->u.v0.cbBlock;
    319         case 1: return ph->u.v1.cbBlock;
    320     }
    321     AssertFailed();
    322     return 0;
    323 }
    324 
    325 static inline unsigned getImageExtraBlockSize(PVDIHEADER ph)
    326 {
    327     switch (GET_MAJOR_HEADER_VERSION(ph))
    328     {
    329         case 0: return 0;
    330         case 1: return ph->u.v1.cbBlockExtra;
    331     }
    332     AssertFailed();
    333     return 0;
    334 }
    335 
    336 static inline unsigned getImageBlocks(PVDIHEADER ph)
    337 {
    338     switch (GET_MAJOR_HEADER_VERSION(ph))
    339     {
    340         case 0: return ph->u.v0.cBlocks;
    341         case 1: return ph->u.v1.cBlocks;
    342     }
    343     AssertFailed();
    344     return 0;
    345 }
    346 
    347 static inline unsigned getImageBlocksAllocated(PVDIHEADER ph)
    348 {
    349     switch (GET_MAJOR_HEADER_VERSION(ph))
    350     {
    351         case 0: return ph->u.v0.cBlocksAllocated;
    352         case 1: return ph->u.v1.cBlocksAllocated;
    353     }
    354     AssertFailed();
    355     return 0;
    356 }
    357 
    358 static inline void setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
    359 {
    360     switch (GET_MAJOR_HEADER_VERSION(ph))
    361     {
    362         case 0: ph->u.v0.cBlocksAllocated = cBlocks; return;
    363         case 1: ph->u.v1.cBlocksAllocated = cBlocks; return;
    364     }
    365     AssertFailed();
    366 }
    367 
    368 static inline PRTUUID getImageCreationUUID(PVDIHEADER ph)
    369 {
    370     switch (GET_MAJOR_HEADER_VERSION(ph))
    371     {
    372         case 0: return &ph->u.v0.uuidCreate;
    373         case 1: return &ph->u.v1.uuidCreate;
    374     }
    375     AssertFailed();
    376     return NULL;
    377 }
    378 
    379 static inline PRTUUID getImageModificationUUID(PVDIHEADER ph)
    380 {
    381     switch (GET_MAJOR_HEADER_VERSION(ph))
    382     {
    383         case 0: return &ph->u.v0.uuidModify;
    384         case 1: return &ph->u.v1.uuidModify;
    385     }
    386     AssertFailed();
    387     return NULL;
    388 }
    389 
    390 static inline PRTUUID getImageParentUUID(PVDIHEADER ph)
    391 {
    392     switch (GET_MAJOR_HEADER_VERSION(ph))
    393     {
    394         case 0: return &ph->u.v0.uuidLinkage;
    395         case 1: return &ph->u.v1.uuidLinkage;
    396     }
    397     AssertFailed();
    398     return NULL;
    399 }
    400 
    401 static inline PRTUUID getImageParentModificationUUID(PVDIHEADER ph)
    402 {
    403     switch (GET_MAJOR_HEADER_VERSION(ph))
    404     {
    405         case 1: return &ph->u.v1.uuidParentModify;
    406     }
    407     AssertFailed();
    408     return NULL;
    409 }
    410 
    411 /**
    412  * Default image block size, may be changed by setBlockSize/getBlockSize.
    413  *
    414  * Note: for speed reasons block size should be a power of 2 !
    415  */
    416 #define VDI_IMAGE_DEFAULT_BLOCK_SIZE            _1M
    417 
    418 /**
    419  * fModified bit flags.
    420  */
    421 #define VDI_IMAGE_MODIFIED_FLAG                 BIT(0)
    422 #define VDI_IMAGE_MODIFIED_FIRST                BIT(1)
    423 #define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE  BIT(2)
    424 
    425 /**
    426  * Image structure
    427  */
    428 typedef struct VDIIMAGEDESC
    429 {
    430     /** Link to parent image descriptor, if any. */
    431     struct VDIIMAGEDESC    *pPrev;
    432     /** Link to child image descriptor, if any. */
    433     struct VDIIMAGEDESC    *pNext;
    434     /** File handle. */
    435     RTFILE                  File;
    436     /** True if the image is operating in readonly mode. */
    437     bool                    fReadOnly;
    438     /** Image open flags, VDI_OPEN_FLAGS_*. */
    439     unsigned                fOpen;
    440     /** Image pre-header. */
    441     VDIPREHEADER            PreHeader;
    442     /** Image header. */
    443     VDIHEADER               Header;
    444     /** Pointer to a block array. */
    445     PVDIIMAGEBLOCKPOINTER   paBlocks;
    446     /** fFlags copy from image header, for speed optimization. */
    447     unsigned                fFlags;
    448     /** Start offset of block array in image file, here for speed optimization. */
    449     unsigned                offStartBlocks;
    450     /** Start offset of data in image file, here for speed optimization. */
    451     unsigned                offStartData;
    452     /** Block mask for getting the offset into a block from a byte hdd offset. */
    453     unsigned                uBlockMask;
    454     /** Block shift value for converting byte hdd offset into paBlock index. */
    455     unsigned                uShiftOffset2Index;
    456     /** Block shift value for converting block index into offset in image. */
    457     unsigned                uShiftIndex2Offset;
    458     /** Offset of data from the beginning of block. */
    459     unsigned                offStartBlockData;
    460     /** Image is modified flags (VDI_IMAGE_MODIFIED*). */
    461     unsigned                fModified;
    462     /** Container filename. (UTF-8)
    463      * @todo Make this variable length to save a bunch of bytes. (low prio) */
    464     char                    szFilename[RTPATH_MAX];
    465 } VDIIMAGEDESC, *PVDIIMAGEDESC;
    466 
    467 /**
    468  * Default work buffer size, may be changed by setBufferSize() method.
    469  *
    470  * For best speed performance it must be equal to image block size.
    471  */
    472 #define VDIDISK_DEFAULT_BUFFER_SIZE   (VDI_IMAGE_DEFAULT_BLOCK_SIZE)
    473 
    474 /** VDIDISK Signature. */
    475 #define VDIDISK_SIGNATURE (0xbedafeda)
    476 
    477 /**
    478  * VBox HDD Container main structure, private part.
    479  */
    480 struct VDIDISK
    481 {
    482     /** Structure signature (VDIDISK_SIGNATURE). */
    483     uint32_t        u32Signature;
    484 
    485     /** Number of opened images. */
    486     unsigned        cImages;
    487 
    488     /** Base image. */
    489     PVDIIMAGEDESC   pBase;
    490 
    491     /** Last opened image in the chain.
    492      * The same as pBase if only one image is used or the last opened diff image. */
    493     PVDIIMAGEDESC   pLast;
    494 
    495     /** Default block size for newly created images. */
    496     unsigned        cbBlock;
    497 
    498     /** Working buffer size, allocated only while committing data,
    499      * copying block from primary image to secondary and saving previously
    500      * zero block. Buffer deallocated after operation complete.
    501      * @remark  For best performance buffer size must be equal to image's
    502      *          block size, however it may be decreased for memory saving.
    503      */
    504     unsigned        cbBuf;
    505 
    506     /** Flag whether zero writes should be handled normally or optimized
    507      * away if possible. */
    508     bool            fHonorZeroWrites;
    509 
    510     /** The media interface. */
    511     PDMIMEDIA       IMedia;
    512     /** Pointer to the driver instance. */
    513     PPDMDRVINS      pDrvIns;
    514 };
    515 
    516 
    517 /** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */
    518 #define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) )
    519 
    520 /** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
    521 #define PDMIBASE_2_DRVINS(pInterface)   ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
    522 
    523 /** Converts a pointer to PDMDRVINS::IBase to a PVDIDISK. */
    524 #define PDMIBASE_2_VDIDISK(pInterface)  ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVDIDISK) )
    52536
    52637
     
    55162static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    55263#endif
    553 static void vdiFlushImage(PVDIIMAGEDESC pImage);
    55464static void vdiCloseImage(PVDIIMAGEDESC pImage);
    55565static int  vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
     
    56171static int  vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    56272                          PFNVMPROGRESS pfnProgress, void *pvUser);
    563 static void vdiInitVDIDisk(PVDIDISK pDisk);
    56473static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    56574static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    56675static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
    567 static int  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
    56876static int  vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
    56977
     
    57179                            PFNVMPROGRESS pfnProgress, void *pvUser);
    57280static void vdiDumpImage(PVDIIMAGEDESC pImage);
    573 
    574 static DECLCALLBACK(int)  vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
    575 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns);
    576 static DECLCALLBACK(int)  vdiRead(PPDMIMEDIA pInterface,
    577                                   uint64_t off, void *pvBuf, size_t cbRead);
    578 static DECLCALLBACK(int)  vdiWrite(PPDMIMEDIA pInterface,
    579                                    uint64_t off, const void *pvBuf, size_t cbWrite);
    580 static DECLCALLBACK(int)  vdiFlush(PPDMIMEDIA pInterface);
    581 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface);
    582 static DECLCALLBACK(int)  vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
    583                                              uint32_t *pcHeads, uint32_t *pcSectors);
    584 static DECLCALLBACK(int)  vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
    585                                              uint32_t cHeads, uint32_t cSectors);
    586 static DECLCALLBACK(int)  vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
    587 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface);
    588 static DECLCALLBACK(int)  vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    589                                                 PPDMBIOSTRANSLATION penmTranslation);
    590 static DECLCALLBACK(int)  vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    591                                                 PDMBIOSTRANSLATION enmTranslation);
    592 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
    59381
    59482
     
    1384872
    1385873/**
    1386  * internal: flush image file to disk.
    1387  */
    1388 static void vdiFlushImage(PVDIIMAGEDESC pImage)
     874 * Flush the image file to disk.
     875 */
     876void vdiFlushImage(PVDIIMAGEDESC pImage)
    1389877{
    1390878    if (!pImage->fReadOnly)
     
    31702658
    31712659/**
    3172  * internal: initialize VDIDISK structure.
    3173  */
    3174 static void vdiInitVDIDisk(PVDIDISK pDisk)
     2660 * Initialize the VDIDISK structure.
     2661 */
     2662void vdiInitVDIDisk(PVDIDISK pDisk)
    31752663{
    31762664    Assert(pDisk);
     
    37843272
    37853273/**
    3786  * internal: Relock image as read/write or read-only.
    3787  */
    3788 static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
     3274 * Relock the image as read/write or read-only.
     3275 */
     3276int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
    37893277{
    37903278    Assert(pImage);
     
    41993687        vdiDumpImage(pImage);
    42003688}
    4201 
    4202 
    4203 /*******************************************************************************
    4204 *   PDM interface                                                              *
    4205 *******************************************************************************/
    4206 
    4207 /**
    4208  * Construct a VBox HDD media driver instance.
    4209  *
    4210  * @returns VBox status.
    4211  * @param   pDrvIns     The driver instance data.
    4212  *                      If the registration structure is needed, pDrvIns->pDrvReg points to it.
    4213  * @param   pCfgHandle  Configuration node handle for the driver. Use this to obtain the configuration
    4214  *                      of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
    4215  *                      iInstance it's expected to be used a bit in this function.
    4216  */
    4217 static DECLCALLBACK(int) vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
    4218 {
    4219     LogFlow(("vdiConstruct:\n"));
    4220     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4221     char *pszName;      /**< The path of the disk image file. */
    4222     bool fReadOnly;     /**< True if the media is readonly. */
    4223     bool fHonorZeroWrites = false;
    4224 
    4225     /*
    4226      * Init the static parts.
    4227      */
    4228     pDrvIns->IBase.pfnQueryInterface    = vdiQueryInterface;
    4229     pData->pDrvIns = pDrvIns;
    4230 
    4231     vdiInitVDIDisk(pData);
    4232 
    4233     /* IMedia */
    4234     pData->IMedia.pfnRead               = vdiRead;
    4235     pData->IMedia.pfnWrite              = vdiWrite;
    4236     pData->IMedia.pfnFlush              = vdiFlush;
    4237     pData->IMedia.pfnGetSize            = vdiGetSize;
    4238     pData->IMedia.pfnGetUuid            = vdiGetUuid;
    4239     pData->IMedia.pfnIsReadOnly         = vdiIsReadOnly;
    4240     pData->IMedia.pfnBiosGetGeometry    = vdiBiosGetGeometry;
    4241     pData->IMedia.pfnBiosSetGeometry    = vdiBiosSetGeometry;
    4242     pData->IMedia.pfnBiosGetTranslation = vdiBiosGetTranslation;
    4243     pData->IMedia.pfnBiosSetTranslation = vdiBiosSetTranslation;
    4244 
    4245     /*
    4246      * Validate configuration and find the great to the level of umpteen grandparent.
    4247      * The parents are found in the 'Parent' subtree, so it's sorta up side down
    4248      * from the image dependency tree.
    4249      */
    4250     unsigned    iLevel = 0;
    4251     PCFGMNODE   pCurNode = pCfgHandle;
    4252     for (;;)
    4253     {
    4254         if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0ReadOnly\0HonorZeroWrites\0"))
    4255             return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
    4256 
    4257         PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
    4258         if (!pParent)
    4259             break;
    4260         pCurNode = pParent;
    4261         iLevel++;
    4262     }
    4263 
    4264     /*
    4265      * Open the images.
    4266      */
    4267     int rc = VINF_SUCCESS;
    4268     while (pCurNode && VBOX_SUCCESS(rc))
    4269     {
    4270         /*
    4271          * Read the image configuration.
    4272          */
    4273         int rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
    4274         if (VBOX_FAILURE(rc))
    4275             return PDMDRV_SET_ERROR(pDrvIns, rc,
    4276                                     N_("VHDD: Configuration error: Querying \"Path\" as string failed"));
    4277 
    4278         rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
    4279         if (rc == VERR_CFGM_VALUE_NOT_FOUND)
    4280             fReadOnly = false;
    4281         else if (VBOX_FAILURE(rc))
    4282         {
    4283             MMR3HeapFree(pszName);
    4284             return PDMDRV_SET_ERROR(pDrvIns, rc,
    4285                                     N_("VHDD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
    4286         }
    4287 
    4288         if (!fHonorZeroWrites)
    4289         {
    4290             rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
    4291             if (rc == VERR_CFGM_VALUE_NOT_FOUND)
    4292                 fHonorZeroWrites = false;
    4293             else if (VBOX_FAILURE(rc))
    4294             {
    4295                 MMR3HeapFree(pszName);
    4296                 return PDMDRV_SET_ERROR(pDrvIns, rc,
    4297                                         N_("VHDD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
    4298             }
    4299         }
    4300 
    4301         /*
    4302          * Open the image.
    4303          */
    4304         rc = VDIDiskOpenImage(pData, pszName, fReadOnly ? VDI_OPEN_FLAGS_READONLY
    4305                                                         : VDI_OPEN_FLAGS_NORMAL);
    4306         if (VBOX_SUCCESS(rc))
    4307             Log(("vdiConstruct: %d - Opened '%s' in %s mode\n",
    4308                  iLevel, pszName, VDIDiskIsReadOnly(pData) ? "read-only" : "read-write"));
    4309         else
    4310             AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
    4311         MMR3HeapFree(pszName);
    4312 
    4313         /* next */
    4314         iLevel--;
    4315         pCurNode = CFGMR3GetParent(pCurNode);
    4316     }
    4317 
    4318     /* If any of the images has the flag set, handle zero writes like normal. */
    4319     if (VBOX_SUCCESS(rc))
    4320         pData->fHonorZeroWrites = fHonorZeroWrites;
    4321 
    4322     /* On failure, vdiDestruct will be called, so no need to clean up here. */
    4323 
    4324     if (rc == VERR_ACCESS_DENIED)
    4325         /* This should never happen here since this case is covered by Console::PowerUp */
    4326         return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
    4327                                    N_("Cannot open virtual disk image '%s' for %s access"),
    4328                                    pszName, fReadOnly ? "readonly" : "read/write");
    4329 
    4330     return rc;
    4331 }
    4332 
    4333 /**
    4334  * Destruct a driver instance.
    4335  *
    4336  * Most VM resources are freed by the VM. This callback is provided so that any non-VM
    4337  * resources can be freed correctly.
    4338  *
    4339  * @param   pDrvIns     The driver instance data.
    4340  */
    4341 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
    4342 {
    4343     LogFlow(("vdiDestruct:\n"));
    4344     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4345     VDIDiskCloseAllImages(pData);
    4346 }
    4347 
    4348 /**
    4349  * When the VM has been suspended we'll change the image mode to read-only
    4350  * so that main and others can read the VDIs. This is important when
    4351  * saving state and so forth.
    4352  *
    4353  * @param   pDrvIns     The driver instance data.
    4354  */
    4355 static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
    4356 {
    4357     LogFlow(("vdiSuspend:\n"));
    4358 #if 1 // #ifdef DEBUG_dmik
    4359     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4360     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4361     {
    4362         int rc = vdiChangeImageMode(pData->pLast, true);
    4363         AssertRC(rc);
    4364     }
    4365 #endif
    4366 }
    4367 
    4368 /**
    4369  * Before the VM resumes we'll have to undo the read-only mode change
    4370  * done in vdiSuspend.
    4371  *
    4372  * @param   pDrvIns     The driver instance data.
    4373  */
    4374 static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
    4375 {
    4376     LogFlow(("vdiSuspend:\n"));
    4377 #if 1 //#ifdef DEBUG_dmik
    4378     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4379     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4380     {
    4381         int rc = vdiChangeImageMode(pData->pLast, false);
    4382         AssertRC(rc);
    4383     }
    4384 #endif
    4385 }
    4386 
    4387 
    4388 /** @copydoc PDMIMEDIA::pfnGetSize */
    4389 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
    4390 {
    4391     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4392     uint64_t cb = VDIDiskGetSize(pData);
    4393     LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
    4394     return cb;
    4395 }
    4396 
    4397 /**
    4398  * Get stored media geometry - BIOS property.
    4399  *
    4400  * @see PDMIMEDIA::pfnBiosGetGeometry for details.
    4401  */
    4402 static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
    4403 {
    4404     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4405     int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
    4406     if (VBOX_SUCCESS(rc))
    4407     {
    4408         LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
    4409         return VINF_SUCCESS;
    4410     }
    4411     Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
    4412     return VERR_PDM_GEOMETRY_NOT_SET;
    4413 }
    4414 
    4415 /**
    4416  * Set stored media geometry - BIOS property.
    4417  *
    4418  * @see PDMIMEDIA::pfnBiosSetGeometry for details.
    4419  */
    4420 static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
    4421 {
    4422     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4423     int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
    4424     LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
    4425     return rc;
    4426 }
    4427 
    4428 /**
    4429  * Read bits.
    4430  *
    4431  * @see PDMIMEDIA::pfnRead for details.
    4432  */
    4433 static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
    4434 {
    4435     LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
    4436     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4437     int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
    4438     if (VBOX_SUCCESS(rc))
    4439         Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
    4440               "%.*Vhxd\n",
    4441               off, pvBuf, cbRead, cbRead, pvBuf));
    4442     LogFlow(("vdiRead: returns %Vrc\n", rc));
    4443     return rc;
    4444 }
    4445 
    4446 /**
    4447  * Write bits.
    4448  *
    4449  * @see PDMIMEDIA::pfnWrite for details.
    4450  */
    4451 static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
    4452 {
    4453     LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
    4454     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4455     Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
    4456           "%.*Vhxd\n",
    4457           off, pvBuf, cbWrite, cbWrite, pvBuf));
    4458     int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
    4459     LogFlow(("vdiWrite: returns %Vrc\n", rc));
    4460     return rc;
    4461 }
    4462 
    4463 /**
    4464  * Flush bits to media.
    4465  *
    4466  * @see PDMIMEDIA::pfnFlush for details.
    4467  */
    4468 static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
    4469 {
    4470     LogFlow(("vdiFlush:\n"));
    4471     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4472     vdiFlushImage(pData->pLast);
    4473     int rc = VINF_SUCCESS;
    4474     LogFlow(("vdiFlush: returns %Vrc\n", rc));
    4475     return rc;
    4476 }
    4477 
    4478 /** @copydoc PDMIMEDIA::pfnGetUuid */
    4479 static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
    4480 {
    4481     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4482     int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
    4483     LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
    4484     return rc;
    4485 }
    4486 
    4487 /** @copydoc PDMIMEDIA::pfnIsReadOnly */
    4488 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
    4489 {
    4490     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4491     LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
    4492     return VDIDiskIsReadOnly(pData);
    4493 }
    4494 
    4495 /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
    4496 static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    4497                                                PPDMBIOSTRANSLATION penmTranslation)
    4498 {
    4499     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4500     int rc = VDIDiskGetTranslation(pData, penmTranslation);
    4501     LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
    4502     return rc;
    4503 }
    4504 
    4505 /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
    4506 static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    4507                                                PDMBIOSTRANSLATION enmTranslation)
    4508 {
    4509     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4510     int rc = VDIDiskSetTranslation(pData, enmTranslation);
    4511     LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
    4512     return rc;
    4513 }
    4514 
    4515 
    4516 /**
    4517  * Queries an interface to the driver.
    4518  *
    4519  * @returns Pointer to interface.
    4520  * @returns NULL if the interface was not supported by the driver.
    4521  * @param   pInterface          Pointer to this interface structure.
    4522  * @param   enmInterface        The requested interface identification.
    4523  * @thread  Any thread.
    4524  */
    4525 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
    4526 {
    4527     PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
    4528     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4529     switch (enmInterface)
    4530     {
    4531         case PDMINTERFACE_BASE:
    4532             return &pDrvIns->IBase;
    4533         case PDMINTERFACE_MEDIA:
    4534             return &pData->IMedia;
    4535         default:
    4536             return NULL;
    4537     }
    4538 }
    4539 
    4540 
    4541 /**
    4542  * VBox HDD driver registration record.
    4543  */
    4544 const PDMDRVREG g_DrvVBoxHDD =
    4545 {
    4546     /* u32Version */
    4547     PDM_DRVREG_VERSION,
    4548     /* szDriverName */
    4549     "VBoxHDD",
    4550     /* pszDescription */
    4551     "VBoxHDD media driver.",
    4552     /* fFlags */
    4553     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
    4554     /* fClass. */
    4555     PDM_DRVREG_CLASS_MEDIA,
    4556     /* cMaxInstances */
    4557     ~0,
    4558     /* cbInstance */
    4559     sizeof(VDIDISK),
    4560     /* pfnConstruct */
    4561     vdiConstruct,
    4562     /* pfnDestruct */
    4563     vdiDestruct,
    4564     /* pfnIOCtl */
    4565     NULL,
    4566     /* pfnPowerOn */
    4567     NULL,
    4568     /* pfnReset */
    4569     NULL,
    4570     /* pfnSuspend */
    4571     vdiSuspend,
    4572     /* pfnResume */
    4573     vdiResume,
    4574     /* pfnDetach */
    4575     NULL
    4576 };
  • trunk/src/VBox/Devices/Storage/VDICore.h

    r1562 r1565  
     1/** $Id$ */
    12/** @file
    2  *
    3  * VBox storage devices:
    4  * VBox HDD container implementation
     3 * Virtual Disk Image (VDI), Core Code Header (internal).
    54 */
    65
     
    2120 */
    2221
     22#ifndef __VDICore_h__
     23
     24
    2325/*******************************************************************************
    2426*   Header Files                                                               *
    2527*******************************************************************************/
    26 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
    2728#include <VBox/VBoxHDD.h>
    2829#include <VBox/pdm.h>
     
    3839#include <iprt/asm.h>
    3940
    40 #include "Builtins.h"
    4141
    4242/*******************************************************************************
     
    210210#define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
    211211
     212
    212213/*******************************************************************************
    213214*   Internal Functions for header access                                       *
    214215*******************************************************************************/
    215 static inline VDIIMAGETYPE getImageType(PVDIHEADER ph)
     216DECLINLINE(VDIIMAGETYPE) getImageType(PVDIHEADER ph)
    216217{
    217218    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    224225}
    225226
    226 static inline unsigned getImageFlags(PVDIHEADER ph)
     227DECLINLINE(unsigned) getImageFlags(PVDIHEADER ph)
    227228{
    228229    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    235236}
    236237
    237 static inline char *getImageComment(PVDIHEADER ph)
     238DECLINLINE(char *) getImageComment(PVDIHEADER ph)
    238239{
    239240    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    246247}
    247248
    248 static inline unsigned getImageBlocksOffset(PVDIHEADER ph)
     249DECLINLINE(unsigned) getImageBlocksOffset(PVDIHEADER ph)
    249250{
    250251    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    257258}
    258259
    259 static inline unsigned getImageDataOffset(PVDIHEADER ph)
     260DECLINLINE(unsigned) getImageDataOffset(PVDIHEADER ph)
    260261{
    261262    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    269270}
    270271
    271 static inline PVDIDISKGEOMETRY getImageGeometry(PVDIHEADER ph)
     272DECLINLINE(PVDIDISKGEOMETRY) getImageGeometry(PVDIHEADER ph)
    272273{
    273274    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    280281}
    281282
    282 static inline PDMBIOSTRANSLATION getImageTranslation(PVDIHEADER ph)
     283DECLINLINE(PDMBIOSTRANSLATION) getImageTranslation(PVDIHEADER ph)
    283284{
    284285    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    291292}
    292293
    293 static inline void setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)
     294DECLINLINE(void) setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)
    294295{
    295296    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    301302}
    302303
    303 static inline uint64_t getImageDiskSize(PVDIHEADER ph)
     304DECLINLINE(uint64_t) getImageDiskSize(PVDIHEADER ph)
    304305{
    305306    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    312313}
    313314
    314 static inline unsigned getImageBlockSize(PVDIHEADER ph)
     315DECLINLINE(unsigned) getImageBlockSize(PVDIHEADER ph)
    315316{
    316317    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    323324}
    324325
    325 static inline unsigned getImageExtraBlockSize(PVDIHEADER ph)
     326DECLINLINE(unsigned) getImageExtraBlockSize(PVDIHEADER ph)
    326327{
    327328    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    334335}
    335336
    336 static inline unsigned getImageBlocks(PVDIHEADER ph)
     337DECLINLINE(unsigned) getImageBlocks(PVDIHEADER ph)
    337338{
    338339    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    345346}
    346347
    347 static inline unsigned getImageBlocksAllocated(PVDIHEADER ph)
     348DECLINLINE(unsigned) getImageBlocksAllocated(PVDIHEADER ph)
    348349{
    349350    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    356357}
    357358
    358 static inline void setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
     359DECLINLINE(void) setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
    359360{
    360361    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    366367}
    367368
    368 static inline PRTUUID getImageCreationUUID(PVDIHEADER ph)
     369DECLINLINE(PRTUUID) getImageCreationUUID(PVDIHEADER ph)
    369370{
    370371    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    377378}
    378379
    379 static inline PRTUUID getImageModificationUUID(PVDIHEADER ph)
     380DECLINLINE(PRTUUID) getImageModificationUUID(PVDIHEADER ph)
    380381{
    381382    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    388389}
    389390
    390 static inline PRTUUID getImageParentUUID(PVDIHEADER ph)
     391DECLINLINE(PRTUUID) getImageParentUUID(PVDIHEADER ph)
    391392{
    392393    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    399400}
    400401
    401 static inline PRTUUID getImageParentModificationUUID(PVDIHEADER ph)
     402DECLINLINE(PRTUUID) getImageParentModificationUUID(PVDIHEADER ph)
    402403{
    403404    switch (GET_MAJOR_HEADER_VERSION(ph))
     
    515516
    516517
    517 /** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */
    518 #define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) )
    519 
    520 /** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
    521 #define PDMIBASE_2_DRVINS(pInterface)   ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
    522 
    523 /** Converts a pointer to PDMDRVINS::IBase to a PVDIDISK. */
    524 #define PDMIBASE_2_VDIDISK(pInterface)  ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVDIDISK) )
    525 
    526 
    527518/*******************************************************************************
    528519*   Internal Functions                                                         *
    529520*******************************************************************************/
    530 static unsigned getPowerOfTwo(unsigned uNumber);
    531 static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
    532 static int  vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
    533 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
    534                           const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    535                           uint32_t cbBlockExtra);
    536 static int  vdiValidateHeader(PVDIHEADER pHeader);
    537 static int  vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    538                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    539                           PFNVMPROGRESS pfnProgress, void *pvUser);
    540 static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
    541 static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
    542 static int  vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename, unsigned fOpen,
    543                         PVDIIMAGEDESC pParent);
    544 static int  vdiUpdateHeader(PVDIIMAGEDESC pImage);
    545 static int  vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
    546 static int  vdiUpdateBlocks(PVDIIMAGEDESC pImage);
    547 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
    548 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
    549 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    550 #if 0 /* unused */
    551 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
    552 #endif
    553 static void vdiFlushImage(PVDIIMAGEDESC pImage);
    554 static void vdiCloseImage(PVDIIMAGEDESC pImage);
    555 static int  vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    556                            unsigned cbToRead, void *pvBuf);
    557 static int  vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    558 static int  vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock,
    559                             unsigned offWrite, unsigned cbToWrite, const void *pvBuf);
    560 static int  vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
    561 static int  vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    562                           PFNVMPROGRESS pfnProgress, void *pvUser);
    563 static void vdiInitVDIDisk(PVDIDISK pDisk);
    564 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    565 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
    566 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
    567 static int  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
    568 static int  vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
    569 
    570 static int  vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    571                             PFNVMPROGRESS pfnProgress, void *pvUser);
    572 static void vdiDumpImage(PVDIIMAGEDESC pImage);
    573 
    574 static DECLCALLBACK(int)  vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
    575 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns);
    576 static DECLCALLBACK(int)  vdiRead(PPDMIMEDIA pInterface,
    577                                   uint64_t off, void *pvBuf, size_t cbRead);
    578 static DECLCALLBACK(int)  vdiWrite(PPDMIMEDIA pInterface,
    579                                    uint64_t off, const void *pvBuf, size_t cbWrite);
    580 static DECLCALLBACK(int)  vdiFlush(PPDMIMEDIA pInterface);
    581 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface);
    582 static DECLCALLBACK(int)  vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders,
    583                                              uint32_t *pcHeads, uint32_t *pcSectors);
    584 static DECLCALLBACK(int)  vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders,
    585                                              uint32_t cHeads, uint32_t cSectors);
    586 static DECLCALLBACK(int)  vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
    587 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface);
    588 static DECLCALLBACK(int)  vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    589                                                 PPDMBIOSTRANSLATION penmTranslation);
    590 static DECLCALLBACK(int)  vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    591                                                 PDMBIOSTRANSLATION enmTranslation);
    592 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
    593 
    594 
    595 /**
    596  * internal: return power of 2 or 0 if num error.
    597  */
    598 static unsigned getPowerOfTwo(unsigned uNumber)
    599 {
    600     if (uNumber == 0)
    601         return 0;
    602     unsigned uPower2 = 0;
    603     while ((uNumber & 1) == 0)
    604     {
    605         uNumber >>= 1;
    606         uPower2++;
    607     }
    608     return uNumber == 1 ? uPower2 : 0;
    609 }
    610 
    611 /**
    612  * internal: init HDD preheader.
    613  */
    614 static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
    615 {
    616     pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
    617     pPreHdr->u32Version = VDI_IMAGE_VERSION;
    618     memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
    619     strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
    620 }
    621 
    622 /**
    623  * internal: check HDD preheader.
    624  */
    625 static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
    626 {
    627     if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
    628         return VERR_VDI_INVALID_SIGNATURE;
    629 
    630     if (    pPreHdr->u32Version != VDI_IMAGE_VERSION
    631         &&  pPreHdr->u32Version != 0x00000002)    /* old version. */
    632         return VERR_VDI_UNSUPPORTED_VERSION;
    633 
    634     return VINF_SUCCESS;
    635 }
    636 
    637 /**
    638  * internal: init HDD header. Always use latest header version.
    639  * @param   pHeader     Assumes it was initially initialized to all zeros.
    640  */
    641 static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
    642                           const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
    643                           uint32_t cbBlockExtra)
    644 {
    645     pHeader->uVersion = VDI_IMAGE_VERSION;
    646     pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
    647     pHeader->u.v1.u32Type = (uint32_t)enmType;
    648     pHeader->u.v1.fFlags = fFlags;
    649 #ifdef VBOX_STRICT
    650     char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
    651     Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
    652 #endif
    653     pHeader->u.v1.szComment[0] = '\0';
    654     if (pszComment)
    655     {
    656         AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
    657                   ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
    658         strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
    659     }
    660 
    661     /* Mark the geometry not-calculated. */
    662     pHeader->u.v1.Geometry.cCylinders = 0;
    663     pHeader->u.v1.Geometry.cHeads = 0;
    664     pHeader->u.v1.Geometry.cSectors = 0;
    665     pHeader->u.v1.Geometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    666     pHeader->u.v1.u32Translation = PDMBIOSTRANSLATION_AUTO;
    667 
    668     pHeader->u.v1.cbDisk = cbDisk;
    669     pHeader->u.v1.cbBlock = cbBlock;
    670     pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
    671     if (cbDisk % cbBlock)
    672         pHeader->u.v1.cBlocks++;
    673     pHeader->u.v1.cbBlockExtra = cbBlockExtra;
    674     pHeader->u.v1.cBlocksAllocated = 0;
    675 
    676     /* Init offsets. */
    677     pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
    678     pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
    679 
    680     /* Init uuids. */
    681     RTUuidCreate(&pHeader->u.v1.uuidCreate);
    682     RTUuidClear(&pHeader->u.v1.uuidModify);
    683     RTUuidClear(&pHeader->u.v1.uuidLinkage);
    684     RTUuidClear(&pHeader->u.v1.uuidParentModify);
    685 }
    686 
    687 /**
    688  * internal: check HDD header.
    689  */
    690 static int vdiValidateHeader(PVDIHEADER pHeader)
    691 {
    692     /* Check verion-dependend header parameters. */
    693     switch (GET_MAJOR_HEADER_VERSION(pHeader))
    694     {
    695         case 0:
    696         {
    697             /* Old header version. */
    698             break;
    699         }
    700         case 1:
    701         {
    702             /* Current header version. */
    703 
    704             if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
    705             {
    706                 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
    707                        pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
    708                 return VERR_VDI_INVALID_HEADER;
    709             }
    710 
    711             if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
    712             {
    713                 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
    714                        getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
    715                 return VERR_VDI_INVALID_HEADER;
    716             }
    717 
    718             if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
    719             {
    720                 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
    721                        getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
    722                 return VERR_VDI_INVALID_HEADER;
    723             }
    724 
    725             if (    getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
    726                 ||  getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
    727             {
    728                 if (RTUuidIsNull(getImageParentUUID(pHeader)))
    729                 {
    730                     LogRel(("VDI: v1 uuid of parent is 0)\n"));
    731                     return VERR_VDI_INVALID_HEADER;
    732                 }
    733                 if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
    734                 {
    735                     LogRel(("VDI: v1 uuid of parent modification is 0\n"));
    736                     return VERR_VDI_INVALID_HEADER;
    737                 }
    738             }
    739 
    740             break;
    741         }
    742         default:
    743             /* Unsupported. */
    744             return VERR_VDI_UNSUPPORTED_VERSION;
    745     }
    746 
    747     /* Check common header parameters. */
    748 
    749     bool fFailed = false;
    750 
    751     if (    getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
    752         ||  getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
    753     {
    754         LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
    755         fFailed = true;
    756     }
    757 
    758     if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
    759     {
    760         LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
    761         fFailed = true;
    762     }
    763 
    764     if ((getImageGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
    765     {
    766         LogRel(("VDI: wrong sector size (%d != %d)\n",
    767                (getImageGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
    768         fFailed = true;
    769     }
    770 
    771     if (    getImageDiskSize(pHeader) == 0
    772         ||  getImageBlockSize(pHeader) == 0
    773         ||  getImageBlocks(pHeader) == 0
    774         ||  getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
    775     {
    776         LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
    777               getImageDiskSize(pHeader), getImageBlockSize(pHeader),
    778               getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
    779         fFailed = true;
    780     }
    781 
    782     if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
    783     {
    784         LogRel(("VDI: too many blocks allocated (%d > %d)\n"
    785                 "     blocksize=%d disksize=%lld\n",
    786               getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
    787               getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
    788         fFailed = true;
    789     }
    790 
    791     if (    getImageExtraBlockSize(pHeader) != 0
    792         &&  getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
    793     {
    794         LogRel(("VDI: wrong extra size (%d, %d)\n",
    795                getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
    796         fFailed = true;
    797     }
    798 
    799     if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
    800     {
    801         LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
    802                getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
    803         fFailed = true;
    804     }
    805 
    806     if (RTUuidIsNull(getImageCreationUUID(pHeader)))
    807     {
    808         LogRel(("VDI: uuid of creator is 0\n"));
    809         fFailed = true;
    810     }
    811 
    812     if (RTUuidIsNull(getImageModificationUUID(pHeader)))
    813     {
    814         LogRel(("VDI: uuid of modificator is 0\n"));
    815         fFailed = true;
    816     }
    817 
    818     return fFailed ? VERR_VDI_INVALID_HEADER : VINF_SUCCESS;
    819 }
    820 
    821 /**
    822  * internal: init VDIIMAGEDESC structure.
    823  */
    824 static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
    825 {
    826     pImage->pPrev = NULL;
    827     pImage->pNext = NULL;
    828     pImage->File = NIL_RTFILE;
    829     pImage->paBlocks = NULL;
    830 }
    831 
    832 /**
    833  * internal: setup VDIIMAGEDESC structure by image header.
    834  */
    835 static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
    836 {
    837     pImage->fFlags             = getImageFlags(&pImage->Header);
    838     pImage->offStartBlocks     = getImageBlocksOffset(&pImage->Header);
    839     pImage->offStartData       = getImageDataOffset(&pImage->Header);
    840     pImage->uBlockMask         = getImageBlockSize(&pImage->Header) - 1;
    841     pImage->uShiftIndex2Offset =
    842     pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
    843     pImage->offStartBlockData  = getImageExtraBlockSize(&pImage->Header);
    844     if (pImage->offStartBlockData != 0)
    845         pImage->uShiftIndex2Offset += getPowerOfTwo(pImage->offStartBlockData);
    846 }
    847 
    848 /**
    849  * internal: create image.
    850  */
    851 static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
    852                           uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
    853                           PFNVMPROGRESS pfnProgress, void *pvUser)
    854 {
    855     /* Check args. */
    856     Assert(pszFilename);
    857     Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
    858     Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
    859     Assert(cbSize);
    860 
    861     /* Special check for comment length. */
    862     if (    pszComment
    863         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    864     {
    865         Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    866         return VERR_VDI_COMMENT_TOO_LONG;
    867     }
    868 
    869     if (    enmType == VDI_IMAGE_TYPE_UNDO
    870         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    871     {
    872         Assert(pParent);
    873         if ((pParent->PreHeader.u32Version >> 16) != VDI_IMAGE_VERSION_MAJOR)
    874         {
    875             /* Invalid parent image version. */
    876             Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
    877             return VERR_VDI_UNSUPPORTED_VERSION;
    878         }
    879 
    880         /* get image params from the parent image. */
    881         fFlags = getImageFlags(&pParent->Header);
    882         cbSize = getImageDiskSize(&pParent->Header);
    883     }
    884 
    885     PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    886     if (!pImage)
    887         return VERR_NO_MEMORY;
    888     vdiInitImageDesc(pImage);
    889 
    890     vdiInitPreHeader(&pImage->PreHeader);
    891     vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
    892 
    893     if (    enmType == VDI_IMAGE_TYPE_UNDO
    894         ||  enmType == VDI_IMAGE_TYPE_DIFF)
    895     {
    896         /* Set up linkage information. */
    897         pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
    898         pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
    899     }
    900 
    901     pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    902     if (!pImage->paBlocks)
    903     {
    904         RTMemFree(pImage);
    905         return VERR_NO_MEMORY;
    906     }
    907 
    908     if (enmType != VDI_IMAGE_TYPE_FIXED)
    909     {
    910         /* for growing images mark all blocks in paBlocks as free. */
    911         for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
    912             pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
    913     }
    914     else
    915     {
    916         /* for fixed images mark all blocks in paBlocks as allocated */
    917         for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
    918             pImage->paBlocks[i] = i;
    919         pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
    920     }
    921 
    922     /* Setup image parameters. */
    923     vdiSetupImageDesc(pImage);
    924 
    925     /* create file */
    926     int rc = RTFileOpen(&pImage->File,
    927                         pszFilename,
    928                         RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    929     if (VBOX_SUCCESS(rc))
    930     {
    931         /* Lock image exclusively to close any wrong access by VDI API calls. */
    932         uint64_t cbLock = pImage->offStartData
    933                         + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    934         rc = RTFileLock(pImage->File,
    935                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    936         if (VBOX_FAILURE(rc))
    937         {
    938             cbLock = 0;    /* Not locked. */
    939             goto l_create_failed;
    940         }
    941 
    942         if (enmType == VDI_IMAGE_TYPE_FIXED)
    943         {
    944             /*
    945              * Allocate & commit whole file if fixed image, it must be more
    946              * effective than expanding file by write operations.
    947              */
    948             rc = RTFileSetSize(pImage->File,
    949                                pImage->offStartData
    950                              + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
    951         }
    952         else
    953         {
    954             /* Set file size to hold header and blocks array. */
    955             rc = RTFileSetSize(pImage->File, pImage->offStartData);
    956         }
    957         if (VBOX_FAILURE(rc))
    958             goto l_create_failed;
    959 
    960         /* Generate image last-modify uuid */
    961         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    962 
    963         /* Write pre-header. */
    964         rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    965         if (VBOX_FAILURE(rc))
    966             goto l_create_failed;
    967 
    968         /* Write header. */
    969         rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    970         if (VBOX_FAILURE(rc))
    971             goto l_create_failed;
    972 
    973         /* Write blocks array. */
    974         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    975         if (VBOX_FAILURE(rc))
    976             goto l_create_failed;
    977         rc = RTFileWrite(pImage->File,
    978                          pImage->paBlocks,
    979                          getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
    980                          NULL);
    981         if (VBOX_FAILURE(rc))
    982             goto l_create_failed;
    983 
    984         if (    (enmType == VDI_IMAGE_TYPE_FIXED)
    985             &&  (fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND))
    986         {
    987             /* Fill image with zeroes. */
    988 
    989             rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
    990             if (VBOX_FAILURE(rc))
    991                 goto l_create_failed;
    992 
    993             /* alloc tmp zero-filled buffer */
    994             void *pvBuf = RTMemTmpAllocZ(VDIDISK_DEFAULT_BUFFER_SIZE);
    995             if (pvBuf)
    996             {
    997                 uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
    998                 uint64_t cbDisk = cbFill;
    999 
    1000                 /* do loop to fill all image. */
    1001                 while (cbFill > 0)
    1002                 {
    1003                     unsigned to_fill = (unsigned)RT_MIN(cbFill, VDIDISK_DEFAULT_BUFFER_SIZE);
    1004 
    1005                     rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    1006                     if (VBOX_FAILURE(rc))
    1007                         break;
    1008 
    1009                     cbFill -= to_fill;
    1010 
    1011                     if (pfnProgress)
    1012                     {
    1013                         rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1014                                          (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
    1015                                          pvUser);
    1016                         if (VBOX_FAILURE(rc))
    1017                             break;
    1018                     }
    1019                 }
    1020                 RTMemTmpFree(pvBuf);
    1021             }
    1022             else
    1023             {
    1024                 /* alloc error */
    1025                 rc = VERR_NO_MEMORY;
    1026             }
    1027         }
    1028 
    1029     l_create_failed:
    1030 
    1031         if (cbLock)
    1032             RTFileUnlock(pImage->File, 0, cbLock);
    1033 
    1034         RTFileClose(pImage->File);
    1035 
    1036         /* Delete image file if error occured while creating */
    1037         if (VBOX_FAILURE(rc))
    1038             RTFileDelete(pszFilename);
    1039     }
    1040 
    1041     RTMemFree(pImage->paBlocks);
    1042     RTMemFree(pImage);
    1043 
    1044     if (    VBOX_SUCCESS(rc)
    1045         &&  pfnProgress)
    1046         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    1047 
    1048     Log(("vdiCreateImage: done, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
    1049 
    1050     return rc;
    1051 }
    1052 
    1053 /**
    1054  * Open an image.
    1055  * @internal
    1056  */
    1057 static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename,
    1058                         unsigned fOpen, PVDIIMAGEDESC pParent)
    1059 {
    1060     /*
    1061      * Validate input.
    1062      */
    1063     Assert(ppImage);
    1064     Assert(pszFilename);
    1065     Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
    1066 
    1067     PVDIIMAGEDESC   pImage;
    1068     size_t          cchFilename = strlen(pszFilename);
    1069     if (cchFilename >= sizeof(pImage->szFilename))
    1070     {
    1071         AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
    1072         return VERR_FILENAME_TOO_LONG;
    1073     }
    1074 
    1075     pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
    1076     if (!pImage)
    1077         return VERR_NO_MEMORY;
    1078     vdiInitImageDesc(pImage);
    1079 
    1080     memcpy(pImage->szFilename, pszFilename, cchFilename);
    1081     pImage->fOpen = fOpen;
    1082 
    1083     /*
    1084      * Open the image.
    1085      */
    1086     int rc = RTFileOpen(&pImage->File,
    1087                         pImage->szFilename,
    1088                         fOpen & VDI_OPEN_FLAGS_READONLY
    1089                         ? RTFILE_O_READ      | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
    1090                         : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    1091     if (VBOX_FAILURE(rc))
    1092     {
    1093         if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
    1094         {
    1095             /* Try to open image for reading only. */
    1096             rc = RTFileOpen(&pImage->File,
    1097                             pImage->szFilename,
    1098                             RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    1099             if (VBOX_SUCCESS(rc))
    1100                 pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
    1101         }
    1102         if (VBOX_FAILURE(rc))
    1103         {
    1104             RTMemFree(pImage);
    1105             return rc;
    1106         }
    1107     }
    1108     /* Set up current image r/w state. */
    1109     pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
    1110 
    1111     /*
    1112      * Set initial file lock for reading header only.
    1113      * Length of lock doesn't matter, it just must include image header.
    1114      */
    1115     uint64_t cbLock = _1M;
    1116     rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    1117     if (VBOX_FAILURE(rc))
    1118     {
    1119         cbLock = 0;
    1120         goto l_open_failed;
    1121     }
    1122 
    1123     /* Read pre-header. */
    1124     rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    1125     if (VBOX_FAILURE(rc))
    1126         goto l_open_failed;
    1127     rc = vdiValidatePreHeader(&pImage->PreHeader);
    1128     if (VBOX_FAILURE(rc))
    1129         goto l_open_failed;
    1130 
    1131     /* Read header. */
    1132     pImage->Header.uVersion = pImage->PreHeader.u32Version;
    1133     switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    1134     {
    1135         case 0:
    1136             rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
    1137             break;
    1138         case 1:
    1139             rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    1140             break;
    1141         default:
    1142             rc = VERR_VDI_UNSUPPORTED_VERSION;
    1143             break;
    1144     }
    1145     if (VBOX_FAILURE(rc))
    1146         goto l_open_failed;
    1147 
    1148     rc = vdiValidateHeader(&pImage->Header);
    1149     if (VBOX_FAILURE(rc))
    1150         goto l_open_failed;
    1151 
    1152     /* Check diff image correctness. */
    1153     if (pParent)
    1154     {
    1155         if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
    1156         {
    1157             rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
    1158             goto l_open_failed;
    1159         }
    1160 
    1161         if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
    1162             &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
    1163         {
    1164             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    1165             goto l_open_failed;
    1166         }
    1167 
    1168         if (    getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
    1169             ||  getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
    1170             ||  getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
    1171             ||  getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
    1172         {
    1173             rc = VERR_VDI_WRONG_DIFF_IMAGE;
    1174             goto l_open_failed;
    1175         }
    1176 
    1177         /* Check linkage data. */
    1178         if (    RTUuidCompare(getImageParentUUID(&pImage->Header),
    1179                               getImageCreationUUID(&pParent->Header))
    1180             ||  RTUuidCompare(getImageParentModificationUUID(&pImage->Header),
    1181                               getImageModificationUUID(&pParent->Header)))
    1182         {
    1183             rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    1184             goto l_open_failed;
    1185         }
    1186     }
    1187 
    1188     /* Setup image parameters by header. */
    1189     vdiSetupImageDesc(pImage);
    1190 
    1191     /* reset modified flag into first-modified state. */
    1192     pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
    1193 
    1194     /* Image is validated, set working file lock on it. */
    1195     rc = RTFileUnlock(pImage->File, 0, cbLock);
    1196     AssertRC(rc);
    1197     cbLock = pImage->offStartData
    1198            + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    1199     rc = RTFileLock(pImage->File,
    1200                     (pImage->fReadOnly) ?
    1201                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    1202                         RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    1203                     0,
    1204                     cbLock);
    1205     if (    VBOX_FAILURE(rc)
    1206         &&  !pImage->fReadOnly)
    1207     {
    1208         /* Failed to lock image for writing, try read-only lock. */
    1209         rc = RTFileLock(pImage->File,
    1210                         RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
    1211         if (VBOX_SUCCESS(rc))
    1212             pImage->fReadOnly = true;
    1213     }
    1214     if (VBOX_FAILURE(rc))
    1215     {
    1216         cbLock = 0;    /* Not locked. */
    1217         goto l_open_failed;
    1218     }
    1219 
    1220     /* Allocate memory for blocks array. */
    1221     pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
    1222     if (!pImage->paBlocks)
    1223     {
    1224         rc = VERR_NO_MEMORY;
    1225         goto l_open_failed;
    1226     }
    1227 
    1228     /* Read blocks array. */
    1229     rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    1230     if (VBOX_FAILURE(rc))
    1231         goto l_open_failed;
    1232     rc = RTFileRead(pImage->File, pImage->paBlocks,
    1233                     getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), NULL);
    1234     if (VBOX_FAILURE(rc))
    1235         goto l_open_failed;
    1236 
    1237     /* all done. */
    1238     *ppImage = pImage;
    1239     return VINF_SUCCESS;
    1240 
    1241 l_open_failed:
    1242     /* Clean up. */
    1243     if (pImage->paBlocks)
    1244         RTMemFree(pImage->paBlocks);
    1245     if (cbLock)
    1246         RTFileUnlock(pImage->File, 0, cbLock);
    1247     RTFileClose(pImage->File);
    1248     RTMemFree(pImage);
    1249     Log(("vdiOpenImage: failed, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
    1250     return rc;
    1251 }
    1252 
    1253 /**
    1254  * internal: save header to file.
    1255  */
    1256 static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
    1257 {
    1258     /* Seek to header start. */
    1259     int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
    1260     if (VBOX_SUCCESS(rc))
    1261     {
    1262         switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
    1263         {
    1264             case 0:
    1265                 rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
    1266                 break;
    1267             case 1:
    1268                 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
    1269                 break;
    1270             default:
    1271                 rc = VERR_VDI_UNSUPPORTED_VERSION;
    1272                 break;
    1273         }
    1274     }
    1275     AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->szFilename, rc));
    1276     return rc;
    1277 }
    1278 
    1279 /**
    1280  * internal: save block pointer to file, save header to file.
    1281  */
    1282 static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
    1283 {
    1284     /* Update image header. */
    1285     int rc = vdiUpdateHeader(pImage);
    1286     if (VBOX_SUCCESS(rc))
    1287     {
    1288         /* write only one block pointer. */
    1289         rc = RTFileSeek(pImage->File,
    1290                         pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
    1291                         RTFILE_SEEK_BEGIN,
    1292                         NULL);
    1293         if (VBOX_SUCCESS(rc))
    1294             rc = RTFileWrite(pImage->File,
    1295                              &pImage->paBlocks[uBlock],
    1296                              sizeof(VDIIMAGEBLOCKPOINTER),
    1297                              NULL);
    1298         AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
    1299                          uBlock, pImage->szFilename, rc));
    1300     }
    1301     return rc;
    1302 }
    1303 
    1304 /**
    1305  * internal: save blocks array to file, save header to file.
    1306  */
    1307 static int vdiUpdateBlocks(PVDIIMAGEDESC pImage)
    1308 {
    1309     /* Update image header. */
    1310     int rc = vdiUpdateHeader(pImage);
    1311     if (VBOX_SUCCESS(rc))
    1312     {
    1313         /* write the block pointers array. */
    1314         rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
    1315         if (VBOX_SUCCESS(rc))
    1316             rc = RTFileWrite(pImage->File,
    1317                              pImage->paBlocks,
    1318                              sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header),
    1319                              NULL);
    1320         AssertMsgRC(rc, ("vdiUpdateBlocks failed, filename=\"%s\", rc=%Vrc\n",
    1321                          pImage->szFilename, rc));
    1322     }
    1323     return rc;
    1324 }
    1325 
    1326 /**
    1327  * internal: mark image as modified, if this is the first change - update image header
    1328  * on disk with a new uuidModify value.
    1329  */
    1330 static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
    1331 {
    1332     pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
    1333     if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
    1334     {
    1335         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
    1336 
    1337         /* first modify - generate uuidModify and save to file. */
    1338         vdiResetModifiedFlag(pImage);
    1339 
    1340         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    1341         {
    1342             /* save header to file,
    1343              * note: no rc checking.
    1344              */
    1345             vdiUpdateHeader(pImage);
    1346         }
    1347     }
    1348 }
    1349 
    1350 /**
    1351  * internal: generate new uuidModify if the image was changed.
    1352  */
    1353 static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
    1354 {
    1355     if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
    1356     {
    1357         /* generate new last-modified uuid */
    1358         if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
    1359             RTUuidCreate(getImageModificationUUID(&pImage->Header));
    1360 
    1361         pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
    1362     }
    1363 }
    1364 
    1365 /**
    1366  * internal: disables updates of the last-modified UUID
    1367  * when performing image writes.
    1368  */
    1369 static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    1370 {
    1371     pImage->fModified |= VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    1372 }
    1373 
    1374 #if 0 /* unused */
    1375 /**
    1376  * internal: enables updates of the last-modified UUID
    1377  * when performing image writes.
    1378  */
    1379 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage)
    1380 {
    1381     pImage->fModified &= ~VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
    1382 }
    1383 #endif
    1384 
    1385 /**
    1386  * internal: flush image file to disk.
    1387  */
    1388 static void vdiFlushImage(PVDIIMAGEDESC pImage)
    1389 {
    1390     if (!pImage->fReadOnly)
    1391     {
    1392         /* Update last-modified uuid if need. */
    1393         vdiResetModifiedFlag(pImage);
    1394 
    1395         /* Save header. */
    1396         int rc = vdiUpdateHeader(pImage);
    1397         AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    1398                          pImage->szFilename, rc));
    1399         RTFileFlush(pImage->File);
    1400     }
    1401 }
    1402 
    1403 /**
    1404  * internal: close image file.
    1405  */
    1406 static void vdiCloseImage(PVDIIMAGEDESC pImage)
    1407 {
    1408     /* Params checking. */
    1409     Assert(pImage);
    1410     Assert(pImage->File != NIL_RTFILE);
    1411 
    1412     vdiFlushImage(pImage);
    1413     RTFileUnlock(pImage->File,
    1414                  0,
    1415                  pImage->offStartData
    1416                + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
    1417     RTFileClose(pImage->File);
    1418 
    1419     /* free image resources */
    1420     RTMemFree(pImage->paBlocks);
    1421     RTMemFree(pImage);
    1422 }
    1423 
    1424 /**
    1425  * internal: read data inside image block.
    1426  *
    1427  * note: uBlock must be valid, readed data must not overlap block bounds.
    1428  */
    1429 static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
    1430                           unsigned cbToRead, void *pvBuf)
    1431 {
    1432     if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    1433     {
    1434         /* block present in image file */
    1435         uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1436                            + (pImage->offStartData + pImage->offStartBlockData + offRead);
    1437         int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1438         if (VBOX_SUCCESS(rc))
    1439             rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
    1440         if (VBOX_FAILURE(rc))
    1441             Log(("vdiReadInBlock: rc=%Vrc filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
    1442                  rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
    1443         return rc;
    1444     }
    1445 
    1446     /* Returns zeroes for both free and zero block types. */
    1447     memset(pvBuf, 0, cbToRead);
    1448     return VINF_SUCCESS;
    1449 }
    1450 
    1451 /**
    1452  * Read data from virtual HDD.
    1453  *
    1454  * @returns VBox status code.
    1455  * @param   pDisk           Pointer to VDI HDD container.
    1456  * @param   offStart        Offset of first reading byte from start of disk.
    1457  * @param   pvBuf           Pointer to buffer for reading data.
    1458  * @param   cbToRead        Number of bytes to read.
    1459  */
    1460 IDER3DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, unsigned cbToRead)
    1461 {
    1462     /* sanity check */
    1463     Assert(pDisk);
    1464     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1465 
    1466     PVDIIMAGEDESC pImage = pDisk->pLast;
    1467     Assert(pImage);
    1468 
    1469     /* Check params. */
    1470     if (    offStart + cbToRead > getImageDiskSize(&pImage->Header)
    1471         ||  cbToRead == 0)
    1472     {
    1473         AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
    1474         return VERR_INVALID_PARAMETER;
    1475     }
    1476 
    1477     /* Calculate starting block number and offset inside it. */
    1478     unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1479     unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
    1480 
    1481     /* Save block size here for speed optimization. */
    1482     unsigned cbBlock = getImageBlockSize(&pImage->Header);
    1483 
    1484     /* loop through blocks */
    1485     int rc;
    1486     for (;;)
    1487     {
    1488         unsigned to_read;
    1489         if ((offRead + cbToRead) <= cbBlock)
    1490             to_read = cbToRead;
    1491         else
    1492             to_read = cbBlock - offRead;
    1493 
    1494         if (pDisk->cImages > 1)
    1495         {
    1496             /* Differencing images are used, handle them. */
    1497             pImage = pDisk->pLast;
    1498 
    1499             /* Search for image with allocated block. */
    1500             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1501             {
    1502                 pImage = pImage->pPrev;
    1503                 if (!pImage)
    1504                 {
    1505                     /* Block is not allocated in all images of chain. */
    1506                     pImage = pDisk->pLast;
    1507                     break;
    1508                 }
    1509             }
    1510         }
    1511 
    1512         rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
    1513 
    1514         cbToRead -= to_read;
    1515         if (    cbToRead == 0
    1516             ||  VBOX_FAILURE(rc))
    1517             break;
    1518 
    1519         /* goto next block */
    1520         uBlock++;
    1521         offRead = 0;
    1522         pvBuf = (char *)pvBuf + to_read;
    1523     }
    1524 
    1525     return rc;
    1526 }
    1527 
    1528 /**
    1529  * internal: fill the whole block with zeroes.
    1530  *
    1531  * note: block id must be valid, block must be already allocated in file.
    1532  * note: if pDisk is NULL, the default buffer size is used
    1533  */
    1534 static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1535 {
    1536     int rc;
    1537 
    1538     /* seek to start of block in file. */
    1539     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1540                        + (pImage->offStartData + pImage->offStartBlockData);
    1541     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1542     if (VBOX_FAILURE(rc))
    1543     {
    1544         Log(("vdiFillBlockByZeroes: seek rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu\n",
    1545              rc, pImage->szFilename, uBlock, u64Offset));
    1546         return rc;
    1547     }
    1548 
    1549     /* alloc tmp zero-filled buffer */
    1550     void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1551     if (!pvBuf)
    1552         return VERR_NO_MEMORY;
    1553 
    1554     unsigned cbFill = getImageBlockSize(&pImage->Header);
    1555 
    1556     /* do loop, because buffer size may be less then block size */
    1557     while (cbFill > 0)
    1558     {
    1559         unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
    1560         rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
    1561         if (VBOX_FAILURE(rc))
    1562         {
    1563             Log(("vdiFillBlockByZeroes: write rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
    1564                  rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
    1565             break;
    1566         }
    1567 
    1568         cbFill -= to_fill;
    1569     }
    1570 
    1571     RTMemTmpFree(pvBuf);
    1572     return rc;
    1573 }
    1574 
    1575 /**
    1576  * internal: write data inside image block.
    1577  *
    1578  * note: uBlock must be valid, written data must not overlap block bounds.
    1579  */
    1580 static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, unsigned cbToWrite, const void *pvBuf)
    1581 {
    1582     int rc;
    1583 
    1584     /* Check if we can write into file. */
    1585     if (pImage->fReadOnly)
    1586     {
    1587         Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
    1588         return VERR_WRITE_PROTECT;
    1589     }
    1590 
    1591     /* This could be optimized a little (not setting it when writing zeroes
    1592      * to a zeroed block). Won't buy us much, because it's very unlikely
    1593      * that only such zero data block writes occur while the VDI is opened. */
    1594     vdiSetModifiedFlag(pImage);
    1595 
    1596     if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    1597     {
    1598         if (!pDisk || !pDisk->fHonorZeroWrites)
    1599         {
    1600             /* If the destination block is unallocated at this point, it's either
    1601              * a zero block or a block which hasn't been used so far (which also
    1602              * means that it's a zero block. Don't need to write anything to this
    1603              * block if the data consists of just zeroes. */
    1604             Assert(cbToWrite % 4 == 0);
    1605             if (ASMBitFirstSet((volatile void *)pvBuf, cbToWrite * 8) == -1)
    1606             {
    1607                 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1608                 return VINF_SUCCESS;
    1609             }
    1610         }
    1611 
    1612         /* need to allocate a new block in image file */
    1613 
    1614         /* expand file by one block */
    1615         uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
    1616                          + pImage->offStartData;
    1617         rc = RTFileSetSize(pImage->File, u64Size);
    1618         if (VBOX_FAILURE(rc))
    1619         {
    1620             Log(("vdiWriteInBlock: set size rc=%Vrc filename=\"%s\" uBlock=%u u64Size=%llu\n",
    1621                  rc, pImage->szFilename, uBlock, u64Size));
    1622             return rc;
    1623         }
    1624 
    1625         unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    1626         pImage->paBlocks[uBlock] = cBlocksAllocated;
    1627         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
    1628 
    1629         if (    pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
    1630             ||  pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1631         {
    1632             /* Fill newly allocated block by zeroes. */
    1633 
    1634             if (offWrite || cbToWrite != getImageBlockSize(&pImage->Header))
    1635             {
    1636                 rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
    1637                 if (VBOX_FAILURE(rc))
    1638                     return rc;
    1639             }
    1640         }
    1641 
    1642         rc = vdiUpdateBlockInfo(pImage, uBlock);
    1643         if (VBOX_FAILURE(rc))
    1644             return rc;
    1645     }
    1646 
    1647     /* Now block present in image file, write data inside it. */
    1648     uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
    1649                        + (pImage->offStartData + pImage->offStartBlockData + offWrite);
    1650     rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    1651     if (VBOX_SUCCESS(rc))
    1652     {
    1653         rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
    1654         if (VBOX_FAILURE(rc))
    1655             Log(("vdiWriteInBlock: write rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
    1656                  rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
    1657     }
    1658     else
    1659         Log(("vdiWriteInBlock: seek rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
    1660              rc, pImage->szFilename, uBlock, offWrite, u64Offset));
    1661 
    1662     return rc;
    1663 }
    1664 
    1665 /**
    1666  * internal: copy data block from one (parent) image to last image.
    1667  */
    1668 static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
    1669 {
    1670     Assert(pImage != pDisk->pLast);
    1671 
    1672     if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1673     {
    1674         /*
    1675          * if src block is zero, set dst block to zero too.
    1676          */
    1677         pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1678         return VINF_SUCCESS;
    1679     }
    1680 
    1681     /* alloc tmp buffer */
    1682     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1683     if (!pvBuf)
    1684         return VERR_NO_MEMORY;
    1685 
    1686     int rc = VINF_SUCCESS;
    1687 
    1688     unsigned cbCopy = getImageBlockSize(&pImage->Header);
    1689     unsigned offCopy = 0;
    1690 
    1691     /* do loop, because buffer size may be less then block size */
    1692     while (cbCopy > 0)
    1693     {
    1694         unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
    1695         rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
    1696         if (VBOX_FAILURE(rc))
    1697             break;
    1698 
    1699         rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
    1700         if (VBOX_FAILURE(rc))
    1701             break;
    1702 
    1703         cbCopy -= to_copy;
    1704         offCopy += to_copy;
    1705     }
    1706 
    1707     RTMemTmpFree(pvBuf);
    1708     return rc;
    1709 }
    1710 
    1711 /**
    1712  * Write data to virtual HDD.
    1713  *
    1714  * @returns VBox status code.
    1715  * @param   pDisk           Pointer to VDI HDD container.
    1716  * @param   offStart        Offset of first writing byte from start of HDD.
    1717  * @param   pvBuf           Pointer to buffer of writing data.
    1718  * @param   cbToWrite       Number of bytes to write.
    1719  */
    1720 IDER3DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, unsigned cbToWrite)
    1721 {
    1722     /* sanity check */
    1723     Assert(pDisk);
    1724     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1725 
    1726     PVDIIMAGEDESC pImage = pDisk->pLast;
    1727     Assert(pImage);
    1728 
    1729     /* Check params. */
    1730     if (    offStart + cbToWrite > getImageDiskSize(&pImage->Header)
    1731         ||  cbToWrite == 0)
    1732     {
    1733         AssertMsgFailed(("offStart=%llu cbToWrite=%u\n", offStart, cbToWrite));
    1734         return VERR_INVALID_PARAMETER;
    1735     }
    1736 
    1737     /* Calculate starting block number and offset inside it. */
    1738     unsigned uBlock   = (unsigned)(offStart >> pImage->uShiftOffset2Index);
    1739     unsigned offWrite = (unsigned)offStart   & pImage->uBlockMask;
    1740     unsigned cbBlock  = getImageBlockSize(&pImage->Header);
    1741 
    1742     /* loop through blocks */
    1743     int rc;
    1744     for (;;)
    1745     {
    1746         unsigned to_write;
    1747         if (offWrite + cbToWrite <= cbBlock)
    1748             to_write = cbToWrite;
    1749         else
    1750             to_write = cbBlock - offWrite;
    1751 
    1752         /* All callers write less than a VDI block right now (assuming
    1753          * default VDI block size). So not worth optimizing for the case
    1754          * where a full block is overwritten (no copying required).
    1755          * Checking whether a block is all zeroes after the write is too
    1756          * expensive (would require reading the rest of the block). */
    1757 
    1758         if (pDisk->cImages > 1)
    1759         {
    1760             /* Differencing images are used, handle them. */
    1761 
    1762             /* Search for image with allocated block. */
    1763             while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1764             {
    1765                 pImage = pImage->pPrev;
    1766                 if (!pImage)
    1767                 {
    1768                     /* Block is not allocated in all images of chain. */
    1769                     pImage = pDisk->pLast;
    1770                     break;
    1771                 }
    1772             }
    1773 
    1774             if (pImage != pDisk->pLast)
    1775             {
    1776                 /* One of parent image has a block data, copy it into last image. */
    1777                 rc = vdiCopyBlock(pDisk, pImage, uBlock);
    1778                 if (VBOX_FAILURE(rc))
    1779                     break;
    1780                 pImage = pDisk->pLast;
    1781             }
    1782         }
    1783 
    1784         /* Actually write the data into block. */
    1785         rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
    1786 
    1787         cbToWrite -= to_write;
    1788         if (    cbToWrite == 0
    1789             || VBOX_FAILURE(rc))
    1790             break;
    1791 
    1792         /* goto next block */
    1793         uBlock++;
    1794         offWrite = 0;
    1795         pvBuf = (char *)pvBuf + to_write;
    1796     }
    1797 
    1798     return rc;
    1799 }
    1800 
    1801 /**
    1802  * internal: commit one image to another, no changes to header, just
    1803  * plain copy operation. Blocks that are not allocated in the source
    1804  * image (i.e. inherited by its parent(s)) are not merged.
    1805  *
    1806  * @param pImageFrom        source image
    1807  * @param pImageTo          target image (will receive all the modifications)
    1808  * @param fParentToChild    true if the source image is parent of the target one,
    1809  *                          false of the target image is the parent of the source.
    1810  * @param pfnProgress       progress callback (NULL if not to be used)
    1811  * @param pvUser            user argument for the progress callback
    1812  *
    1813  * @note the target image has to be opened read/write
    1814  * @note this method does not check whether merging is possible!
    1815  */
    1816 static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
    1817                           PFNVMPROGRESS pfnProgress, void *pvUser)
    1818 {
    1819     Assert(pImageFrom);
    1820     Assert(pImageTo);
    1821 
    1822     Log(("vdiMergeImages: merging from image \"%s\" to image \"%s\" (fParentToChild=%d)\n",
    1823          pImageFrom->szFilename, pImageTo->szFilename, fParentToChild));
    1824 
    1825     /* alloc tmp buffer */
    1826     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    1827     if (!pvBuf)
    1828         return VERR_NO_MEMORY;
    1829 
    1830     int rc = VINF_SUCCESS;
    1831 
    1832     if (!fParentToChild)
    1833     {
    1834         /*
    1835          *  Commit the child image to the parent image.
    1836          *  Child is the source (from), parent is the target (to).
    1837          */
    1838 
    1839         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1840 
    1841         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1842         {
    1843             /* only process blocks that are allocated in the source image */
    1844             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE)
    1845             {
    1846                 /* Found used block in source image, commit it. */
    1847                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    1848                     &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pImageTo->paBlocks[uBlock]))
    1849                 {
    1850                     /* Block is zero in the source image and not allocated in the target image. */
    1851                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1852                     vdiSetModifiedFlag(pImageTo);
    1853                 }
    1854                 else
    1855                 {
    1856                     /* Block is not zero / allocated in source image. */
    1857                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1858                     unsigned offCommit = 0;
    1859 
    1860                     /* do loop, because buffer size may be less then block size */
    1861                     while (cbCommit > 0)
    1862                     {
    1863                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1864 
    1865                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1866                         if (VBOX_FAILURE(rc))
    1867                             break;
    1868 
    1869                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1870                         if (VBOX_FAILURE(rc))
    1871                             break;
    1872 
    1873                         cbCommit -= cbToCopy;
    1874                         offCommit += cbToCopy;
    1875                     }
    1876                     if (VBOX_FAILURE(rc))
    1877                         break;
    1878                 }
    1879             }
    1880 
    1881             if (pfnProgress)
    1882             {
    1883                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1884                             (uBlock * 100) / cBlocks,
    1885                             pvUser);
    1886                 /* Note: commiting is non breakable operation, skipping rc here. */
    1887             }
    1888         }
    1889     }
    1890     else
    1891     {
    1892         /*
    1893          *  Commit the parent image to the child image.
    1894          *  Parent is the source (from), child is the target (to).
    1895          */
    1896 
    1897         unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
    1898 
    1899         for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1900         {
    1901             /*
    1902              *  only process blocks that are allocated or zero in the source image
    1903              *  and NEITHER allocated NOR zero in the target image
    1904              */
    1905             if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE &&
    1906                 pImageTo->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
    1907             {
    1908                 /* Found used block in source image (but unused in target), commit it. */
    1909                 if (    pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
    1910                 {
    1911                     /* Block is zero in the source image and not allocated in the target image. */
    1912                     pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    1913                     vdiSetModifiedFlag(pImageTo);
    1914                 }
    1915                 else
    1916                 {
    1917                     /* Block is not zero / allocated in source image. */
    1918                     unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
    1919                     unsigned offCommit = 0;
    1920 
    1921                     /* do loop, because buffer size may be less then block size */
    1922                     while (cbCommit > 0)
    1923                     {
    1924                         unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
    1925 
    1926                         rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
    1927                         if (VBOX_FAILURE(rc))
    1928                             break;
    1929 
    1930                         rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
    1931                         if (VBOX_FAILURE(rc))
    1932                             break;
    1933 
    1934                         cbCommit -= cbToCopy;
    1935                         offCommit += cbToCopy;
    1936                     }
    1937                     if (VBOX_FAILURE(rc))
    1938                         break;
    1939                 }
    1940             }
    1941 
    1942             if (pfnProgress)
    1943             {
    1944                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    1945                             (uBlock * 100) / cBlocks,
    1946                             pvUser);
    1947                 /* Note: commiting is non breakable operation, skipping rc here. */
    1948             }
    1949         }
    1950     }
    1951 
    1952     RTMemTmpFree(pvBuf);
    1953     return rc;
    1954 }
    1955 
    1956 /**
    1957  * internal: commit last image(s) to selected previous image.
    1958  * note: all images accessed across this call must be opened in R/W mode.
    1959  * @remark    Only used by tstVDI.
    1960  */
    1961 static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
    1962                             PFNVMPROGRESS pfnProgress, void *pvUser)
    1963 {
    1964     /* sanity check */
    1965     Assert(pDisk);
    1966     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    1967     Assert(pDstImage);
    1968 
    1969     PVDIIMAGEDESC pImage = pDisk->pLast;
    1970     Assert(pImage);
    1971     Log(("vdiCommitToImage: commiting from image \"%s\" to image \"%s\"\n",
    1972          pImage->szFilename, pDstImage->szFilename));
    1973     if (pDstImage == pImage)
    1974     {
    1975         Log(("vdiCommitToImage: attempt to commit to the same image!\n"));
    1976         return VERR_VDI_NO_DIFF_IMAGES;
    1977     }
    1978 
    1979     /* Scan images for pDstImage. */
    1980     while (pImage && pImage != pDstImage)
    1981         pImage = pImage->pPrev;
    1982     if (!pImage)
    1983     {
    1984         AssertMsgFailed(("Invalid arguments: pDstImage is not in images chain\n"));
    1985         return VERR_INVALID_PARAMETER;
    1986     }
    1987     pImage = pDisk->pLast;
    1988 
    1989     /* alloc tmp buffer */
    1990     void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
    1991     if (!pvBuf)
    1992         return VERR_NO_MEMORY;
    1993 
    1994     int rc = VINF_SUCCESS;
    1995     unsigned cBlocks = getImageBlocks(&pImage->Header);
    1996 
    1997     for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
    1998     {
    1999         pImage = pDisk->pLast;
    2000 
    2001         /* Find allocated block to commit. */
    2002         while (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE
    2003                &&  pImage != pDstImage)
    2004             pImage = pImage->pPrev;
    2005 
    2006         if (pImage != pDstImage)
    2007         {
    2008             /* Found used block in diff image (pImage), commit it. */
    2009             if (    pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
    2010                 &&  !IS_VDI_IMAGE_BLOCK_ALLOCATED(pDstImage->paBlocks[uBlock]))
    2011             {
    2012                 /* Block is zero in difference image and not allocated in primary image. */
    2013                 pDstImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
    2014                 vdiSetModifiedFlag(pDstImage);
    2015             }
    2016             else
    2017             {
    2018                 /* Block is not zero / allocated in primary image. */
    2019                 unsigned cbCommit = getImageBlockSize(&pImage->Header);
    2020                 unsigned offCommit = 0;
    2021 
    2022                 /* do loop, because buffer size may be less then block size */
    2023                 while (cbCommit > 0)
    2024                 {
    2025                     unsigned cbToCopy = RT_MIN(cbCommit, pDisk->cbBuf);
    2026 
    2027                     rc = vdiReadInBlock(pImage, uBlock, offCommit, cbToCopy, pvBuf);
    2028                     if (VBOX_FAILURE(rc))
    2029                         break;
    2030 
    2031                     rc = vdiWriteInBlock(pDisk, pDstImage, uBlock, offCommit, cbToCopy, pvBuf);
    2032                     if (VBOX_FAILURE(rc))
    2033                         break;
    2034 
    2035                     cbCommit -= cbToCopy;
    2036                     offCommit += cbToCopy;
    2037                 }
    2038                 if (VBOX_FAILURE(rc))
    2039                     break;
    2040             }
    2041             pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_FREE;
    2042         }
    2043 
    2044         if (pfnProgress)
    2045         {
    2046             pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2047                         (uBlock * 100) / cBlocks,
    2048                         pvUser);
    2049             /* Note: commiting is non breakable operation, skipping rc here. */
    2050         }
    2051     }
    2052 
    2053     RTMemTmpFree(pvBuf);
    2054 
    2055     /* Go forward and update linkage information. */
    2056     for (pImage = pDstImage; pImage; pImage = pImage->pNext)
    2057     {
    2058         /* generate new last-modified uuid. */
    2059         RTUuidCreate(getImageModificationUUID(&pImage->Header));
    2060 
    2061         /* fix up linkage. */
    2062         if (pImage != pDstImage)
    2063             *getImageParentModificationUUID(&pImage->Header) = *getImageModificationUUID(&pImage->pPrev->Header);
    2064 
    2065         /* reset modified flag. */
    2066         pImage->fModified = 0;
    2067     }
    2068 
    2069     /* Process committed images - truncate them. */
    2070     for (pImage = pDisk->pLast; pImage != pDstImage; pImage = pImage->pPrev)
    2071     {
    2072         /* note: can't understand how to do error works here? */
    2073 
    2074         setImageBlocksAllocated(&pImage->Header, 0);
    2075 
    2076         /* Truncate file. */
    2077         int rc2 = RTFileSetSize(pImage->File, pImage->offStartData);
    2078         if (VBOX_FAILURE(rc2))
    2079         {
    2080             rc = rc2;
    2081             Log(("vdiCommitToImage: set size (truncate) rc=%Vrc filename=\"%s\"\n",
    2082                  rc, pImage->szFilename));
    2083         }
    2084 
    2085         /* Save header and blocks array. */
    2086         rc2 = vdiUpdateBlocks(pImage);
    2087         if (VBOX_FAILURE(rc2))
    2088         {
    2089             rc = rc2;
    2090             Log(("vdiCommitToImage: update blocks and header rc=%Vrc filename=\"%s\"\n",
    2091                  rc, pImage->szFilename));
    2092         }
    2093     }
    2094 
    2095     if (pfnProgress)
    2096     {
    2097         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2098        /* Note: commiting is non breakable operation, skipping rc here. */
    2099     }
    2100 
    2101     Log(("vdiCommitToImage: done, rc=%Vrc\n", rc));
    2102 
    2103     return rc;
    2104 }
    2105 
    2106 /**
    2107  * Checks if image is available and not broken, returns some useful image parameters if requested.
    2108  *
    2109  * @returns VBox status code.
    2110  * @param   pszFilename     Name of the image file to check.
    2111  * @param   puVersion       Where to store the version of image. NULL is ok.
    2112  * @param   penmType        Where to store the type of image. NULL is ok.
    2113  * @param   pcbSize         Where to store the size of image in bytes. NULL is ok.
    2114  * @param   pUuid           Where to store the uuid of image creation. NULL is ok.
    2115  * @param   pParentUuid     Where to store the UUID of the parent image. NULL is ok.
    2116  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    2117  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    2118  */
    2119 IDER3DECL(int) VDICheckImage(const char *pszFilename, unsigned *puVersion, PVDIIMAGETYPE penmType,
    2120                              uint64_t *pcbSize, PRTUUID pUuid, PRTUUID pParentUuid,
    2121                              char *pszComment, unsigned cbComment)
    2122 {
    2123     LogFlow(("VDICheckImage:\n"));
    2124 
    2125     /* Check arguments. */
    2126     if (    !pszFilename
    2127         ||  *pszFilename == '\0')
    2128     {
    2129         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2130         return VERR_INVALID_PARAMETER;
    2131     }
    2132 
    2133     PVDIIMAGEDESC pImage;
    2134     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    2135     if (VBOX_SUCCESS(rc))
    2136     {
    2137         Log(("VDICheckImage: filename=\"%s\" version=%08X type=%X cbDisk=%llu uuid={%Vuuid}\n",
    2138              pszFilename,
    2139              pImage->PreHeader.u32Version,
    2140              getImageType(&pImage->Header),
    2141              getImageDiskSize(&pImage->Header),
    2142              getImageCreationUUID(&pImage->Header)));
    2143 
    2144         if (    pszComment
    2145             &&  cbComment > 0)
    2146         {
    2147             char *pszTmp = getImageComment(&pImage->Header);
    2148             unsigned cb = strlen(pszTmp);
    2149             if (cbComment > cb)
    2150                 memcpy(pszComment, pszTmp, cb + 1);
    2151             else
    2152                 rc = VERR_BUFFER_OVERFLOW;
    2153         }
    2154         if (VBOX_SUCCESS(rc))
    2155         {
    2156             if (puVersion)
    2157                 *puVersion = pImage->PreHeader.u32Version;
    2158             if (penmType)
    2159                 *penmType = getImageType(&pImage->Header);
    2160             if (pcbSize)
    2161                 *pcbSize = getImageDiskSize(&pImage->Header);
    2162             if (pUuid)
    2163                 *pUuid = *getImageCreationUUID(&pImage->Header);
    2164             if (pParentUuid)
    2165                 *pParentUuid = *getImageParentUUID(&pImage->Header);
    2166         }
    2167         vdiCloseImage(pImage);
    2168     }
    2169 
    2170     LogFlow(("VDICheckImage: returns %Vrc\n", rc));
    2171     return rc;
    2172 }
    2173 
    2174 /**
    2175  * Changes an image's comment string.
    2176  *
    2177  * @returns VBox status code.
    2178  * @param   pszFilename     Name of the image file to operate on.
    2179  * @param   pszComment      New comment string (UTF-8). NULL is allowed to reset the comment.
    2180  */
    2181 IDER3DECL(int) VDISetImageComment(const char *pszFilename, const char *pszComment)
    2182 {
    2183     LogFlow(("VDISetImageComment:\n"));
    2184 
    2185     /*
    2186      * Validate arguments.
    2187      */
    2188     if (    !pszFilename
    2189         ||  *pszFilename == '\0')
    2190     {
    2191         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2192         return VERR_INVALID_PARAMETER;
    2193     }
    2194 
    2195     const size_t cchComment = pszComment ? strlen(pszComment) : 0;
    2196     if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
    2197     {
    2198         Log(("VDISetImageComment: pszComment is too long, %d bytes!\n", cchComment));
    2199         return VERR_VDI_COMMENT_TOO_LONG;
    2200     }
    2201 
    2202     /*
    2203      * Open the image for updating.
    2204      */
    2205     PVDIIMAGEDESC pImage;
    2206     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2207     if (VBOX_FAILURE(rc))
    2208     {
    2209         Log(("VDISetImageComment: vdiOpenImage rc=%Vrc filename=\"%s\"!\n", rc, pszFilename));
    2210         return rc;
    2211     }
    2212     if (!pImage->fReadOnly)
    2213     {
    2214         /* we don't support old style images */
    2215         if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
    2216         {
    2217             /*
    2218              * Update the comment field, making sure to zero out all of the previous comment.
    2219              */
    2220             memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
    2221             memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
    2222 
    2223             /* write out new the header */
    2224             rc = vdiUpdateHeader(pImage);
    2225             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    2226                              pImage->szFilename, rc));
    2227         }
    2228         else
    2229         {
    2230             Log(("VDISetImageComment: Unsupported version!\n"));
    2231             rc = VERR_VDI_UNSUPPORTED_VERSION;
    2232         }
    2233     }
    2234     else
    2235     {
    2236         Log(("VDISetImageComment: image \"%s\" is opened as read-only!\n", pszFilename));
    2237         rc = VERR_VDI_IMAGE_READ_ONLY;
    2238     }
    2239 
    2240     vdiCloseImage(pImage);
    2241     return rc;
    2242 }
    2243 
    2244 /**
    2245  * Creates a new base image file.
    2246  *
    2247  * @returns VBox status code.
    2248  * @param   pszFilename     Name of the creating image file.
    2249  * @param   enmType         Image type, only base image types are acceptable.
    2250  * @param   cbSize          Image size in bytes.
    2251  * @param   pszComment      Pointer to image comment. NULL is ok.
    2252  * @param   pfnProgress     Progress callback. Optional.
    2253  * @param   pvUser          User argument for the progress callback.
    2254  */
    2255 IDER3DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize,
    2256                                   const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    2257 {
    2258     LogFlow(("VDICreateBaseImage:\n"));
    2259 
    2260     /* Check arguments. */
    2261     if (    !pszFilename
    2262         ||  *pszFilename == '\0'
    2263         ||  (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
    2264         ||  cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
    2265     {
    2266         AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
    2267                          pszFilename, enmType, cbSize));
    2268         return VERR_INVALID_PARAMETER;
    2269     }
    2270 
    2271     int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
    2272                             pfnProgress, pvUser);
    2273     LogFlow(("VDICreateBaseImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2274     return rc;
    2275 }
    2276 
    2277 /**
    2278  * Creates a differencing dynamically growing image file for specified parent image.
    2279  *
    2280  * @returns VBox status code.
    2281  * @param   pszFilename     Name of the creating differencing image file.
    2282  * @param   pszParent       Name of the parent image file. May be base or diff image type.
    2283  * @param   pszComment      Pointer to image comment. NULL is ok.
    2284  * @param   pfnProgress     Progress callback. Optional.
    2285  * @param   pvUser          User argument for the progress callback.
    2286  */
    2287 IDER3DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent,
    2288                                         const char *pszComment, PFNVMPROGRESS pfnProgress,
    2289                                         void *pvUser)
    2290 {
    2291     LogFlow(("VDICreateDifferenceImage:\n"));
    2292 
    2293     /* Check arguments. */
    2294     if (    !pszFilename
    2295         ||  *pszFilename == '\0'
    2296         ||  !pszParent
    2297         ||  *pszParent == '\0')
    2298     {
    2299         AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
    2300                          pszFilename, pszParent));
    2301         return VERR_INVALID_PARAMETER;
    2302     }
    2303 
    2304     PVDIIMAGEDESC pParent;
    2305     int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
    2306     if (VBOX_SUCCESS(rc))
    2307     {
    2308         rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
    2309                             getImageDiskSize(&pParent->Header), pszComment, pParent,
    2310                             pfnProgress, pvUser);
    2311         vdiCloseImage(pParent);
    2312     }
    2313     LogFlow(("VDICreateDifferenceImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2314     return rc;
    2315 }
    2316 
    2317 /**
    2318  * Deletes an image. Only valid image files can be deleted by this call.
    2319  *
    2320  * @returns VBox status code.
    2321  * @param   pszFilename     Name of the image file to check.
    2322  */
    2323 IDER3DECL(int) VDIDeleteImage(const char *pszFilename)
    2324 {
    2325     LogFlow(("VDIDeleteImage:\n"));
    2326     /* Check arguments. */
    2327     if (    !pszFilename
    2328         ||  *pszFilename == '\0')
    2329     {
    2330         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2331         return VERR_INVALID_PARAMETER;
    2332     }
    2333 
    2334     int rc = VDICheckImage(pszFilename, NULL, NULL, NULL, NULL, NULL, NULL, 0);
    2335     if (VBOX_SUCCESS(rc))
    2336         rc = RTFileDelete(pszFilename);
    2337 
    2338     LogFlow(("VDIDeleteImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2339     return rc;
    2340 }
    2341 
    2342 /**
    2343  * Makes a copy of image file with a new (other) creation uuid.
    2344  *
    2345  * @returns VBox status code.
    2346  * @param   pszDstFilename  Name of the image file to create.
    2347  * @param   pszSrcFilename  Name of the image file to copy from.
    2348  * @param   pszComment      Pointer to image comment. If NULL specified comment
    2349  *                          will be copied from source image.
    2350  * @param   pfnProgress     Progress callback. Optional.
    2351  * @param   pvUser          User argument for the progress callback.
    2352  */
    2353 IDER3DECL(int) VDICopyImage(const char *pszDstFilename, const char *pszSrcFilename,
    2354                             const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
    2355 {
    2356     LogFlow(("VDICopyImage:\n"));
    2357 
    2358     /* Check arguments. */
    2359     if (    !pszDstFilename
    2360         ||  *pszDstFilename == '\0'
    2361         ||  !pszSrcFilename
    2362         ||  *pszSrcFilename == '\0')
    2363     {
    2364         AssertMsgFailed(("Invalid arguments: pszDstFilename=%p pszSrcFilename=%p\n",
    2365                          pszDstFilename, pszSrcFilename));
    2366         return VERR_INVALID_PARAMETER;
    2367     }
    2368 
    2369     /* Special check for comment length. */
    2370     if (    pszComment
    2371         &&  strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
    2372     {
    2373         Log(("VDICopyImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
    2374         return VERR_VDI_COMMENT_TOO_LONG;
    2375     }
    2376 
    2377     PVDIIMAGEDESC pImage;
    2378     int rc = vdiOpenImage(&pImage, pszSrcFilename, VDI_OPEN_FLAGS_READONLY, NULL);
    2379     if (VBOX_FAILURE(rc))
    2380     {
    2381         Log(("VDICopyImage: src image \"%s\" open failed rc=%Vrc\n", pszSrcFilename, rc));
    2382         return rc;
    2383     }
    2384 
    2385     uint64_t cbFile = pImage->offStartData
    2386                     + ((uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset);
    2387 
    2388     /* create file */
    2389     RTFILE File;
    2390     rc = RTFileOpen(&File,
    2391                     pszDstFilename,
    2392                     RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
    2393     if (VBOX_SUCCESS(rc))
    2394     {
    2395         /* lock new image exclusively to close any wrong access by VDI API calls. */
    2396         rc = RTFileLock(File, RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbFile);
    2397         if (VBOX_SUCCESS(rc))
    2398         {
    2399             /* Set the size of a new file. */
    2400             rc = RTFileSetSize(File, cbFile);
    2401             if (VBOX_SUCCESS(rc))
    2402             {
    2403                 /* A dirty trick - use original image data to fill the new image. */
    2404                 RTFILE oldFileHandle = pImage->File;
    2405                 pImage->File = File;
    2406                 pImage->fReadOnly = false;
    2407 
    2408                 /* generate a new image creation uuid. */
    2409                 RTUuidCreate(getImageCreationUUID(&pImage->Header));
    2410                 /* generate a new image last-modified uuid. */
    2411                 RTUuidCreate(getImageModificationUUID(&pImage->Header));
    2412                 /* set image comment, if present. */
    2413                 if (pszComment)
    2414                     strncpy(getImageComment(&pImage->Header), pszComment, VDI_IMAGE_COMMENT_SIZE);
    2415 
    2416                 /* Write the pre-header to new image. */
    2417                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2418                 if (VBOX_SUCCESS(rc))
    2419                     rc = RTFileWrite(pImage->File,
    2420                                      &pImage->PreHeader,
    2421                                      sizeof(pImage->PreHeader),
    2422                                      NULL);
    2423 
    2424                 /* Write the header and the blocks array to new image. */
    2425                 if (VBOX_SUCCESS(rc))
    2426                     rc = vdiUpdateBlocks(pImage);
    2427 
    2428                 pImage->File = oldFileHandle;
    2429                 pImage->fReadOnly = true;
    2430 
    2431                 /* Seek to the data start in both images. */
    2432                 if (VBOX_SUCCESS(rc))
    2433                     rc = RTFileSeek(pImage->File,
    2434                                     pImage->offStartData,
    2435                                     RTFILE_SEEK_BEGIN,
    2436                                     NULL);
    2437                 if (VBOX_SUCCESS(rc))
    2438                     rc = RTFileSeek(File,
    2439                                     pImage->offStartData,
    2440                                     RTFILE_SEEK_BEGIN,
    2441                                     NULL);
    2442 
    2443                 if (VBOX_SUCCESS(rc))
    2444                 {
    2445                     /* alloc tmp buffer */
    2446                     void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    2447                     if (pvBuf)
    2448                     {
    2449                         /* Main copy loop. */
    2450                         uint64_t cbData = cbFile - pImage->offStartData;
    2451                         unsigned cBlocks = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2452                         unsigned c = 0;
    2453 
    2454                         while (cbData)
    2455                         {
    2456                             unsigned cbToCopy = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2457 
    2458                             /* Read. */
    2459                             rc = RTFileRead(pImage->File, pvBuf, cbToCopy, NULL);
    2460                             if (VBOX_FAILURE(rc))
    2461                                 break;
    2462 
    2463                             /* Write. */
    2464                             rc = RTFileWrite(File, pvBuf, cbToCopy, NULL);
    2465                             if (VBOX_FAILURE(rc))
    2466                                 break;
    2467 
    2468                             if (pfnProgress)
    2469                             {
    2470                                 c++;
    2471                                 rc = pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2472                                                  (c * 100) / cBlocks,
    2473                                                  pvUser);
    2474                                 if (VBOX_FAILURE(rc))
    2475                                     break;
    2476                             }
    2477                             cbData -= cbToCopy;
    2478                         }
    2479 
    2480                         RTMemTmpFree(pvBuf);
    2481                     }
    2482                     else
    2483                         rc = VERR_NO_MEMORY;
    2484                 }
    2485             }
    2486 
    2487             RTFileUnlock(File, 0, cbFile);
    2488         }
    2489 
    2490         RTFileClose(File);
    2491 
    2492         if (VBOX_FAILURE(rc))
    2493             RTFileDelete(pszDstFilename);
    2494 
    2495         if (pfnProgress)
    2496             pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2497     }
    2498 
    2499     vdiCloseImage(pImage);
    2500 
    2501     LogFlow(("VDICopyImage: returns %Vrc for pszSrcFilename=\"%s\" pszDstFilename=\"%s\"\n",
    2502              rc, pszSrcFilename, pszDstFilename));
    2503     return rc;
    2504 }
    2505 
    2506 /**
    2507  * Shrinks growing image file by removing zeroed data blocks.
    2508  *
    2509  * @returns VBox status code.
    2510  * @param   pszFilename     Name of the image file to shrink.
    2511  * @param   pfnProgress     Progress callback. Optional.
    2512  * @param   pvUser          User argument for the progress callback.
    2513  */
    2514 IDER3DECL(int) VDIShrinkImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2515 {
    2516     LogFlow(("VDIShrinkImage:\n"));
    2517 
    2518     /* Check arguments. */
    2519     if (    !pszFilename
    2520         ||  *pszFilename == '\0')
    2521     {
    2522         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2523         return VERR_INVALID_PARAMETER;
    2524     }
    2525 
    2526     PVDIIMAGEDESC pImage;
    2527     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2528     if (VBOX_FAILURE(rc))
    2529     {
    2530         Log(("VDIShrinkImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2531         return rc;
    2532     }
    2533     if (pImage->fReadOnly)
    2534     {
    2535         Log(("VDIShrinkImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2536         vdiCloseImage(pImage);
    2537         return VERR_VDI_IMAGE_READ_ONLY;
    2538     }
    2539 
    2540     /* Do debug dump. */
    2541     vdiDumpImage(pImage);
    2542 
    2543     /* Working data. */
    2544     unsigned cbBlock          = getImageBlockSize(&pImage->Header);
    2545     unsigned cBlocks          = getImageBlocks(&pImage->Header);
    2546     unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
    2547 
    2548     uint64_t cbFile;
    2549     rc = RTFileGetSize(pImage->File, &cbFile);
    2550     if (VBOX_FAILURE(rc))
    2551     {
    2552         Log(("VDIShrinkImage: RTFileGetSize rc=%Vrc for file=\"%s\"\n", rc, pszFilename));
    2553         vdiCloseImage(pImage);
    2554         return rc;
    2555     }
    2556 
    2557     uint64_t cbData = cbFile - pImage->offStartData;
    2558     unsigned cBlocksAllocated2 = (unsigned)(cbData >> pImage->uShiftIndex2Offset);
    2559     if (cbData != (uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
    2560         Log(("VDIShrinkImage: invalid image file length, cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2561              cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2562 
    2563     /* Allocate second blocks array for back resolving. */
    2564     PVDIIMAGEBLOCKPOINTER paBlocks2 =
    2565                 (PVDIIMAGEBLOCKPOINTER)RTMemTmpAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks);
    2566     if (!paBlocks2)
    2567     {
    2568         Log(("VDIShrinkImage: failed to allocate paBlocks2 buffer (%u bytes)\n", sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks));
    2569         vdiCloseImage(pImage);
    2570         return VERR_NO_MEMORY;
    2571     }
    2572 
    2573     /* Init second blocks array. */
    2574     for (unsigned n = 0; n < cBlocks; n++)
    2575         paBlocks2[n] = VDI_IMAGE_BLOCK_FREE;
    2576 
    2577     /* Fill second blocks array, check for allocational errors. */
    2578     for (unsigned n = 0; n < cBlocks; n++)
    2579     {
    2580         if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[n]))
    2581         {
    2582             unsigned uBlock = pImage->paBlocks[n];
    2583             if (uBlock < cBlocksAllocated2)
    2584             {
    2585                 if (paBlocks2[uBlock] == VDI_IMAGE_BLOCK_FREE)
    2586                     paBlocks2[uBlock] = n;
    2587                 else
    2588                 {
    2589                     Log(("VDIShrinkImage: block n=%u -> uBlock=%u is already in use!\n", n, uBlock));
    2590                     /* free second link to block. */
    2591                     pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
    2592                 }
    2593             }
    2594             else
    2595             {
    2596                 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is out of blocks range! (cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu)\n",
    2597                      n, uBlock, cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2598                 /* free link to invalid block. */
    2599                 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
    2600             }
    2601         }
    2602     }
    2603 
    2604     /* Allocate a working buffer for one block. */
    2605     void *pvBuf = RTMemTmpAlloc(cbBlock);
    2606     if (pvBuf)
    2607     {
    2608         /* Main voodoo loop, search holes and fill it. */
    2609         unsigned uBlockWrite = 0;
    2610         for (unsigned uBlock = 0; uBlock < cBlocksAllocated2; uBlock++)
    2611         {
    2612             if (paBlocks2[uBlock] != VDI_IMAGE_BLOCK_FREE)
    2613             {
    2614                 /* Read the block from file and check for zeroes. */
    2615                 uint64_t u64Offset = ((uint64_t)uBlock << pImage->uShiftIndex2Offset)
    2616                                    + (pImage->offStartData + pImage->offStartBlockData);
    2617                 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2618                 if (VBOX_FAILURE(rc))
    2619                 {
    2620                     Log(("VDIShrinkImage: seek rc=%Vrc filename=\"%s\" uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2621                          rc, pImage->szFilename, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2622                     break;
    2623                 }
    2624                 rc = RTFileRead(pImage->File, pvBuf, cbBlock, NULL);
    2625                 if (VBOX_FAILURE(rc))
    2626                 {
    2627                     Log(("VDIShrinkImage: read rc=%Vrc filename=\"%s\" cbBlock=%u uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2628                          rc, pImage->szFilename, cbBlock, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2629                     break;
    2630                 }
    2631 
    2632                 /* Check block for data. */
    2633                 Assert(cbBlock % 4 == 0);
    2634                 if (ASMBitFirstSet(pvBuf, cbBlock * 8) != -1)
    2635                 {
    2636                     /* Block has a data, may be it must be moved. */
    2637                     if (uBlockWrite < uBlock)
    2638                     {
    2639                         /* Move the block. */
    2640                         u64Offset = ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)
    2641                                   + (pImage->offStartData + pImage->offStartBlockData);
    2642                         rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
    2643                         if (VBOX_FAILURE(rc))
    2644                         {
    2645                             Log(("VDIShrinkImage: seek(2) rc=%Vrc filename=\"%s\" uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2646                                  rc, pImage->szFilename, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2647                             break;
    2648                         }
    2649                         rc = RTFileWrite(pImage->File, pvBuf, cbBlock, NULL);
    2650                         if (VBOX_FAILURE(rc))
    2651                         {
    2652                             Log(("VDIShrinkImage: write rc=%Vrc filename=\"%s\" cbBlock=%u uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
    2653                                  rc, pImage->szFilename, cbBlock, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
    2654                             break;
    2655                         }
    2656                     }
    2657                     /* Fix the block pointer. */
    2658                     pImage->paBlocks[paBlocks2[uBlock]] = uBlockWrite;
    2659                     uBlockWrite++;
    2660                 }
    2661                 else
    2662                 {
    2663                     Log(("VDIShrinkImage: found a zeroed block, uBlock=%u\n", uBlock));
    2664 
    2665                     /* Fix the block pointer. */
    2666                     pImage->paBlocks[paBlocks2[uBlock]] = VDI_IMAGE_BLOCK_ZERO;
    2667                 }
    2668             }
    2669             else
    2670                 Log(("VDIShrinkImage: found an unused block, uBlock=%u\n", uBlock));
    2671 
    2672             if (pfnProgress)
    2673             {
    2674                 pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2675                             (uBlock * 100) / cBlocksAllocated2,
    2676                             pvUser);
    2677                 /* Shrink is unbreakable operation! */
    2678             }
    2679         }
    2680 
    2681         RTMemTmpFree(pvBuf);
    2682 
    2683         if (    VBOX_SUCCESS(rc)
    2684             &&  uBlockWrite < cBlocksAllocated2)
    2685         {
    2686             /* File size must be shrinked. */
    2687             Log(("VDIShrinkImage: shrinking file size from %llu to %llu bytes\n",
    2688                  cbFile,
    2689                  pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)));
    2690             rc = RTFileSetSize(pImage->File,
    2691                                pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset));
    2692             if (VBOX_FAILURE(rc))
    2693                 Log(("VDIShrinkImage: RTFileSetSize rc=%Vrc\n", rc));
    2694         }
    2695         cBlocksAllocated2 = uBlockWrite;
    2696     }
    2697     else
    2698     {
    2699         Log(("VDIShrinkImage: failed to allocate working buffer (%u bytes)\n", cbBlock));
    2700         rc = VERR_NO_MEMORY;
    2701     }
    2702 
    2703     /* Save header and blocks array. */
    2704     if (VBOX_SUCCESS(rc))
    2705     {
    2706         setImageBlocksAllocated(&pImage->Header, cBlocksAllocated2);
    2707         rc = vdiUpdateBlocks(pImage);
    2708         if (pfnProgress)
    2709             pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    2710     }
    2711 
    2712     /* Do debug dump. */
    2713     vdiDumpImage(pImage);
    2714 
    2715     /* Clean up. */
    2716     RTMemTmpFree(paBlocks2);
    2717     vdiCloseImage(pImage);
    2718 
    2719     LogFlow(("VDIShrinkImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2720     return rc;
    2721 }
    2722 
    2723 /**
    2724  * Converts image file from older VDI formats to current one.
    2725  *
    2726  * @returns VBox status code.
    2727  * @param   pszFilename     Name of the image file to convert.
    2728  * @param   pfnProgress     Progress callback. Optional.
    2729  * @param   pvUser          User argument for the progress callback.
    2730  * @remark  Only used by vditool
    2731  */
    2732 IDER3DECL(int) VDIConvertImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
    2733 {
    2734     LogFlow(("VDIConvertImage:\n"));
    2735 
    2736     /* Check arguments. */
    2737     if (    !pszFilename
    2738         ||  *pszFilename == '\0')
    2739     {
    2740         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2741         return VERR_INVALID_PARAMETER;
    2742     }
    2743 
    2744     PVDIIMAGEDESC pImage;
    2745     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2746     if (VBOX_FAILURE(rc))
    2747     {
    2748         Log(("VDIConvertImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2749         return rc;
    2750     }
    2751 
    2752     VDIHEADER Header = {0};
    2753     int off;
    2754     uint64_t cbFile;
    2755     uint64_t cbData;
    2756 
    2757     if (pImage->fReadOnly)
    2758     {
    2759         Log(("VDIConvertImage: image \"%s\" is opened as read-only!\n", pszFilename));
    2760         rc = VERR_VDI_IMAGE_READ_ONLY;
    2761         goto l_conversion_failed;
    2762     }
    2763 
    2764     if (pImage->PreHeader.u32Version != 0x00000002)
    2765     {
    2766         Log(("VDIConvertImage: unsupported version=%08X filename=\"%s\"\n",
    2767              pImage->PreHeader.u32Version, pszFilename));
    2768         rc = VERR_VDI_UNSUPPORTED_VERSION;
    2769         goto l_conversion_failed;
    2770     }
    2771 
    2772     /* Build new version header from old one. */
    2773     vdiInitHeader(&Header,
    2774                   getImageType(&pImage->Header),
    2775                   VDI_IMAGE_FLAGS_DEFAULT,    /* Safety issue: Always use default flags. */
    2776                   getImageComment(&pImage->Header),
    2777                   getImageDiskSize(&pImage->Header),
    2778                   getImageBlockSize(&pImage->Header),
    2779                   0);
    2780     setImageBlocksAllocated(&Header, getImageBlocksAllocated(&pImage->Header));
    2781     *getImageGeometry(&Header) = *getImageGeometry(&pImage->Header);
    2782     setImageTranslation(&Header, getImageTranslation(&pImage->Header));
    2783     *getImageCreationUUID(&Header) = *getImageCreationUUID(&pImage->Header);
    2784     *getImageModificationUUID(&Header) = *getImageModificationUUID(&pImage->Header);
    2785 
    2786     /* Calc data offset. */
    2787     off = getImageDataOffset(&Header) - getImageDataOffset(&pImage->Header);
    2788     if (off <= 0)
    2789     {
    2790         rc = VERR_VDI_INVALID_HEADER;
    2791         goto l_conversion_failed;
    2792     }
    2793 
    2794     rc = RTFileGetSize(pImage->File, &cbFile);
    2795     if (VBOX_FAILURE(rc))
    2796         goto l_conversion_failed;
    2797 
    2798     /* Check file size. */
    2799     cbData = cbFile - getImageDataOffset(&pImage->Header);
    2800     if (cbData != (uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset)
    2801     {
    2802         AssertMsgFailed(("Invalid file size, broken image?\n"));
    2803         rc = VERR_VDI_INVALID_HEADER;
    2804         goto l_conversion_failed;
    2805     }
    2806 
    2807     /* Expand file. */
    2808     rc = RTFileSetSize(pImage->File, cbFile + off);
    2809     if (VBOX_FAILURE(rc))
    2810         goto l_conversion_failed;
    2811 
    2812     if (cbData > 0)
    2813     {
    2814         /* Calc current file position to move data from. */
    2815         uint64_t offFile;
    2816         if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2817             offFile = cbFile - VDIDISK_DEFAULT_BUFFER_SIZE;
    2818         else
    2819             offFile = getImageDataOffset(&pImage->Header);
    2820 
    2821         unsigned cMoves = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
    2822         unsigned c = 0;
    2823 
    2824         /* alloc tmp buffer */
    2825         void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
    2826         if (pvBuf)
    2827         {
    2828             /* Move data. */
    2829             for (;;)
    2830             {
    2831                 unsigned cbToMove = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
    2832 
    2833                 /* Read. */
    2834                 rc = RTFileSeek(pImage->File, offFile, RTFILE_SEEK_BEGIN, NULL);
    2835                 if (VBOX_FAILURE(rc))
    2836                     break;
    2837                 rc = RTFileRead(pImage->File, pvBuf, cbToMove, NULL);
    2838                 if (VBOX_FAILURE(rc))
    2839                     break;
    2840 
    2841                 /* Write. */
    2842                 rc = RTFileSeek(pImage->File, offFile + off, RTFILE_SEEK_BEGIN, NULL);
    2843                 if (VBOX_FAILURE(rc))
    2844                     break;
    2845                 rc = RTFileWrite(pImage->File, pvBuf, cbToMove, NULL);
    2846                 if (VBOX_FAILURE(rc))
    2847                     break;
    2848 
    2849                 if (pfnProgress)
    2850                 {
    2851                     c++;
    2852                     pfnProgress(NULL /* WARNING! pVM=NULL  */,
    2853                                 (c * 100) / cMoves,
    2854                                 pvUser);
    2855                     /* Note: conversion is non breakable operation, skipping rc here. */
    2856                 }
    2857 
    2858                 cbData -= cbToMove;
    2859                 if (cbData == 0)
    2860                     break;
    2861 
    2862                 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
    2863                     offFile -= VDIDISK_DEFAULT_BUFFER_SIZE;
    2864                 else
    2865                     offFile = getImageDataOffset(&pImage->Header);
    2866             }
    2867 
    2868             /* Fill the beginning of file with zeroes to wipe out old headers etc. */
    2869             if (VBOX_SUCCESS(rc))
    2870             {
    2871                 Assert(offFile + off <= VDIDISK_DEFAULT_BUFFER_SIZE);
    2872                 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2873                 if (VBOX_SUCCESS(rc))
    2874                 {
    2875                     memset(pvBuf, 0, (unsigned)offFile + off);
    2876                     rc = RTFileWrite(pImage->File, pvBuf, (unsigned)offFile + off, NULL);
    2877                 }
    2878             }
    2879 
    2880             RTMemTmpFree(pvBuf);
    2881         }
    2882         else
    2883             rc = VERR_NO_MEMORY;
    2884 
    2885         if (VBOX_FAILURE(rc))
    2886             goto l_conversion_failed;
    2887     }
    2888 
    2889     if (pfnProgress)
    2890     {
    2891         pfnProgress(NULL /* WARNING! pVM=NULL  */, 100, pvUser);
    2892        /* Note: conversion is non breakable operation, skipping rc here. */
    2893     }
    2894 
    2895     /* Data moved, now we need to save new pre header, header and blocks array. */
    2896 
    2897     vdiInitPreHeader(&pImage->PreHeader);
    2898     pImage->Header = Header;
    2899 
    2900     /* Setup image parameters by header. */
    2901     vdiSetupImageDesc(pImage);
    2902 
    2903     /* Write pre-header. */
    2904     rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
    2905     if (VBOX_FAILURE(rc))
    2906         goto l_conversion_failed;
    2907     rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
    2908     if (VBOX_FAILURE(rc))
    2909         goto l_conversion_failed;
    2910 
    2911     /* Write header and blocks array. */
    2912     rc = vdiUpdateBlocks(pImage);
    2913 
    2914 l_conversion_failed:
    2915     vdiCloseImage(pImage);
    2916 
    2917     LogFlow(("VDIConvertImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
    2918     return rc;
    2919 }
    2920 
    2921 /**
    2922  * Queries the image's UUID and parent UUIDs.
    2923  *
    2924  * @returns VBox status code.
    2925  * @param   pszFilename             Name of the image file to operate on.
    2926  * @param   pUuid                   Where to store image UUID (can be NULL).
    2927  * @param   pModificationUuid       Where to store modification UUID (can be NULL).
    2928  * @param   pParentUuuid            Where to store parent UUID (can be NULL).
    2929  * @param   pParentModificationUuid Where to store parent modification UUID (can be NULL).
    2930  */
    2931 IDER3DECL(int) VDIGetImageUUIDs(const char *pszFilename,
    2932                                 PRTUUID pUuid, PRTUUID pModificationUuid,
    2933                                 PRTUUID pParentUuid, PRTUUID pParentModificationUuid)
    2934 {
    2935     LogFlow(("VDIGetImageUUIDs:\n"));
    2936 
    2937     /* Check arguments. */
    2938     if (    !pszFilename
    2939         ||  *pszFilename == '\0')
    2940     {
    2941         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    2942         return VERR_INVALID_PARAMETER;
    2943     }
    2944 
    2945     /*
    2946      * Try open the specified image.
    2947      */
    2948     PVDIIMAGEDESC pImage;
    2949     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    2950     if (VBOX_FAILURE(rc))
    2951     {
    2952         Log(("VDIGetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    2953         return rc;
    2954     }
    2955 
    2956     /*
    2957      * Query data.
    2958      */
    2959     if (pUuid)
    2960     {
    2961         PCRTUUID pTmpUuid = getImageCreationUUID(&pImage->Header);
    2962         if (pTmpUuid)
    2963             *pUuid = *pTmpUuid;
    2964         else
    2965             RTUuidClear(pUuid);
    2966     }
    2967     if (pModificationUuid)
    2968     {
    2969         PCRTUUID pTmpUuid = getImageModificationUUID(&pImage->Header);
    2970         if (pTmpUuid)
    2971             *pModificationUuid = *pTmpUuid;
    2972         else
    2973             RTUuidClear(pModificationUuid);
    2974     }
    2975     if (pParentUuid)
    2976     {
    2977         PCRTUUID pTmpUuid = getImageParentUUID(&pImage->Header);
    2978         if (pTmpUuid)
    2979             *pParentUuid = *pTmpUuid;
    2980         else
    2981             RTUuidClear(pParentUuid);
    2982     }
    2983     if (pParentModificationUuid)
    2984     {
    2985         PCRTUUID pTmpUuid = getImageParentModificationUUID(&pImage->Header);
    2986         if (pTmpUuid)
    2987             *pParentModificationUuid = *pTmpUuid;
    2988         else
    2989             RTUuidClear(pParentModificationUuid);
    2990     }
    2991 
    2992     /*
    2993      * Close the image.
    2994      */
    2995     vdiCloseImage(pImage);
    2996 
    2997     return VINF_SUCCESS;
    2998 }
    2999 
    3000 /**
    3001  * Changes the image's UUID and parent UUIDs.
    3002  *
    3003  * @returns VBox status code.
    3004  * @param   pszFilename             Name of the image file to operate on.
    3005  * @param   pUuid                   Optional parameter, new UUID of the image.
    3006  * @param   pModificationUuid       Optional parameter, new modification UUID of the image.
    3007  * @param   pParentUuuid            Optional parameter, new parent UUID of the image.
    3008  * @param   pParentModificationUuid Optional parameter, new parent modification UUID of the image.
    3009  */
    3010 IDER3DECL(int) VDISetImageUUIDs(const char *pszFilename,
    3011                                 PCRTUUID pUuid, PCRTUUID pModificationUuid,
    3012                                 PCRTUUID pParentUuid, PCRTUUID pParentModificationUuid)
    3013 {
    3014     LogFlow(("VDISetImageUUIDs:\n"));
    3015 
    3016     /* Check arguments. */
    3017     if (    !pszFilename
    3018         ||  *pszFilename == '\0')
    3019     {
    3020         AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
    3021         return VERR_INVALID_PARAMETER;
    3022     }
    3023 
    3024     PVDIIMAGEDESC pImage;
    3025     int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
    3026     if (VBOX_FAILURE(rc))
    3027     {
    3028         Log(("VDISetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
    3029         return rc;
    3030     }
    3031     if (!pImage->fReadOnly)
    3032     {
    3033         /* we only support new images */
    3034         if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
    3035         {
    3036             if (pUuid)
    3037                 pImage->Header.u.v1.uuidCreate = *pUuid;
    3038 
    3039             if (pModificationUuid)
    3040                 pImage->Header.u.v1.uuidModify = *pModificationUuid;
    3041 
    3042             if (pParentUuid)
    3043                 pImage->Header.u.v1.uuidLinkage = *pParentUuid;
    3044 
    3045             if (pParentModificationUuid)
    3046                 pImage->Header.u.v1.uuidParentModify = *pParentModificationUuid;
    3047 
    3048             /* write out new header */
    3049             rc = vdiUpdateHeader(pImage);
    3050             AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
    3051                              pImage->szFilename, rc));
    3052         }
    3053         else
    3054         {
    3055             Log(("VDISetImageUUIDs: Version is not supported!\n"));
    3056             rc = VERR_VDI_UNSUPPORTED_VERSION;
    3057         }
    3058     }
    3059     else
    3060     {
    3061         Log(("VDISetImageUUIDs: image \"%s\" is opened as read-only!\n", pszFilename));
    3062         rc = VERR_VDI_IMAGE_READ_ONLY;
    3063     }
    3064 
    3065     vdiCloseImage(pImage);
    3066     return rc;
    3067 }
    3068 
    3069 /**
    3070  * Merges two images having a parent/child relationship (both directions).
    3071  *
    3072  * @returns VBox status code.
    3073  * @param   pszFilenameFrom         Name of the image file to merge from.
    3074  * @param   pszFilenameTo           Name of the image file to merge into.
    3075  * @param   pfnProgress     Progress callback. Optional. NULL if not to be used.
    3076  * @param   pvUser          User argument for the progress callback.
    3077  */
    3078 IDER3DECL(int) VDIMergeImage(const char *pszFilenameFrom, const char *pszFilenameTo,
    3079                              PFNVMPROGRESS pfnProgress, void *pvUser)
    3080 {
    3081     LogFlow(("VDIMergeImage:\n"));
    3082 
    3083     /* Check arguments. */
    3084     if (    !pszFilenameFrom
    3085         ||  *pszFilenameFrom == '\0'
    3086         ||  !pszFilenameTo
    3087         ||  *pszFilenameTo   == '\0')
    3088     {
    3089         AssertMsgFailed(("Invalid arguments: pszFilenameFrom=%p, pszFilenameTo=%p\n", pszFilenameFrom, pszFilenameTo));
    3090         return VERR_INVALID_PARAMETER;
    3091     }
    3092 
    3093     PVDIIMAGEDESC pImageFrom;
    3094     int rc = vdiOpenImage(&pImageFrom, pszFilenameFrom, VDI_OPEN_FLAGS_READONLY, NULL);
    3095     if (VBOX_FAILURE(rc))
    3096     {
    3097         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pstFilenameFrom=\"%s\"\n", rc, pszFilenameFrom));
    3098         return rc;
    3099     }
    3100 
    3101     PVDIIMAGEDESC pImageTo;
    3102     rc = vdiOpenImage(&pImageTo, pszFilenameTo, VDI_OPEN_FLAGS_NORMAL, NULL);
    3103     if (VBOX_FAILURE(rc))
    3104     {
    3105         Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pszFilenameTo=\"%s\"\n", rc, pszFilenameTo));
    3106         vdiCloseImage(pImageFrom);
    3107         return rc;
    3108     }
    3109     if (pImageTo->fReadOnly)
    3110     {
    3111         Log(("VDIMergeImage: image \"%s\" is opened as read-only!\n", pszFilenameTo));
    3112         vdiCloseImage(pImageFrom);
    3113         vdiCloseImage(pImageTo);
    3114         return VERR_VDI_IMAGE_READ_ONLY;
    3115     }
    3116 
    3117     /*
    3118      *  when merging, we should not update the modification uuid of the target
    3119      *  image, because from the point of view of its children, it hasn't been
    3120      *  logically changed after the successful merge.
    3121      */
    3122     vdiDisableLastModifiedUpdate(pImageTo);
    3123 
    3124     /*
    3125      * Check in which direction we merge
    3126      */
    3127 
    3128     bool bParentToChild = false;
    3129     if (   getImageParentUUID(&pImageFrom->Header)
    3130         && !RTUuidCompare(getImageParentUUID(&pImageFrom->Header),
    3131                           getImageCreationUUID(&pImageTo->Header))
    3132         && !RTUuidCompare(getImageParentModificationUUID(&pImageFrom->Header),
    3133                           getImageModificationUUID(&pImageTo->Header)))
    3134     {
    3135         /* we merge from a child to its parent */
    3136     }
    3137     else
    3138     if (   getImageParentUUID(&pImageTo->Header)
    3139         && !RTUuidCompare(getImageParentUUID(&pImageTo->Header),
    3140                           getImageCreationUUID(&pImageFrom->Header))
    3141         && !RTUuidCompare(getImageParentModificationUUID(&pImageTo->Header),
    3142                           getImageModificationUUID(&pImageFrom->Header)))
    3143     {
    3144         /* we merge from a parent to its child */
    3145         bParentToChild = true;
    3146     }
    3147     else
    3148     {
    3149         /* the images are not related, we can't merge! */
    3150         Log(("VDIMergeImages: images do not have a parent/child or child/parent relationship!\n"));
    3151         rc = VERR_VDI_IMAGES_UUID_MISMATCH;
    3152     }
    3153 
    3154     rc = vdiMergeImages(pImageFrom, pImageTo, bParentToChild, pfnProgress, pvUser);
    3155 
    3156     if (pfnProgress)
    3157     {
    3158         pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
    3159        /* Note: commiting is non breakable operation, skipping rc here. */
    3160     }
    3161 
    3162     /* cleanup */
    3163     vdiCloseImage(pImageFrom);
    3164     vdiCloseImage(pImageTo);
    3165 
    3166     Log(("VDIMergeImage: done, returning with rc = %Vrc\n", rc));
    3167     return rc;
    3168 }
    3169 
    3170 
    3171 /**
    3172  * internal: initialize VDIDISK structure.
    3173  */
    3174 static void vdiInitVDIDisk(PVDIDISK pDisk)
    3175 {
    3176     Assert(pDisk);
    3177     pDisk->u32Signature = VDIDISK_SIGNATURE;
    3178     pDisk->cImages = 0;
    3179     pDisk->pBase   = NULL;
    3180     pDisk->pLast   = NULL;
    3181     pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
    3182     pDisk->cbBuf   = VDIDISK_DEFAULT_BUFFER_SIZE;
    3183     pDisk->fHonorZeroWrites = false;
    3184 }
    3185 
    3186 /**
    3187  * internal: add image structure to the end of images list.
    3188  */
    3189 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    3190 {
    3191     pImage->pPrev = NULL;
    3192     pImage->pNext = NULL;
    3193 
    3194     if (pDisk->pBase)
    3195     {
    3196         Assert(pDisk->cImages > 0);
    3197         pImage->pPrev = pDisk->pLast;
    3198         pDisk->pLast->pNext = pImage;
    3199         pDisk->pLast = pImage;
    3200     }
    3201     else
    3202     {
    3203         Assert(pDisk->cImages == 0);
    3204         pDisk->pBase = pImage;
    3205         pDisk->pLast = pImage;
    3206     }
    3207 
    3208     pDisk->cImages++;
    3209 }
    3210 
    3211 /**
    3212  * internal: remove image structure from the images list.
    3213  */
    3214 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
    3215 {
    3216     Assert(pDisk->cImages > 0);
    3217 
    3218     if (pImage->pPrev)
    3219         pImage->pPrev->pNext = pImage->pNext;
    3220     else
    3221         pDisk->pBase = pImage->pNext;
    3222 
    3223     if (pImage->pNext)
    3224         pImage->pNext->pPrev = pImage->pPrev;
    3225     else
    3226         pDisk->pLast = pImage->pPrev;
    3227 
    3228     pImage->pPrev = NULL;
    3229     pImage->pNext = NULL;
    3230 
    3231     pDisk->cImages--;
    3232 }
    3233 
    3234 /**
    3235  * Allocates and initializes VDI HDD container.
    3236  *
    3237  * @returns Pointer to newly created HDD container with no one opened image file.
    3238  * @returns NULL on failure, typically out of memory.
    3239  */
    3240 IDER3DECL(PVDIDISK) VDIDiskCreate(void)
    3241 {
    3242     PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
    3243     if (pDisk)
    3244         vdiInitVDIDisk(pDisk);
    3245     LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
    3246     return pDisk;
    3247 }
    3248 
    3249 /**
    3250  * Destroys VDI HDD container. If container has opened image files they will be closed.
    3251  *
    3252  * @param   pDisk           Pointer to VDI HDD container.
    3253  */
    3254 IDER3DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
    3255 {
    3256     LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
    3257     /* sanity check */
    3258     Assert(pDisk);
    3259     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3260 
    3261     if (pDisk)
    3262     {
    3263         VDIDiskCloseAllImages(pDisk);
    3264         RTMemFree(pDisk);
    3265     }
    3266 }
    3267 
    3268 /**
    3269  * Get working buffer size of VDI HDD container.
    3270  *
    3271  * @returns Working buffer size in bytes.
    3272  */
    3273 IDER3DECL(unsigned) VDIDiskGetBufferSize(PVDIDISK pDisk)
    3274 {
    3275     /* sanity check */
    3276     Assert(pDisk);
    3277     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3278 
    3279     LogFlow(("VDIDiskGetBufferSize: returns %u\n", pDisk->cbBuf));
    3280     return pDisk->cbBuf;
    3281 }
    3282 
    3283 /**
    3284  * Get read/write mode of VDI HDD.
    3285  *
    3286  * @returns Disk ReadOnly status.
    3287  * @returns true if no one VDI image is opened in HDD container.
    3288  */
    3289 IDER3DECL(bool) VDIDiskIsReadOnly(PVDIDISK pDisk)
    3290 {
    3291     /* sanity check */
    3292     Assert(pDisk);
    3293     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3294 
    3295     if (pDisk->pLast)
    3296     {
    3297         LogFlow(("VDIDiskIsReadOnly: returns %u\n", pDisk->pLast->fReadOnly));
    3298         return pDisk->pLast->fReadOnly;
    3299     }
    3300 
    3301     AssertMsgFailed(("No one disk image is opened!\n"));
    3302     return true;
    3303 }
    3304 
    3305 /**
    3306  * Get disk size of VDI HDD container.
    3307  *
    3308  * @returns Virtual disk size in bytes.
    3309  * @returns 0 if no one VDI image is opened in HDD container.
    3310  */
    3311 IDER3DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
    3312 {
    3313     /* sanity check */
    3314     Assert(pDisk);
    3315     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3316 
    3317     if (pDisk->pBase)
    3318     {
    3319         LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
    3320         return getImageDiskSize(&pDisk->pBase->Header);
    3321     }
    3322 
    3323     AssertMsgFailed(("No one disk image is opened!\n"));
    3324     return 0;
    3325 }
    3326 
    3327 /**
    3328  * Get block size of VDI HDD container.
    3329  *
    3330  * @returns VDI image block size in bytes.
    3331  * @returns 0 if no one VDI image is opened in HDD container.
    3332  */
    3333 IDER3DECL(unsigned) VDIDiskGetBlockSize(PVDIDISK pDisk)
    3334 {
    3335     /* sanity check */
    3336     Assert(pDisk);
    3337     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3338 
    3339     if (pDisk->pBase)
    3340     {
    3341         LogFlow(("VDIDiskGetBlockSize: returns %u\n", getImageBlockSize(&pDisk->pBase->Header)));
    3342         return getImageBlockSize(&pDisk->pBase->Header);
    3343     }
    3344 
    3345     AssertMsgFailed(("No one disk image is opened!\n"));
    3346     return 0;
    3347 }
    3348 
    3349 /**
    3350  * Get virtual disk geometry stored in image file.
    3351  *
    3352  * @returns VBox status code.
    3353  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3354  * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
    3355  * @param   pDisk           Pointer to VDI HDD container.
    3356  * @param   pcCylinders     Where to store the number of cylinders. NULL is ok.
    3357  * @param   pcHeads         Where to store the number of heads. NULL is ok.
    3358  * @param   pcSectors       Where to store the number of sectors. NULL is ok.
    3359  */
    3360 IDER3DECL(int) VDIDiskGetGeometry(PVDIDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
    3361 {
    3362     /* sanity check */
    3363     Assert(pDisk);
    3364     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3365 
    3366     if (pDisk->pBase)
    3367     {
    3368         int rc = VINF_SUCCESS;
    3369         PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
    3370         LogFlow(("VDIDiskGetGeometry: C/H/S = %u/%u/%u\n",
    3371                  pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
    3372         if (    pGeometry->cCylinders > 0
    3373             &&  pGeometry->cHeads > 0
    3374             &&  pGeometry->cSectors > 0)
    3375         {
    3376             if (pcCylinders)
    3377                 *pcCylinders = pGeometry->cCylinders;
    3378             if (pcHeads)
    3379                 *pcHeads = pGeometry->cHeads;
    3380             if (pcSectors)
    3381                 *pcSectors = pGeometry->cSectors;
    3382         }
    3383         else
    3384             rc = VERR_VDI_GEOMETRY_NOT_SET;
    3385 
    3386         LogFlow(("VDIDiskGetGeometry: returns %Vrc\n", rc));
    3387         return rc;
    3388     }
    3389 
    3390     AssertMsgFailed(("No one disk image is opened!\n"));
    3391     return VERR_VDI_NOT_OPENED;
    3392 }
    3393 
    3394 /**
    3395  * Store virtual disk geometry into base image file of HDD container.
    3396  *
    3397  * Note that in case of unrecoverable error all images of HDD container will be closed.
    3398  *
    3399  * @returns VBox status code.
    3400  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3401  * @param   pDisk           Pointer to VDI HDD container.
    3402  * @param   cCylinders      Number of cylinders.
    3403  * @param   cHeads          Number of heads.
    3404  * @param   cSectors        Number of sectors.
    3405  */
    3406 IDER3DECL(int) VDIDiskSetGeometry(PVDIDISK pDisk, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
    3407 {
    3408     LogFlow(("VDIDiskSetGeometry: C/H/S = %u/%u/%u\n", cCylinders, cHeads, cSectors));
    3409     /* sanity check */
    3410     Assert(pDisk);
    3411     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3412 
    3413     if (pDisk->pBase)
    3414     {
    3415         PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
    3416         pGeometry->cCylinders = cCylinders;
    3417         pGeometry->cHeads = cHeads;
    3418         pGeometry->cSectors = cSectors;
    3419         pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
    3420 
    3421         /* Update header information in base image file. */
    3422         int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
    3423         LogFlow(("VDIDiskSetGeometry: returns %Vrc\n", rc));
    3424         return rc;
    3425     }
    3426 
    3427     AssertMsgFailed(("No one disk image is opened!\n"));
    3428     return VERR_VDI_NOT_OPENED;
    3429 }
    3430 
    3431 /**
    3432  * Get virtual disk translation mode stored in image file.
    3433  *
    3434  * @returns VBox status code.
    3435  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3436  * @param   pDisk           Pointer to VDI HDD container.
    3437  * @param   penmTranslation Where to store the translation mode (see pdm.h).
    3438  */
    3439 IDER3DECL(int) VDIDiskGetTranslation(PVDIDISK pDisk, PPDMBIOSTRANSLATION penmTranslation)
    3440 {
    3441     /* sanity check */
    3442     Assert(pDisk);
    3443     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3444     Assert(penmTranslation);
    3445 
    3446     if (pDisk->pBase)
    3447     {
    3448         *penmTranslation = getImageTranslation(&pDisk->pBase->Header);
    3449         LogFlow(("VDIDiskGetTranslation: translation=%d\n", *penmTranslation));
    3450         return VINF_SUCCESS;
    3451     }
    3452 
    3453     AssertMsgFailed(("No one disk image is opened!\n"));
    3454     return VERR_VDI_NOT_OPENED;
    3455 }
    3456 
    3457 /**
    3458  * Store virtual disk translation mode into base image file of HDD container.
    3459  *
    3460  * Note that in case of unrecoverable error all images of HDD container will be closed.
    3461  *
    3462  * @returns VBox status code.
    3463  * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
    3464  * @param   pDisk           Pointer to VDI HDD container.
    3465  * @param   enmTranslation  Translation mode (see pdm.h).
    3466  */
    3467 IDER3DECL(int) VDIDiskSetTranslation(PVDIDISK pDisk, PDMBIOSTRANSLATION enmTranslation)
    3468 {
    3469     LogFlow(("VDIDiskSetTranslation: enmTranslation=%d\n", enmTranslation));
    3470     /* sanity check */
    3471     Assert(pDisk);
    3472     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3473 
    3474     if (pDisk->pBase)
    3475     {
    3476         setImageTranslation(&pDisk->pBase->Header, enmTranslation);
    3477 
    3478         /* Update header information in base image file. */
    3479         int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
    3480         LogFlow(("VDIDiskSetTranslation: returns %Vrc\n", rc));
    3481         return rc;
    3482     }
    3483 
    3484     AssertMsgFailed(("No one disk image is opened!\n"));
    3485     return VERR_VDI_NOT_OPENED;
    3486 }
    3487 
    3488 /**
    3489  * Get number of opened images in HDD container.
    3490  *
    3491  * @returns Number of opened images for HDD container. 0 if no images is opened.
    3492  * @param   pDisk           Pointer to VDI HDD container.
    3493  */
    3494 IDER3DECL(int) VDIDiskGetImagesCount(PVDIDISK pDisk)
    3495 {
    3496     /* sanity check */
    3497     Assert(pDisk);
    3498     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3499 
    3500     LogFlow(("VDIDiskGetImagesCount: returns %d\n", pDisk->cImages));
    3501     return pDisk->cImages;
    3502 }
    3503 
    3504 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage)
    3505 {
    3506     PVDIIMAGEDESC pImage = pDisk->pBase;
    3507     while (pImage && nImage)
    3508     {
    3509         pImage = pImage->pNext;
    3510         nImage--;
    3511     }
    3512     return pImage;
    3513 }
    3514 
    3515 /**
    3516  * Get version of opened image of HDD container.
    3517  *
    3518  * @returns VBox status code.
    3519  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3520  * @param   pDisk           Pointer to VDI HDD container.
    3521  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3522  * @param   puVersion       Where to store the image version.
    3523  */
    3524 IDER3DECL(int) VDIDiskGetImageVersion(PVDIDISK pDisk, int nImage, unsigned *puVersion)
    3525 {
    3526     /* sanity check */
    3527     Assert(pDisk);
    3528     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3529     Assert(puVersion);
    3530 
    3531     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3532     Assert(pImage);
    3533 
    3534     if (pImage)
    3535     {
    3536         *puVersion = pImage->PreHeader.u32Version;
    3537         LogFlow(("VDIDiskGetImageVersion: returns %08X\n", pImage->PreHeader.u32Version));
    3538         return VINF_SUCCESS;
    3539     }
    3540 
    3541     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3542     return VERR_VDI_IMAGE_NOT_FOUND;
    3543 }
    3544 
    3545 /**
    3546  * Get filename of opened image of HDD container.
    3547  *
    3548  * @returns VBox status code.
    3549  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3550  * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
    3551  * @param   pDisk           Pointer to VDI HDD container.
    3552  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3553  * @param   pszFilename     Where to store the image file name.
    3554  * @param   cbFilename      Size of buffer pszFilename points to.
    3555  */
    3556 IDER3DECL(int) VDIDiskGetImageFilename(PVDIDISK pDisk, int nImage, char *pszFilename, unsigned cbFilename)
    3557 {
    3558     /* sanity check */
    3559     Assert(pDisk);
    3560     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3561     Assert(pszFilename);
    3562 
    3563     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3564     Assert(pImage);
    3565 
    3566     if (pImage)
    3567     {
    3568         unsigned cb = strlen(pImage->szFilename);
    3569         if (cb < cbFilename)
    3570         {
    3571             /* memcpy is much better than strncpy. */
    3572             memcpy(pszFilename, pImage->szFilename, cb + 1);
    3573             LogFlow(("VDIDiskGetImageFilename: returns VINF_SUCCESS, filename=\"%s\" nImage=%d\n",
    3574                      pszFilename, nImage));
    3575             return VINF_SUCCESS;
    3576         }
    3577         else
    3578         {
    3579             AssertMsgFailed(("Out of buffer space, cbFilename=%d needed=%d\n", cbFilename, cb + 1));
    3580             return VERR_BUFFER_OVERFLOW;
    3581         }
    3582     }
    3583 
    3584     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3585     return VERR_VDI_IMAGE_NOT_FOUND;
    3586 }
    3587 
    3588 /**
    3589  * Get the comment line of opened image of HDD container.
    3590  *
    3591  * @returns VBox status code.
    3592  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3593  * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
    3594  * @param   pDisk           Pointer to VDI HDD container.
    3595  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3596  * @param   pszComment      Where to store the comment string of image. NULL is ok.
    3597  * @param   cbComment       The size of pszComment buffer. 0 is ok.
    3598  */
    3599 IDER3DECL(int) VDIDiskGetImageComment(PVDIDISK pDisk, int nImage, char *pszComment, unsigned cbComment)
    3600 {
    3601     /* sanity check */
    3602     Assert(pDisk);
    3603     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3604     Assert(pszComment);
    3605 
    3606     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3607     Assert(pImage);
    3608 
    3609     if (pImage)
    3610     {
    3611         char *pszTmp = getImageComment(&pImage->Header);
    3612         unsigned cb = strlen(pszTmp);
    3613         if (cb < cbComment)
    3614         {
    3615             /* memcpy is much better than strncpy. */
    3616             memcpy(pszComment, pszTmp, cb + 1);
    3617             LogFlow(("VDIDiskGetImageComment: returns VINF_SUCCESS, comment=\"%s\" nImage=%d\n",
    3618                      pszTmp, nImage));
    3619             return VINF_SUCCESS;
    3620         }
    3621         else
    3622         {
    3623             AssertMsgFailed(("Out of buffer space, cbComment=%d needed=%d\n", cbComment, cb + 1));
    3624             return VERR_BUFFER_OVERFLOW;
    3625         }
    3626     }
    3627 
    3628     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3629     return VERR_VDI_IMAGE_NOT_FOUND;
    3630 }
    3631 
    3632 /**
    3633  * Get type of opened image of HDD container.
    3634  *
    3635  * @returns VBox status code.
    3636  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3637  * @param   pDisk           Pointer to VDI HDD container.
    3638  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3639  * @param   penmType        Where to store the image type.
    3640  */
    3641 IDER3DECL(int) VDIDiskGetImageType(PVDIDISK pDisk, int nImage, PVDIIMAGETYPE penmType)
    3642 {
    3643     /* sanity check */
    3644     Assert(pDisk);
    3645     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3646     Assert(penmType);
    3647 
    3648     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3649     Assert(pImage);
    3650 
    3651     if (pImage)
    3652     {
    3653         *penmType = getImageType(&pImage->Header);
    3654         LogFlow(("VDIDiskGetImageType: returns VINF_SUCCESS, type=%X nImage=%d\n",
    3655                  *penmType, nImage));
    3656         return VINF_SUCCESS;
    3657     }
    3658 
    3659     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3660     return VERR_VDI_IMAGE_NOT_FOUND;
    3661 }
    3662 
    3663 /**
    3664  * Get flags of opened image of HDD container.
    3665  *
    3666  * @returns VBox status code.
    3667  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3668  * @param   pDisk           Pointer to VDI HDD container.
    3669  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3670  * @param   pfFlags         Where to store the image flags.
    3671  */
    3672 IDER3DECL(int) VDIDiskGetImageFlags(PVDIDISK pDisk, int nImage, unsigned *pfFlags)
    3673 {
    3674     /* sanity check */
    3675     Assert(pDisk);
    3676     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3677     Assert(pfFlags);
    3678 
    3679     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3680     Assert(pImage);
    3681 
    3682     if (pImage)
    3683     {
    3684         *pfFlags = getImageFlags(&pImage->Header);
    3685         LogFlow(("VDIDiskGetImageFlags: returns VINF_SUCCESS, flags=%08X nImage=%d\n",
    3686                  *pfFlags, nImage));
    3687         return VINF_SUCCESS;
    3688     }
    3689 
    3690     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3691     return VERR_VDI_IMAGE_NOT_FOUND;
    3692 }
    3693 
    3694 /**
    3695  * Get Uuid of opened image of HDD container.
    3696  *
    3697  * @returns VBox status code.
    3698  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3699  * @param   pDisk           Pointer to VDI HDD container.
    3700  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3701  * @param   pUuid           Where to store the image creation uuid.
    3702  */
    3703 IDER3DECL(int) VDIDiskGetImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3704 {
    3705     /* sanity check */
    3706     Assert(pDisk);
    3707     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3708     Assert(pUuid);
    3709 
    3710     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3711     Assert(pImage);
    3712 
    3713     if (pImage)
    3714     {
    3715         *pUuid = *getImageCreationUUID(&pImage->Header);
    3716         LogFlow(("VDIDiskGetImageUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3717                  pUuid, nImage));
    3718         return VINF_SUCCESS;
    3719     }
    3720 
    3721     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3722     return VERR_VDI_IMAGE_NOT_FOUND;
    3723 }
    3724 
    3725 /**
    3726  * Get last modification Uuid of opened image of HDD container.
    3727  *
    3728  * @returns VBox status code.
    3729  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3730  * @param   pDisk           Pointer to VDI HDD container.
    3731  * @param   nImage          Image number, counts from 0. 0 is always base image of container.
    3732  * @param   pUuid           Where to store the image modification uuid.
    3733  */
    3734 IDER3DECL(int) VDIDiskGetImageModificationUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3735 {
    3736     /* sanity check */
    3737     Assert(pDisk);
    3738     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3739     Assert(pUuid);
    3740 
    3741     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3742     Assert(pImage);
    3743 
    3744     if (pImage)
    3745     {
    3746         *pUuid = *getImageModificationUUID(&pImage->Header);
    3747         LogFlow(("VDIDiskGetImageModificationUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
    3748                  pUuid, nImage));
    3749         return VINF_SUCCESS;
    3750     }
    3751 
    3752     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3753     return VERR_VDI_IMAGE_NOT_FOUND;
    3754 }
    3755 
    3756 /**
    3757  * Get Uuid of opened image's parent image.
    3758  *
    3759  * @returns VBox status code.
    3760  * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
    3761  * @param   pDisk           Pointer to VDI HDD container.
    3762  * @param   nImage          Image number, counts from 0. 0 is always base image of the container.
    3763  * @param   pUuid           Where to store the image creation uuid.
    3764  */
    3765 IDER3DECL(int) VDIDiskGetParentImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
    3766 {
    3767     /* sanity check */
    3768     Assert(pDisk);
    3769     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3770     Assert(pUuid);
    3771 
    3772     PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
    3773     if (pImage)
    3774     {
    3775         *pUuid = *getImageParentUUID(&pImage->Header);
    3776         LogFlow(("VDIDiskGetParentImageUuid: returns VINF_SUCCESS, *pUuid={%Vuuid} nImage=%d\n",
    3777                  pUuid, nImage));
    3778         return VINF_SUCCESS;
    3779     }
    3780 
    3781     AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
    3782     return VERR_VDI_IMAGE_NOT_FOUND;
    3783 }
    3784 
    3785 /**
    3786  * internal: Relock image as read/write or read-only.
    3787  */
    3788 static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
    3789 {
    3790     Assert(pImage);
    3791 
    3792     if (    !fReadOnly
    3793         &&  pImage->fOpen & VDI_OPEN_FLAGS_READONLY)
    3794     {
    3795         /* Can't switch read-only opened image to read-write mode. */
    3796         Log(("vdiChangeImageMode: can't switch r/o image to r/w mode, filename=\"%s\" fOpen=%X\n",
    3797              pImage->szFilename, pImage->fOpen));
    3798         return VERR_VDI_IMAGE_READ_ONLY;
    3799     }
    3800 
    3801     /* Flush last image changes if was r/w mode. */
    3802     vdiFlushImage(pImage);
    3803 
    3804     /* Change image locking. */
    3805     uint64_t cbLock = pImage->offStartData
    3806                     + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
    3807     int rc = RTFileChangeLock(pImage->File,
    3808                               (fReadOnly) ?
    3809                                   RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
    3810                                   RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
    3811                               0,
    3812                               cbLock);
    3813     if (VBOX_SUCCESS(rc))
    3814     {
    3815         pImage->fReadOnly = fReadOnly;
    3816         Log(("vdiChangeImageMode: Image \"%s\" mode changed to %s\n",
    3817              pImage->szFilename, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3818         return VINF_SUCCESS;
    3819     }
    3820 
    3821     /* Check for the most bad error in the world. Damn! It must never happens in real life! */
    3822     if (rc == VERR_FILE_LOCK_LOST)
    3823     {
    3824         /* And what we can do now?! */
    3825         AssertMsgFailed(("Image lock has been lost for file=\"%s\"", pImage->szFilename));
    3826         Log(("vdiChangeImageMode: image lock has been lost for file=\"%s\", blocking on r/o lock wait",
    3827              pImage->szFilename));
    3828 
    3829         /* Try to obtain read lock in blocking mode. Maybe it's a very bad method. */
    3830         rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_WAIT, 0, cbLock);
    3831         AssertReleaseRC(rc);
    3832 
    3833         pImage->fReadOnly = false;
    3834         if (pImage->fReadOnly != fReadOnly)
    3835             rc = VERR_FILE_LOCK_VIOLATION;
    3836     }
    3837 
    3838     Log(("vdiChangeImageMode: Image \"%s\" mode change failed with rc=%Vrc, mode is %s\n",
    3839          pImage->szFilename, rc, (pImage->fReadOnly) ? "read-only" : "read/write"));
    3840 
    3841     return rc;
    3842 }
    3843 
    3844 /**
    3845  * internal: try to save header in image file even if image is in read-only mode.
    3846  */
    3847 static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage)
    3848 {
    3849     int rc = VINF_SUCCESS;
    3850 
    3851     if (pImage->fReadOnly)
    3852     {
    3853         rc = vdiChangeImageMode(pImage, false);
    3854         if (VBOX_SUCCESS(rc))
    3855         {
    3856             vdiFlushImage(pImage);
    3857             rc = vdiChangeImageMode(pImage, true);
    3858             AssertReleaseRC(rc);
    3859         }
    3860     }
    3861     else
    3862         vdiFlushImage(pImage);
    3863 
    3864     return rc;
    3865 }
    3866 
    3867 /**
    3868  * Opens an image file.
    3869  *
    3870  * The first opened image file in a HDD container must have a base image type,
    3871  * others (next opened images) must be a differencing or undo images.
    3872  * Linkage is checked for differencing image to be in consistence with the previously opened image.
    3873  * When a next differencing image is opened and the last image was opened in read/write access
    3874  * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
    3875  * other processes to use images in read-only mode too.
    3876  *
    3877  * Note that the image can be opened in read-only mode if a read/write open is not possible.
    3878  * Use VDIDiskIsReadOnly to check open mode.
    3879  *
    3880  * @returns VBox status code.
    3881  * @param   pDisk           Pointer to VDI HDD container.
    3882  * @param   pszFilename     Name of the image file to open.
    3883  * @param   fOpen           Image file open mode, see VDI_OPEN_FLAGS_* constants.
    3884  */
    3885 IDER3DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
    3886 {
    3887     /* sanity check */
    3888     Assert(pDisk);
    3889     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3890 
    3891     /* Check arguments. */
    3892     if (    !pszFilename
    3893         ||  *pszFilename == '\0'
    3894         ||  (fOpen & ~VDI_OPEN_FLAGS_MASK))
    3895     {
    3896         AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
    3897         return VERR_INVALID_PARAMETER;
    3898     }
    3899     LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
    3900 
    3901     PVDIIMAGEDESC pImage;
    3902     int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
    3903     if (VBOX_SUCCESS(rc))
    3904     {
    3905         if (pDisk->pLast)
    3906         {
    3907             /* Opening differencing image. */
    3908             if (!pDisk->pLast->fReadOnly)
    3909             {
    3910                 /*
    3911                  * Previous image is opened in read/write mode -> switch it into read-only.
    3912                  */
    3913                 rc = vdiChangeImageMode(pDisk->pLast, true);
    3914             }
    3915         }
    3916         else
    3917         {
    3918             /* Opening base image, check its type. */
    3919             if (    getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
    3920                 &&  getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
    3921             {
    3922                 rc = VERR_VDI_INVALID_TYPE;
    3923             }
    3924         }
    3925 
    3926         if (VBOX_SUCCESS(rc))
    3927             vdiAddImageToList(pDisk, pImage);
    3928         else
    3929             vdiCloseImage(pImage);
    3930     }
    3931 
    3932     LogFlow(("VDIDiskOpenImage: returns %Vrc\n", rc));
    3933     return rc;
    3934 }
    3935 
    3936 /**
    3937  * Closes the last opened image file in the HDD container. Leaves all changes inside it.
    3938  * If previous image file was opened in read-only mode (that is normal) and closing image
    3939  * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
    3940  * will be reopened in read/write mode.
    3941  *
    3942  * @param   pDisk           Pointer to VDI HDD container.
    3943  */
    3944 IDER3DECL(void) VDIDiskCloseImage(PVDIDISK pDisk)
    3945 {
    3946     /* sanity check */
    3947     Assert(pDisk);
    3948     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3949 
    3950     PVDIIMAGEDESC pImage = pDisk->pLast;
    3951     if (pImage)
    3952     {
    3953         LogFlow(("VDIDiskCloseImage: closing image \"%s\"\n", pImage->szFilename));
    3954 
    3955         bool fWasReadOnly = pImage->fReadOnly;
    3956         vdiRemoveImageFromList(pDisk, pImage);
    3957         vdiCloseImage(pImage);
    3958 
    3959         if (    !fWasReadOnly
    3960             &&  pDisk->pLast
    3961             &&  pDisk->pLast->fReadOnly
    3962             &&  !(pDisk->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    3963         {
    3964             /*
    3965              * Closed image was opened in read/write mode, previous image was opened
    3966              * in read-only mode, try to switch it into read/write.
    3967              */
    3968             int rc = vdiChangeImageMode(pDisk->pLast, false);
    3969             NOREF(rc); /* gcc still hates unused variables... */
    3970         }
    3971 
    3972         return;
    3973     }
    3974     AssertMsgFailed(("No images to close\n"));
    3975 }
    3976 
    3977 /**
    3978  * Closes all opened image files in HDD container.
    3979  *
    3980  * @param   pDisk           Pointer to VDI HDD container.
    3981  */
    3982 IDER3DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
    3983 {
    3984     LogFlow(("VDIDiskCloseAllImages:\n"));
    3985     /* sanity check */
    3986     Assert(pDisk);
    3987     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    3988 
    3989     PVDIIMAGEDESC pImage = pDisk->pLast;
    3990     while (pImage)
    3991     {
    3992         PVDIIMAGEDESC pPrev = pImage->pPrev;
    3993         vdiRemoveImageFromList(pDisk, pImage);
    3994         vdiCloseImage(pImage);
    3995         pImage = pPrev;
    3996     }
    3997     Assert(pDisk->pLast == NULL);
    3998 }
    3999 
    4000 /**
    4001  * Commits last opened differencing/undo image file of HDD container to previous one.
    4002  * If previous image file was opened in read-only mode (that must be always so) it is reopened
    4003  * as read/write to do commit operation.
    4004  * After successfull commit the previous image file again reopened in read-only mode, last opened
    4005  * image file is cleared of data and remains open and active in HDD container.
    4006  * If you want to delete image after commit you must do it manually by VDIDiskCloseImage and
    4007  * VDIDeleteImage calls.
    4008  *
    4009  * Note that in case of unrecoverable error all images of HDD container will be closed.
    4010  *
    4011  * @returns VBox status code.
    4012  * @param   pDisk           Pointer to VDI HDD container.
    4013  * @param   pfnProgress     Progress callback. Optional.
    4014  * @param   pvUser          User argument for the progress callback.
    4015  * @remark  Only used by tstVDI.
    4016  */
    4017 IDER3DECL(int) VDIDiskCommitLastDiff(PVDIDISK pDisk, PFNVMPROGRESS pfnProgress, void *pvUser)
    4018 {
    4019     LogFlow(("VDIDiskCommitLastDiff:\n"));
    4020     /* sanity check */
    4021     Assert(pDisk);
    4022     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4023 
    4024     int rc = VINF_SUCCESS;
    4025     PVDIIMAGEDESC pImage = pDisk->pLast;
    4026     if (!pImage)
    4027     {
    4028         AssertMsgFailed(("No one disk image is opened!\n"));
    4029         return VERR_VDI_NOT_OPENED;
    4030     }
    4031 
    4032     if (pImage->fReadOnly)
    4033     {
    4034         AssertMsgFailed(("Image \"%s\" is read-only!\n", pImage->szFilename));
    4035         return VERR_VDI_IMAGE_READ_ONLY;
    4036     }
    4037 
    4038     if (!pImage->pPrev)
    4039     {
    4040         AssertMsgFailed(("No images to commit to!\n"));
    4041         return VERR_VDI_NO_DIFF_IMAGES;
    4042     }
    4043 
    4044     bool fWasReadOnly = pImage->pPrev->fReadOnly;
    4045     if (fWasReadOnly)
    4046     {
    4047         /* Change previous image mode to r/w. */
    4048         rc = vdiChangeImageMode(pImage->pPrev, false);
    4049         if (VBOX_FAILURE(rc))
    4050         {
    4051             Log(("VDIDiskCommitLastDiff: can't switch previous image into r/w mode, rc=%Vrc\n", rc));
    4052             return rc;
    4053         }
    4054     }
    4055 
    4056     rc = vdiCommitToImage(pDisk, pImage->pPrev, pfnProgress, pvUser);
    4057     if (VBOX_SUCCESS(rc) && fWasReadOnly)
    4058     {
    4059         /* Change previous image mode back to r/o. */
    4060         rc = vdiChangeImageMode(pImage->pPrev, true);
    4061     }
    4062 
    4063     if (VBOX_FAILURE(rc))
    4064     {
    4065         /* Failed! Close all images, can't work with VHDD at all. */
    4066         VDIDiskCloseAllImages(pDisk);
    4067         AssertMsgFailed(("Fatal: commit failed, rc=%Vrc\n", rc));
    4068     }
    4069 
    4070     return rc;
    4071 }
    4072 
    4073 /**
    4074  * Creates and opens a new differencing image file in HDD container.
    4075  * See comments for VDIDiskOpenImage function about differencing images.
    4076  *
    4077  * @returns VBox status code.
    4078  * @param   pDisk           Pointer to VDI HDD container.
    4079  * @param   pszFilename     Name of the image file to create and open.
    4080  * @param   pszComment      Pointer to image comment. NULL is ok.
    4081  * @param   pfnProgress     Progress callback. Optional.
    4082  * @param   pvUser          User argument for the progress callback.
    4083  * @remark  Only used by tstVDI.
    4084  */
    4085 IDER3DECL(int) VDIDiskCreateOpenDifferenceImage(PVDIDISK pDisk, const char *pszFilename,
    4086                                                 const char *pszComment, PFNVMPROGRESS pfnProgress,
    4087                                                 void *pvUser)
    4088 {
    4089     LogFlow(("VDIDiskCreateOpenDifferenceImage:\n"));
    4090     /* sanity check */
    4091     Assert(pDisk);
    4092     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4093     Assert(pszFilename);
    4094 
    4095     if (!pDisk->pLast)
    4096     {
    4097         AssertMsgFailed(("No one disk image is opened!\n"));
    4098         return VERR_VDI_NOT_OPENED;
    4099     }
    4100 
    4101     /* Flush last parent image changes if possible. */
    4102     vdiFlushImage(pDisk->pLast);
    4103 
    4104     int rc = vdiCreateImage(pszFilename,
    4105                             VDI_IMAGE_TYPE_DIFF,
    4106                             VDI_IMAGE_FLAGS_DEFAULT,
    4107                             getImageDiskSize(&pDisk->pLast->Header),
    4108                             pszComment,
    4109                             pDisk->pLast,
    4110                             pfnProgress, pvUser);
    4111     if (VBOX_SUCCESS(rc))
    4112     {
    4113         rc = VDIDiskOpenImage(pDisk, pszFilename, VDI_OPEN_FLAGS_NORMAL);
    4114         if (VBOX_FAILURE(rc))
    4115             VDIDeleteImage(pszFilename);
    4116     }
    4117     LogFlow(("VDIDiskCreateOpenDifferenceImage: returns %Vrc, filename=\"%s\"\n", rc, pszFilename));
    4118     return rc;
    4119 }
    4120 
    4121 /**
    4122  * internal: debug image dump.
    4123  *
    4124  * @remark  Only used by tstVDI.
    4125  */
    4126 static void vdiDumpImage(PVDIIMAGEDESC pImage)
    4127 {
    4128     RTLogPrintf("Dumping VDI image \"%s\" mode=%s fOpen=%X File=%08X\n",
    4129                 pImage->szFilename,
    4130                 (pImage->fReadOnly) ? "r/o" : "r/w",
    4131                 pImage->fOpen,
    4132                 pImage->File);
    4133     RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
    4134                 pImage->PreHeader.u32Version,
    4135                 getImageType(&pImage->Header),
    4136                 getImageFlags(&pImage->Header),
    4137                 getImageDiskSize(&pImage->Header));
    4138     RTLogPrintf("Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
    4139                 getImageBlockSize(&pImage->Header),
    4140                 getImageExtraBlockSize(&pImage->Header),
    4141                 getImageBlocks(&pImage->Header),
    4142                 getImageBlocksAllocated(&pImage->Header));
    4143     RTLogPrintf("Header: offBlocks=%u offData=%u\n",
    4144                 getImageBlocksOffset(&pImage->Header),
    4145                 getImageDataOffset(&pImage->Header));
    4146     PVDIDISKGEOMETRY pg = getImageGeometry(&pImage->Header);
    4147     RTLogPrintf("Header: Geometry: C/H/S=%u/%u/%u cbSector=%u Mode=%u\n",
    4148                 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector,
    4149                 getImageTranslation(&pImage->Header));
    4150     RTLogPrintf("Header: uuidCreation={%Vuuid}\n", getImageCreationUUID(&pImage->Header));
    4151     RTLogPrintf("Header: uuidModification={%Vuuid}\n", getImageModificationUUID(&pImage->Header));
    4152     RTLogPrintf("Header: uuidParent={%Vuuid}\n", getImageParentUUID(&pImage->Header));
    4153     if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
    4154         RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
    4155     RTLogPrintf("Image:  fFlags=%08X offStartBlocks=%u offStartData=%u\n",
    4156                 pImage->fFlags, pImage->offStartBlocks, pImage->offStartData);
    4157     RTLogPrintf("Image:  uBlockMask=%08X uShiftIndex2Offset=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
    4158                 pImage->uBlockMask,
    4159                 pImage->uShiftIndex2Offset,
    4160                 pImage->uShiftOffset2Index,
    4161                 pImage->offStartBlockData);
    4162 
    4163     unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
    4164     for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
    4165     {
    4166         if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
    4167         {
    4168             cBlocksNotFree++;
    4169             if (pImage->paBlocks[uBlock] >= cBlocks)
    4170                 cBadBlocks++;
    4171         }
    4172     }
    4173     if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
    4174     {
    4175         RTLogPrintf("!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
    4176                 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
    4177     }
    4178     if (cBadBlocks)
    4179     {
    4180         RTLogPrintf("!! WARNING: %u bad blocks found !!\n",
    4181                 cBadBlocks);
    4182     }
    4183 }
    4184 
    4185 /**
    4186  * Debug helper - dumps all opened images of HDD container into the log file.
    4187  *
    4188  * @param   pDisk           Pointer to VDI HDD container.
    4189  * @remark  Only used by tstVDI and vditool
    4190  */
    4191 IDER3DECL(void) VDIDiskDumpImages(PVDIDISK pDisk)
    4192 {
    4193     /* sanity check */
    4194     Assert(pDisk);
    4195     AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
    4196 
    4197     RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
    4198     for (PVDIIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
    4199         vdiDumpImage(pImage);
    4200 }
    4201 
    4202 
    4203 /*******************************************************************************
    4204 *   PDM interface                                                              *
    4205 *******************************************************************************/
    4206 
    4207 /**
    4208  * Construct a VBox HDD media driver instance.
    4209  *
    4210  * @returns VBox status.
    4211  * @param   pDrvIns     The driver instance data.
    4212  *                      If the registration structure is needed, pDrvIns->pDrvReg points to it.
    4213  * @param   pCfgHandle  Configuration node handle for the driver. Use this to obtain the configuration
    4214  *                      of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
    4215  *                      iInstance it's expected to be used a bit in this function.
    4216  */
    4217 static DECLCALLBACK(int) vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
    4218 {
    4219     LogFlow(("vdiConstruct:\n"));
    4220     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4221     char *pszName;      /**< The path of the disk image file. */
    4222     bool fReadOnly;     /**< True if the media is readonly. */
    4223     bool fHonorZeroWrites = false;
    4224 
    4225     /*
    4226      * Init the static parts.
    4227      */
    4228     pDrvIns->IBase.pfnQueryInterface    = vdiQueryInterface;
    4229     pData->pDrvIns = pDrvIns;
    4230 
    4231     vdiInitVDIDisk(pData);
    4232 
    4233     /* IMedia */
    4234     pData->IMedia.pfnRead               = vdiRead;
    4235     pData->IMedia.pfnWrite              = vdiWrite;
    4236     pData->IMedia.pfnFlush              = vdiFlush;
    4237     pData->IMedia.pfnGetSize            = vdiGetSize;
    4238     pData->IMedia.pfnGetUuid            = vdiGetUuid;
    4239     pData->IMedia.pfnIsReadOnly         = vdiIsReadOnly;
    4240     pData->IMedia.pfnBiosGetGeometry    = vdiBiosGetGeometry;
    4241     pData->IMedia.pfnBiosSetGeometry    = vdiBiosSetGeometry;
    4242     pData->IMedia.pfnBiosGetTranslation = vdiBiosGetTranslation;
    4243     pData->IMedia.pfnBiosSetTranslation = vdiBiosSetTranslation;
    4244 
    4245     /*
    4246      * Validate configuration and find the great to the level of umpteen grandparent.
    4247      * The parents are found in the 'Parent' subtree, so it's sorta up side down
    4248      * from the image dependency tree.
    4249      */
    4250     unsigned    iLevel = 0;
    4251     PCFGMNODE   pCurNode = pCfgHandle;
    4252     for (;;)
    4253     {
    4254         if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0ReadOnly\0HonorZeroWrites\0"))
    4255             return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
    4256 
    4257         PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
    4258         if (!pParent)
    4259             break;
    4260         pCurNode = pParent;
    4261         iLevel++;
    4262     }
    4263 
    4264     /*
    4265      * Open the images.
    4266      */
    4267     int rc = VINF_SUCCESS;
    4268     while (pCurNode && VBOX_SUCCESS(rc))
    4269     {
    4270         /*
    4271          * Read the image configuration.
    4272          */
    4273         int rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
    4274         if (VBOX_FAILURE(rc))
    4275             return PDMDRV_SET_ERROR(pDrvIns, rc,
    4276                                     N_("VHDD: Configuration error: Querying \"Path\" as string failed"));
    4277 
    4278         rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
    4279         if (rc == VERR_CFGM_VALUE_NOT_FOUND)
    4280             fReadOnly = false;
    4281         else if (VBOX_FAILURE(rc))
    4282         {
    4283             MMR3HeapFree(pszName);
    4284             return PDMDRV_SET_ERROR(pDrvIns, rc,
    4285                                     N_("VHDD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
    4286         }
    4287 
    4288         if (!fHonorZeroWrites)
    4289         {
    4290             rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
    4291             if (rc == VERR_CFGM_VALUE_NOT_FOUND)
    4292                 fHonorZeroWrites = false;
    4293             else if (VBOX_FAILURE(rc))
    4294             {
    4295                 MMR3HeapFree(pszName);
    4296                 return PDMDRV_SET_ERROR(pDrvIns, rc,
    4297                                         N_("VHDD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
    4298             }
    4299         }
    4300 
    4301         /*
    4302          * Open the image.
    4303          */
    4304         rc = VDIDiskOpenImage(pData, pszName, fReadOnly ? VDI_OPEN_FLAGS_READONLY
    4305                                                         : VDI_OPEN_FLAGS_NORMAL);
    4306         if (VBOX_SUCCESS(rc))
    4307             Log(("vdiConstruct: %d - Opened '%s' in %s mode\n",
    4308                  iLevel, pszName, VDIDiskIsReadOnly(pData) ? "read-only" : "read-write"));
    4309         else
    4310             AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
    4311         MMR3HeapFree(pszName);
    4312 
    4313         /* next */
    4314         iLevel--;
    4315         pCurNode = CFGMR3GetParent(pCurNode);
    4316     }
    4317 
    4318     /* If any of the images has the flag set, handle zero writes like normal. */
    4319     if (VBOX_SUCCESS(rc))
    4320         pData->fHonorZeroWrites = fHonorZeroWrites;
    4321 
    4322     /* On failure, vdiDestruct will be called, so no need to clean up here. */
    4323 
    4324     if (rc == VERR_ACCESS_DENIED)
    4325         /* This should never happen here since this case is covered by Console::PowerUp */
    4326         return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
    4327                                    N_("Cannot open virtual disk image '%s' for %s access"),
    4328                                    pszName, fReadOnly ? "readonly" : "read/write");
    4329 
    4330     return rc;
    4331 }
    4332 
    4333 /**
    4334  * Destruct a driver instance.
    4335  *
    4336  * Most VM resources are freed by the VM. This callback is provided so that any non-VM
    4337  * resources can be freed correctly.
    4338  *
    4339  * @param   pDrvIns     The driver instance data.
    4340  */
    4341 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
    4342 {
    4343     LogFlow(("vdiDestruct:\n"));
    4344     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4345     VDIDiskCloseAllImages(pData);
    4346 }
    4347 
    4348 /**
    4349  * When the VM has been suspended we'll change the image mode to read-only
    4350  * so that main and others can read the VDIs. This is important when
    4351  * saving state and so forth.
    4352  *
    4353  * @param   pDrvIns     The driver instance data.
    4354  */
    4355 static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
    4356 {
    4357     LogFlow(("vdiSuspend:\n"));
    4358 #if 1 // #ifdef DEBUG_dmik
    4359     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4360     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4361     {
    4362         int rc = vdiChangeImageMode(pData->pLast, true);
    4363         AssertRC(rc);
    4364     }
    4365 #endif
    4366 }
    4367 
    4368 /**
    4369  * Before the VM resumes we'll have to undo the read-only mode change
    4370  * done in vdiSuspend.
    4371  *
    4372  * @param   pDrvIns     The driver instance data.
    4373  */
    4374 static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
    4375 {
    4376     LogFlow(("vdiSuspend:\n"));
    4377 #if 1 //#ifdef DEBUG_dmik
    4378     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4379     if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
    4380     {
    4381         int rc = vdiChangeImageMode(pData->pLast, false);
    4382         AssertRC(rc);
    4383     }
    4384 #endif
    4385 }
    4386 
    4387 
    4388 /** @copydoc PDMIMEDIA::pfnGetSize */
    4389 static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
    4390 {
    4391     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4392     uint64_t cb = VDIDiskGetSize(pData);
    4393     LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
    4394     return cb;
    4395 }
    4396 
    4397 /**
    4398  * Get stored media geometry - BIOS property.
    4399  *
    4400  * @see PDMIMEDIA::pfnBiosGetGeometry for details.
    4401  */
    4402 static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
    4403 {
    4404     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4405     int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
    4406     if (VBOX_SUCCESS(rc))
    4407     {
    4408         LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
    4409         return VINF_SUCCESS;
    4410     }
    4411     Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
    4412     return VERR_PDM_GEOMETRY_NOT_SET;
    4413 }
    4414 
    4415 /**
    4416  * Set stored media geometry - BIOS property.
    4417  *
    4418  * @see PDMIMEDIA::pfnBiosSetGeometry for details.
    4419  */
    4420 static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
    4421 {
    4422     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4423     int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
    4424     LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
    4425     return rc;
    4426 }
    4427 
    4428 /**
    4429  * Read bits.
    4430  *
    4431  * @see PDMIMEDIA::pfnRead for details.
    4432  */
    4433 static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
    4434 {
    4435     LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
    4436     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4437     int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
    4438     if (VBOX_SUCCESS(rc))
    4439         Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
    4440               "%.*Vhxd\n",
    4441               off, pvBuf, cbRead, cbRead, pvBuf));
    4442     LogFlow(("vdiRead: returns %Vrc\n", rc));
    4443     return rc;
    4444 }
    4445 
    4446 /**
    4447  * Write bits.
    4448  *
    4449  * @see PDMIMEDIA::pfnWrite for details.
    4450  */
    4451 static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
    4452 {
    4453     LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
    4454     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4455     Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
    4456           "%.*Vhxd\n",
    4457           off, pvBuf, cbWrite, cbWrite, pvBuf));
    4458     int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
    4459     LogFlow(("vdiWrite: returns %Vrc\n", rc));
    4460     return rc;
    4461 }
    4462 
    4463 /**
    4464  * Flush bits to media.
    4465  *
    4466  * @see PDMIMEDIA::pfnFlush for details.
    4467  */
    4468 static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
    4469 {
    4470     LogFlow(("vdiFlush:\n"));
    4471     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4472     vdiFlushImage(pData->pLast);
    4473     int rc = VINF_SUCCESS;
    4474     LogFlow(("vdiFlush: returns %Vrc\n", rc));
    4475     return rc;
    4476 }
    4477 
    4478 /** @copydoc PDMIMEDIA::pfnGetUuid */
    4479 static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
    4480 {
    4481     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4482     int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
    4483     LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
    4484     return rc;
    4485 }
    4486 
    4487 /** @copydoc PDMIMEDIA::pfnIsReadOnly */
    4488 static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
    4489 {
    4490     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4491     LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
    4492     return VDIDiskIsReadOnly(pData);
    4493 }
    4494 
    4495 /** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
    4496 static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface,
    4497                                                PPDMBIOSTRANSLATION penmTranslation)
    4498 {
    4499     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4500     int rc = VDIDiskGetTranslation(pData, penmTranslation);
    4501     LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
    4502     return rc;
    4503 }
    4504 
    4505 /** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
    4506 static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface,
    4507                                                PDMBIOSTRANSLATION enmTranslation)
    4508 {
    4509     PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
    4510     int rc = VDIDiskSetTranslation(pData, enmTranslation);
    4511     LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
    4512     return rc;
    4513 }
    4514 
    4515 
    4516 /**
    4517  * Queries an interface to the driver.
    4518  *
    4519  * @returns Pointer to interface.
    4520  * @returns NULL if the interface was not supported by the driver.
    4521  * @param   pInterface          Pointer to this interface structure.
    4522  * @param   enmInterface        The requested interface identification.
    4523  * @thread  Any thread.
    4524  */
    4525 static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
    4526 {
    4527     PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
    4528     PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
    4529     switch (enmInterface)
    4530     {
    4531         case PDMINTERFACE_BASE:
    4532             return &pDrvIns->IBase;
    4533         case PDMINTERFACE_MEDIA:
    4534             return &pData->IMedia;
    4535         default:
    4536             return NULL;
    4537     }
    4538 }
    4539 
    4540 
    4541 /**
    4542  * VBox HDD driver registration record.
    4543  */
    4544 const PDMDRVREG g_DrvVBoxHDD =
    4545 {
    4546     /* u32Version */
    4547     PDM_DRVREG_VERSION,
    4548     /* szDriverName */
    4549     "VBoxHDD",
    4550     /* pszDescription */
    4551     "VBoxHDD media driver.",
    4552     /* fFlags */
    4553     PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
    4554     /* fClass. */
    4555     PDM_DRVREG_CLASS_MEDIA,
    4556     /* cMaxInstances */
    4557     ~0,
    4558     /* cbInstance */
    4559     sizeof(VDIDISK),
    4560     /* pfnConstruct */
    4561     vdiConstruct,
    4562     /* pfnDestruct */
    4563     vdiDestruct,
    4564     /* pfnIOCtl */
    4565     NULL,
    4566     /* pfnPowerOn */
    4567     NULL,
    4568     /* pfnReset */
    4569     NULL,
    4570     /* pfnSuspend */
    4571     vdiSuspend,
    4572     /* pfnResume */
    4573     vdiResume,
    4574     /* pfnDetach */
    4575     NULL
    4576 };
     521__BEGIN_DECLS
     522
     523void vdiInitVDIDisk(PVDIDISK pDisk);
     524void vdiFlushImage(PVDIIMAGEDESC pImage);
     525int  vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
     526
     527__END_DECLS
     528
     529#endif
  • trunk/src/VBox/Devices/Storage/testcase/Makefile

    r960 r1565  
    2222include $(PATH_KBUILD)/header.kmk
    2323
    24 PROGRAMS = vditool
     24PROGRAMS        += vditool
     25vditool_TEMPLATE = VBOXR3EXE
     26vditool_SOURCES  = vditool.cpp
     27ifeq ($(BUILD_TARGET),win)
     28 vditool_LIBS    = $(PATH_LIB)/VBoxDDU$(VBOX_SUFF_LIB)
     29else
     30 vditool_LIBS    = $(PATH_BIN)/VBoxDDU$(VBOX_SUFF_DLL)
     31endif
     32vditool_LIBS    += $(LIB_RUNTIME)
     33
     34
    2535ifdef VBOX_WITH_TESTCASES
    26 PROGRAMS += tstVDI
     36 PROGRAMS       += tstVDI
     37 tstVDI_TEMPLATE = VBOXR3TSTEXE
     38 tstVDI_SOURCES  = tstVDI.cpp
     39 tstVDI_LIBS     = $(vditool_LIBS)
    2740endif
    2841
    29 TEMPLATE        = VBOXR3EXE
    30 ifeq ($(filter-out win,$(BUILD_TARGET)),)
    31  LIBS           = $(PATH_LIB)/VBoxDD.lib
    32 else
    33  LIBS           = \
    34         $(PATH_BIN)/VBoxDD$(VBOX_SUFF_DLL) \
    35         $(PATH_BIN)/VBoxDD2$(VBOX_SUFF_DLL) \
    36         $(LIB_VMM) \
    37         $(LIB_REM)
    38 endif
    39 LIBS           += $(LIB_RUNTIME)
    40 
    41 vditool_SOURCES = vditool.cpp
    42 
    43 tstVDI_SOURCES  = tstVDI.cpp
    44 tstVDI_INST = $(INST_TESTCASE)
    4542
    4643include $(PATH_KBUILD)/footer.kmk
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