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