Changeset 1565 in vbox for trunk/src/VBox/Devices/Storage
- Timestamp:
- Mar 20, 2007 3:06:26 AM (18 years ago)
- Location:
- trunk/src/VBox/Devices/Storage
- Files:
-
- 2 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/VBoxHDD.cpp
r860 r1565 38 38 #include <iprt/asm.h> 39 39 40 #include "VDICore.h" 40 41 #include "Builtins.h" 41 42 43 42 44 /******************************************************************************* 43 * Constants And Macros, Structures and Typedefs*45 * Defined Constants And Macros * 44 46 *******************************************************************************/ 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 VDIDISKGEOMETRY57 {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 VDIPREHEADER77 {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 25691 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 VDIHEADER098 {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 VDIHEADER1130 {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 VDIHEADER176 {177 unsigned uVersion;178 union179 {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 this192 * 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 this198 * block returns zeroes.199 */200 #define VDI_IMAGE_BLOCK_ZERO ((VDIIMAGEBLOCKPOINTER)~1)201 202 /**203 * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not204 * 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 _1M417 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 structure427 */428 typedef struct VDIIMAGEDESC429 {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 VDIDISK481 {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 previously500 * zero block. Buffer deallocated after operation complete.501 * @remark For best performance buffer size must be equal to image's502 * 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 optimized507 * 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 47 /** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */ 518 48 #define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) ) … … 525 55 526 56 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 */ 60 static 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 */ 74 static 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 */ 93 static 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 */ 107 static 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 */ 126 static 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 */ 144 static 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 */ 156 static 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 */ 166 static 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 */ 175 static 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 */ 186 static 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 */ 205 static 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 */ 227 static 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 } 552 237 #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 */ 248 static 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 } 652 258 #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 */ 270 static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns) 271 { 272 LogFlow(("vdiDestruct:\n")); 273 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK); 274 VDIDiskCloseAllImages(pData); 275 } 276 4206 277 4207 278 /** … … 4329 400 4330 401 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-VM4337 * 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-only4350 * so that main and others can read the VDIs. This is important when4351 * 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_dmik4359 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 #endif4366 }4367 4368 /**4369 * Before the VM resumes we'll have to undo the read-only mode change4370 * 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_dmik4378 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 #endif4385 }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 402 } 4539 403 … … 4575 439 NULL 4576 440 }; 441 -
trunk/src/VBox/Devices/Storage/VDICore.cpp
r1562 r1565 1 1 /** @file 2 * 3 * VBox storage devices: 4 * VBox HDD container implementation 2 * Virtual Disk Image (VDI), Core Code. 5 3 */ 6 4 … … 26 24 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD 27 25 #include <VBox/VBoxHDD.h> 28 #include <VBox/pdm.h> 29 #include <VBox/mm.h> 26 #include "VDICore.h" 30 27 #include <VBox/err.h> 31 28 … … 37 34 #include <iprt/string.h> 38 35 #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 VDIDISKGEOMETRY57 {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 VDIPREHEADER77 {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 25691 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 VDIHEADER098 {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 VDIHEADER1130 {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 VDIHEADER176 {177 unsigned uVersion;178 union179 {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 this192 * 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 this198 * block returns zeroes.199 */200 #define VDI_IMAGE_BLOCK_ZERO ((VDIIMAGEBLOCKPOINTER)~1)201 202 /**203 * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not204 * 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 _1M417 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 structure427 */428 typedef struct VDIIMAGEDESC429 {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 VDIDISK481 {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 previously500 * zero block. Buffer deallocated after operation complete.501 * @remark For best performance buffer size must be equal to image's502 * 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 optimized507 * 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) )525 36 526 37 … … 551 62 static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage); 552 63 #endif 553 static void vdiFlushImage(PVDIIMAGEDESC pImage);554 64 static void vdiCloseImage(PVDIIMAGEDESC pImage); 555 65 static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead, … … 561 71 static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild, 562 72 PFNVMPROGRESS pfnProgress, void *pvUser); 563 static void vdiInitVDIDisk(PVDIDISK pDisk);564 73 static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage); 565 74 static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage); 566 75 static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage); 567 static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);568 76 static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage); 569 77 … … 571 79 PFNVMPROGRESS pfnProgress, void *pvUser); 572 80 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 81 594 82 … … 1384 872 1385 873 /** 1386 * internal: flushimage file to disk.1387 */ 1388 staticvoid vdiFlushImage(PVDIIMAGEDESC pImage)874 * Flush the image file to disk. 875 */ 876 void vdiFlushImage(PVDIIMAGEDESC pImage) 1389 877 { 1390 878 if (!pImage->fReadOnly) … … 3170 2658 3171 2659 /** 3172 * internal: initialize VDIDISK structure.3173 */ 3174 staticvoid vdiInitVDIDisk(PVDIDISK pDisk)2660 * Initialize the VDIDISK structure. 2661 */ 2662 void vdiInitVDIDisk(PVDIDISK pDisk) 3175 2663 { 3176 2664 Assert(pDisk); … … 3784 3272 3785 3273 /** 3786 * internal: Relockimage as read/write or read-only.3787 */ 3788 staticint vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)3274 * Relock the image as read/write or read-only. 3275 */ 3276 int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly) 3789 3277 { 3790 3278 Assert(pImage); … … 4199 3687 vdiDumpImage(pImage); 4200 3688 } 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 configuration4214 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like4215 * 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 down4248 * 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_READONLY4305 : 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 else4310 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-VM4337 * 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-only4350 * so that main and others can read the VDIs. This is important when4351 * 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_dmik4359 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 #endif4366 }4367 4368 /**4369 * Before the VM resumes we'll have to undo the read-only mode change4370 * 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_dmik4378 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 #endif4385 }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 NULL4576 }; -
trunk/src/VBox/Devices/Storage/VDICore.h
r1562 r1565 1 /** $Id$ */ 1 2 /** @file 2 * 3 * VBox storage devices: 4 * VBox HDD container implementation 3 * Virtual Disk Image (VDI), Core Code Header (internal). 5 4 */ 6 5 … … 21 20 */ 22 21 22 #ifndef __VDICore_h__ 23 24 23 25 /******************************************************************************* 24 26 * Header Files * 25 27 *******************************************************************************/ 26 #define LOG_GROUP LOG_GROUP_DRV_VBOXHDD27 28 #include <VBox/VBoxHDD.h> 28 29 #include <VBox/pdm.h> … … 38 39 #include <iprt/asm.h> 39 40 40 #include "Builtins.h"41 41 42 42 /******************************************************************************* … … 210 210 #define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion)) 211 211 212 212 213 /******************************************************************************* 213 214 * Internal Functions for header access * 214 215 *******************************************************************************/ 215 static inline VDIIMAGETYPEgetImageType(PVDIHEADER ph)216 DECLINLINE(VDIIMAGETYPE) getImageType(PVDIHEADER ph) 216 217 { 217 218 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 224 225 } 225 226 226 static inline unsignedgetImageFlags(PVDIHEADER ph)227 DECLINLINE(unsigned) getImageFlags(PVDIHEADER ph) 227 228 { 228 229 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 235 236 } 236 237 237 static inline char *getImageComment(PVDIHEADER ph)238 DECLINLINE(char *) getImageComment(PVDIHEADER ph) 238 239 { 239 240 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 246 247 } 247 248 248 static inline unsignedgetImageBlocksOffset(PVDIHEADER ph)249 DECLINLINE(unsigned) getImageBlocksOffset(PVDIHEADER ph) 249 250 { 250 251 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 257 258 } 258 259 259 static inline unsignedgetImageDataOffset(PVDIHEADER ph)260 DECLINLINE(unsigned) getImageDataOffset(PVDIHEADER ph) 260 261 { 261 262 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 269 270 } 270 271 271 static inline PVDIDISKGEOMETRYgetImageGeometry(PVDIHEADER ph)272 DECLINLINE(PVDIDISKGEOMETRY) getImageGeometry(PVDIHEADER ph) 272 273 { 273 274 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 280 281 } 281 282 282 static inline PDMBIOSTRANSLATIONgetImageTranslation(PVDIHEADER ph)283 DECLINLINE(PDMBIOSTRANSLATION) getImageTranslation(PVDIHEADER ph) 283 284 { 284 285 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 291 292 } 292 293 293 static inline voidsetImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)294 DECLINLINE(void) setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation) 294 295 { 295 296 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 301 302 } 302 303 303 static inline uint64_tgetImageDiskSize(PVDIHEADER ph)304 DECLINLINE(uint64_t) getImageDiskSize(PVDIHEADER ph) 304 305 { 305 306 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 312 313 } 313 314 314 static inline unsignedgetImageBlockSize(PVDIHEADER ph)315 DECLINLINE(unsigned) getImageBlockSize(PVDIHEADER ph) 315 316 { 316 317 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 323 324 } 324 325 325 static inline unsignedgetImageExtraBlockSize(PVDIHEADER ph)326 DECLINLINE(unsigned) getImageExtraBlockSize(PVDIHEADER ph) 326 327 { 327 328 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 334 335 } 335 336 336 static inline unsignedgetImageBlocks(PVDIHEADER ph)337 DECLINLINE(unsigned) getImageBlocks(PVDIHEADER ph) 337 338 { 338 339 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 345 346 } 346 347 347 static inline unsignedgetImageBlocksAllocated(PVDIHEADER ph)348 DECLINLINE(unsigned) getImageBlocksAllocated(PVDIHEADER ph) 348 349 { 349 350 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 356 357 } 357 358 358 static inline voidsetImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)359 DECLINLINE(void) setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks) 359 360 { 360 361 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 366 367 } 367 368 368 static inline PRTUUIDgetImageCreationUUID(PVDIHEADER ph)369 DECLINLINE(PRTUUID) getImageCreationUUID(PVDIHEADER ph) 369 370 { 370 371 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 377 378 } 378 379 379 static inline PRTUUIDgetImageModificationUUID(PVDIHEADER ph)380 DECLINLINE(PRTUUID) getImageModificationUUID(PVDIHEADER ph) 380 381 { 381 382 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 388 389 } 389 390 390 static inline PRTUUIDgetImageParentUUID(PVDIHEADER ph)391 DECLINLINE(PRTUUID) getImageParentUUID(PVDIHEADER ph) 391 392 { 392 393 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 399 400 } 400 401 401 static inline PRTUUIDgetImageParentModificationUUID(PVDIHEADER ph)402 DECLINLINE(PRTUUID) getImageParentModificationUUID(PVDIHEADER ph) 402 403 { 403 404 switch (GET_MAJOR_HEADER_VERSION(ph)) … … 515 516 516 517 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 527 518 /******************************************************************************* 528 519 * Internal Functions * 529 520 *******************************************************************************/ 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 523 void vdiInitVDIDisk(PVDIDISK pDisk); 524 void vdiFlushImage(PVDIIMAGEDESC pImage); 525 int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly); 526 527 __END_DECLS 528 529 #endif -
trunk/src/VBox/Devices/Storage/testcase/Makefile
r960 r1565 22 22 include $(PATH_KBUILD)/header.kmk 23 23 24 PROGRAMS = vditool 24 PROGRAMS += vditool 25 vditool_TEMPLATE = VBOXR3EXE 26 vditool_SOURCES = vditool.cpp 27 ifeq ($(BUILD_TARGET),win) 28 vditool_LIBS = $(PATH_LIB)/VBoxDDU$(VBOX_SUFF_LIB) 29 else 30 vditool_LIBS = $(PATH_BIN)/VBoxDDU$(VBOX_SUFF_DLL) 31 endif 32 vditool_LIBS += $(LIB_RUNTIME) 33 34 25 35 ifdef VBOX_WITH_TESTCASES 26 PROGRAMS += tstVDI 36 PROGRAMS += tstVDI 37 tstVDI_TEMPLATE = VBOXR3TSTEXE 38 tstVDI_SOURCES = tstVDI.cpp 39 tstVDI_LIBS = $(vditool_LIBS) 27 40 endif 28 41 29 TEMPLATE = VBOXR3EXE30 ifeq ($(filter-out win,$(BUILD_TARGET)),)31 LIBS = $(PATH_LIB)/VBoxDD.lib32 else33 LIBS = \34 $(PATH_BIN)/VBoxDD$(VBOX_SUFF_DLL) \35 $(PATH_BIN)/VBoxDD2$(VBOX_SUFF_DLL) \36 $(LIB_VMM) \37 $(LIB_REM)38 endif39 LIBS += $(LIB_RUNTIME)40 41 vditool_SOURCES = vditool.cpp42 43 tstVDI_SOURCES = tstVDI.cpp44 tstVDI_INST = $(INST_TESTCASE)45 42 46 43 include $(PATH_KBUILD)/footer.kmk
Note:
See TracChangeset
for help on using the changeset viewer.