Changeset 33088 in vbox for trunk/src/VBox/Devices/Storage
- Timestamp:
- Oct 13, 2010 7:49:04 AM (14 years ago)
- svn:sync-xref-src-repo-rev:
- 66617
- Location:
- trunk/src/VBox/Devices/Storage
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/VBoxHDD.cpp
r33082 r33088 433 433 extern VBOXHDDBACKEND g_RawBackend; 434 434 extern VBOXHDDBACKEND g_VmdkBackend; 435 extern VBOXHDDBACKEND g_VmdkStreamBackend;436 435 extern VBOXHDDBACKEND g_VDIBackend; 437 436 extern VBOXHDDBACKEND g_VhdBackend; … … 450 449 &g_RawBackend, 451 450 &g_VmdkBackend, 452 &g_VmdkStreamBackend,453 451 &g_VDIBackend, 454 452 &g_VhdBackend, -
trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
r32892 r33088 304 304 /** Compression type for this extent. */ 305 305 uint16_t uCompression; 306 /** Append position for writing new grain. Only for sparse extents. */ 307 uint64_t uAppendPosition; 306 308 /** Last grain which has been written to. Only for streamOptimized extents. */ 307 309 uint32_t uLastGrainWritten; … … 309 311 * streamOptimized extents. */ 310 312 uint32_t uLastGrainSector; 311 /** Data size of last grain which has been written to. Only for312 * streamOptimized extents. */313 uint32_t cbLastGrainWritten;314 313 /** Starting sector of the decompressed grain buffer. */ 315 314 uint32_t uGrainSector; … … 473 472 474 473 475 /** State for the input callout of the inflate reader. */476 typedef struct VMDK INFLATESTATE474 /** State for the input/output callout of the inflate reader/deflate writer. */ 475 typedef struct VMDKCOMPRESSIO 477 476 { 478 477 /* Image this operation relates to. */ … … 484 483 /* Pointer to the compressed grain buffer. */ 485 484 void *pvCompGrain; 486 } VMDKINFLATESTATE; 487 488 /** State for the output callout of the deflate writer. */ 489 typedef struct VMDKDEFLATESTATE 490 { 491 /* Image this operation relates to. */ 492 PVMDKIMAGE pImage; 493 /* Current write position. */ 494 ssize_t iOffset; 495 /* Size of the compressed grain buffer. */ 496 size_t cbCompGrain; 497 /* Pointer to the compressed grain buffer. */ 498 void *pvCompGrain; 499 } VMDKDEFLATESTATE; 485 } VMDKCOMPRESSIO; 486 500 487 501 488 /** Tracks async grain allocation. */ 502 489 typedef struct VMDKGRAINALLOCASYNC 503 490 { 504 /** Old size of the extent. Used for rollback after an error. */505 uint64_t cbExtentOld;506 491 /** Flag whether the allocation failed. */ 507 492 bool fIoErr; … … 515 500 /** Extent the allocation happens. */ 516 501 PVMDKEXTENT pExtent; 517 /** New size of the extent, required for the grain table update. */518 uint64_t cbExtentSize;502 /** Position of the new grain, required for the grain table update. */ 503 uint64_t uGrainOffset; 519 504 /** Grain table sector. */ 520 505 uint64_t uGTSector; … … 850 835 static DECLCALLBACK(int) vmdkFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf) 851 836 { 852 VMDK INFLATESTATE *pInflateState = (VMDKINFLATESTATE*)pvUser;837 VMDKCOMPRESSIO *pInflateState = (VMDKCOMPRESSIO *)pvUser; 853 838 size_t cbInjected = 0; 854 839 … … 932 917 512); 933 918 934 VMDK INFLATESTATEInflateState;919 VMDKCOMPRESSIO InflateState; 935 920 InflateState.pImage = pImage; 936 921 InflateState.iOffset = -1; … … 953 938 static DECLCALLBACK(int) vmdkFileDeflateHelper(void *pvUser, const void *pvBuf, size_t cbBuf) 954 939 { 955 VMDK DEFLATESTATE *pDeflateState = (VMDKDEFLATESTATE*)pvUser;940 VMDKCOMPRESSIO *pDeflateState = (VMDKCOMPRESSIO *)pvUser; 956 941 957 942 Assert(cbBuf); … … 990 975 int rc; 991 976 PRTZIPCOMP pZip = NULL; 992 VMDK DEFLATESTATEDeflateState;977 VMDKCOMPRESSIO DeflateState; 993 978 994 979 DeflateState.pImage = pImage; … … 1032 1017 if (RT_FAILURE(rc)) 1033 1018 return rc; 1034 1035 /** @todo remove this code */1036 /* Set the file size to remove old garbage in case the block is1037 * rewritten. Cannot cause data loss as the code calling this1038 * guarantees that data gets only appended. Change the file size1039 * only if the size really changed, because this is very expensive1040 * on some filesystems such as XFS. */1041 uint64_t cbOld;1042 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbOld);1043 if (RT_FAILURE(rc))1044 return rc;1045 1046 if (cbOld != uOffset + uSize)1047 rc = vmdkFileSetSize(pImage, pExtent->pFile, uOffset + uSize);1048 1019 } 1049 1020 return rc; 1050 1021 } 1051 1022 } 1023 1052 1024 1053 1025 /** … … 1181 1153 RTMemFree(pExtent->pRGD); 1182 1154 pExtent->pRGD = NULL; 1183 }1184 if (pExtent->pvCompGrain)1185 {1186 RTMemFree(pExtent->pvCompGrain);1187 pExtent->pvCompGrain = NULL;1188 }1189 if (pExtent->pvGrain)1190 {1191 RTMemFree(pExtent->pvGrain);1192 pExtent->pvGrain = NULL;1193 1155 } 1194 1156 } … … 1368 1330 } 1369 1331 1370 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)1371 {1372 uint32_t uLastGrainWritten = 0;1373 uint32_t uLastGrainSector = 0;1374 size_t cbGT = pExtent->cGTEntries * sizeof(uint32_t);1375 uint32_t *pTmpGT = (uint32_t *)RTMemTmpAlloc(cbGT);1376 if (!pTmpGT)1377 {1378 rc = VERR_NO_MEMORY;1379 goto out;1380 }1381 for (i = 0, pGDTmp = pExtent->pGD; i < pExtent->cGDEntries; i++, pGDTmp++)1382 {1383 /* If no grain table is allocated skip the entry. */1384 if (*pGDTmp == 0)1385 continue;1386 1387 /* The VMDK 1.1 spec seems to talk about compressed grain tables,1388 * but in reality they are not compressed. */1389 rc = vmdkFileReadSync(pImage, pExtent->pFile,1390 VMDK_SECTOR2BYTE(*pGDTmp),1391 pTmpGT, cbGT, NULL);1392 if (RT_FAILURE(rc))1393 {1394 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);1395 RTMemTmpFree(pTmpGT);1396 goto out;1397 }1398 uint32_t j;1399 uint32_t *pGTTmp;1400 for (j = 0, pGTTmp = pTmpGT; j < pExtent->cGTEntries; j++, pGTTmp++)1401 {1402 uint32_t uGTTmp = RT_LE2H_U32(*pGTTmp);1403 1404 /* If no grain is allocated skip the entry. */1405 if (uGTTmp == 0)1406 continue;1407 1408 if (uLastGrainSector && uLastGrainSector >= uGTTmp)1409 {1410 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: grain table in '%s' contains a violation of the ordering assumptions"), pExtent->pszFullname);1411 RTMemTmpFree(pTmpGT);1412 goto out;1413 }1414 uLastGrainSector = uGTTmp;1415 uLastGrainWritten = i * pExtent->cGTEntries + j;1416 }1417 }1418 RTMemTmpFree(pTmpGT);1419 1420 if (uLastGrainSector)1421 {1422 uint64_t uLBA = 0;1423 uint32_t cbMarker = 0;1424 rc = vmdkFileInflateSync(pImage, pExtent,1425 VMDK_SECTOR2BYTE(uLastGrainSector),1426 pExtent->pvGrain,1427 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),1428 &uLBA, &cbMarker);1429 if (RT_FAILURE(rc))1430 goto out;1431 1432 Assert(uLBA == uLastGrainWritten * pExtent->cSectorsPerGrain);1433 pExtent->uGrainSector = uLastGrainSector;1434 pExtent->cbLastGrainWritten = cbMarker;1435 }1436 pExtent->uLastGrainWritten = uLastGrainWritten;1437 pExtent->uLastGrainSector = uLastGrainSector;1438 }1439 1440 1332 out: 1441 1333 if (RT_FAILURE(rc)) … … 1455 1347 1456 1348 if (fPreAlloc) 1349 { 1457 1350 cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512); 1351 cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded 1352 + cbGTRounded; 1353 } 1458 1354 else 1355 { 1356 /* Use a dummy start sector for layout computation. */ 1357 if (uStartSector == VMDK_GD_AT_END) 1358 uStartSector = 1; 1459 1359 cbGTRounded = 0; 1460 1461 if (uStartSector != VMDK_GD_AT_END) 1462 { 1463 cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded + cbGTRounded; 1464 /* For streamOptimized extents there is only one grain directory, 1465 * and for all others take redundant grain directory into account. */ 1466 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 1467 { 1468 cbOverhead = RT_ALIGN_64(cbOverhead, 1469 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)); 1470 if (!pExtent->fFooter) 1471 rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead + 512); 1472 } 1473 else 1474 { 1475 cbOverhead += cbGDRounded + cbGTRounded; 1476 cbOverhead = RT_ALIGN_64(cbOverhead, 1477 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)); 1478 rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead); 1479 } 1480 if (RT_FAILURE(rc)) 1481 goto out; 1482 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 1483 { 1484 pExtent->uSectorRGD = 0; 1485 pExtent->uSectorGD = uStartSector; 1486 } 1487 else 1488 { 1489 pExtent->uSectorRGD = uStartSector; 1490 pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded); 1491 } 1360 cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded; 1361 } 1362 1363 /* For streamOptimized extents there is only one grain directory, 1364 * and for all others take redundant grain directory into account. */ 1365 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 1366 { 1367 cbOverhead = RT_ALIGN_64(cbOverhead, 1368 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)); 1492 1369 } 1493 1370 else 1494 1371 { 1495 cbOverhead = 512 + pImage->cbDescAlloc; 1372 cbOverhead += cbGDRounded + cbGTRounded; 1373 cbOverhead = RT_ALIGN_64(cbOverhead, 1374 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)); 1375 rc = vmdkFileSetSize(pImage, pExtent->pFile, cbOverhead); 1376 } 1377 if (RT_FAILURE(rc)) 1378 goto out; 1379 pExtent->uAppendPosition = cbOverhead; 1380 pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead); 1381 1382 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 1383 { 1384 pExtent->uSectorRGD = 0; 1496 1385 pExtent->uSectorGD = uStartSector; 1386 } 1387 else 1388 { 1389 pExtent->uSectorRGD = uStartSector; 1390 pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded); 1497 1391 } 1498 1392 … … 1543 1437 } 1544 1438 } 1545 pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);1546 1439 1547 1440 out: … … 2798 2691 if (RT_FAILURE(rc)) 2799 2692 goto out; 2693 2694 rc = vmdkFileGetSize(pImage, pExtent->pFile, &pExtent->uAppendPosition); 2695 AssertRC(rc); 2696 if (RT_FAILURE(rc)) 2697 { 2698 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname); 2699 goto out; 2700 } 2701 pExtent->uAppendPosition = RT_ALIGN_64(pExtent->uAppendPosition, 512); 2702 2800 2703 if ( RT_LE2H_U32(Header.flags & RT_BIT(17)) 2801 2704 && RT_LE2H_U64(Header.gdOffset) == VMDK_GD_AT_END) 2802 2705 { 2803 2706 /* Read the footer, which comes before the end-of-stream marker. */ 2804 uint64_t cbSize; 2805 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbSize); 2806 AssertRC(rc); 2807 if (RT_FAILURE(rc)) 2808 { 2809 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname); 2810 goto out; 2811 } 2812 cbSize = RT_ALIGN_64(cbSize, 512); 2813 rc = vmdkFileReadSync(pImage, pExtent->pFile, cbSize - 2*512, &Header, sizeof(Header), NULL); 2707 rc = vmdkFileReadSync(pImage, pExtent->pFile, 2708 pExtent->uAppendPosition - 2*512, &Header, 2709 sizeof(Header), NULL); 2814 2710 AssertRC(rc); 2815 2711 if (RT_FAILURE(rc)) … … 2822 2718 goto out; 2823 2719 pExtent->fFooter = true; 2720 /* Prohibit any writes to this extent. */ 2721 pExtent->uAppendPosition = 0; 2824 2722 } 2825 2723 pExtent->uVersion = RT_LE2H_U32(Header.version); … … 2928 2826 goto out; 2929 2827 } 2828 2829 /* Prohibit any writes to this streamOptimized extent. */ 2830 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 2831 pExtent->uAppendPosition = 0; 2930 2832 2931 2833 rc = vmdkReadGrainDirectory(pImage, pExtent); … … 3162 3064 pExtent->pszFullname = NULL; 3163 3065 } 3066 if (pExtent->pvCompGrain) 3067 { 3068 RTMemFree(pExtent->pvCompGrain); 3069 pExtent->pvCompGrain = NULL; 3070 } 3164 3071 if (pExtent->pvGrain) 3165 3072 { … … 3351 3258 3352 3259 /* Mark the extent as unclean if opened in read-write mode. */ 3353 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)) 3260 if ( !(uOpenFlags & VD_OPEN_FLAGS_READONLY) 3261 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 3354 3262 { 3355 3263 pExtent->fUncleanShutdown = true; … … 3361 3269 /* Allocate at least 10K, and make sure that there is 5K free space 3362 3270 * in case new entries need to be added to the descriptor. Never 3363 * al ocate more than 128K, because that's no valid descriptor file3271 * allocate more than 128K, because that's no valid descriptor file 3364 3272 * and will result in the correct "truncated read" error handling. */ 3365 3273 uint64_t cbFileSize; … … 3538 3446 / pImage->PCHSGeometry.cSectors; 3539 3447 pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383); 3540 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 3448 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 3449 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 3541 3450 { 3542 3451 rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry); … … 4081 3990 4082 3991 /** 3992 * Internal: Create a real stream optimized VMDK using only linear writes. 3993 */ 3994 static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize, 3995 unsigned uImageFlags, 3996 PFNVDPROGRESS pfnProgress, void *pvUser, 3997 unsigned uPercentStart, unsigned uPercentSpan) 3998 { 3999 int rc; 4000 4001 rc = vmdkCreateExtents(pImage, 1); 4002 if (RT_FAILURE(rc)) 4003 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename); 4004 4005 /* Basename strings needed for constructing the extent names. */ 4006 const char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename); 4007 AssertPtr(pszBasenameSubstr); 4008 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1; 4009 4010 /* No separate descriptor file. */ 4011 pImage->pFile = NULL; 4012 4013 /* Set up all extents. */ 4014 PVMDKEXTENT pExtent = &pImage->pExtents[0]; 4015 4016 /* Set up fullname/basename for extent description. Cannot use StrDup 4017 * for basename, as it is not guaranteed that the memory can be freed 4018 * with RTMemTmpFree, which must be used as in other code paths 4019 * StrDup is not usable. */ 4020 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr); 4021 if (!pszBasename) 4022 return VERR_NO_MEMORY; 4023 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr); 4024 pExtent->pszBasename = pszBasename; 4025 4026 char *pszBasedirectory = RTStrDup(pImage->pszFilename); 4027 RTPathStripFilename(pszBasedirectory); 4028 char *pszFullname; 4029 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszBasedirectory, 4030 RTPATH_SLASH, pExtent->pszBasename); 4031 RTStrFree(pszBasedirectory); 4032 if (RT_FAILURE(rc)) 4033 return rc; 4034 pExtent->pszFullname = pszFullname; 4035 4036 /* Create file for extent. */ 4037 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname, 4038 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, 4039 true /* fCreate */), 4040 false /* fAsyncIO */); 4041 if (RT_FAILURE(rc)) 4042 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname); 4043 4044 /* Place descriptor file information. */ 4045 pExtent->uDescriptorSector = 1; 4046 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc); 4047 /* The descriptor is part of the (only) extent. */ 4048 pExtent->pDescData = pImage->pDescData; 4049 pImage->pDescData = NULL; 4050 4051 uint64_t cSectorsPerGDE, cSectorsPerGD; 4052 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; 4053 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K)); 4054 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K); 4055 pExtent->cGTEntries = 512; 4056 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain; 4057 pExtent->cSectorsPerGDE = cSectorsPerGDE; 4058 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE; 4059 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t)); 4060 4061 /* The spec says version is 1 for all VMDKs, but the vast 4062 * majority of streamOptimized VMDKs actually contain 4063 * version 3 - so go with the majority. Both are acepted. */ 4064 pExtent->uVersion = 3; 4065 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE; 4066 pExtent->fFooter = true; 4067 4068 pExtent->enmAccess = VMDKACCESS_READONLY; 4069 pExtent->fUncleanShutdown = false; 4070 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize); 4071 pExtent->uSectorOffset = 0; 4072 pExtent->fMetaDirty = true; 4073 4074 /* Create grain directory, without preallocating it straight away. It will 4075 * be constructed on the fly when writing out the data and written when 4076 * closing the image. The end effect is that the full grain directory is 4077 * allocated, which is a requirement of the VMDK specs. */ 4078 rc = vmdkCreateGrainDirectory(pImage, pExtent, VMDK_GD_AT_END, 4079 false /* fPreAlloc */); 4080 if (RT_FAILURE(rc)) 4081 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname); 4082 4083 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", 4084 "streamOptimized"); 4085 if (RT_FAILURE(rc)) 4086 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename); 4087 4088 return rc; 4089 } 4090 4091 /** 4092 * Internal: The actual code for creating any VMDK variant currently in 4093 * existence on hosted environments. 4094 */ 4095 static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize, 4096 unsigned uImageFlags, const char *pszComment, 4097 PCVDGEOMETRY pPCHSGeometry, 4098 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid, 4099 PFNVDPROGRESS pfnProgress, void *pvUser, 4100 unsigned uPercentStart, unsigned uPercentSpan) 4101 { 4102 int rc; 4103 4104 pImage->uImageFlags = uImageFlags; 4105 4106 /* Try to get error interface. */ 4107 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR); 4108 if (pImage->pInterfaceError) 4109 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError); 4110 4111 /* Get I/O interface. */ 4112 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT); 4113 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER); 4114 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO); 4115 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER); 4116 4117 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc, 4118 &pImage->Descriptor); 4119 if (RT_FAILURE(rc)) 4120 { 4121 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename); 4122 goto out; 4123 } 4124 4125 if ( (uImageFlags & VD_IMAGE_FLAGS_FIXED) 4126 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)) 4127 { 4128 /* Raw disk image (includes raw partition). */ 4129 const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment; 4130 /* As the comment is misused, zap it so that no garbage comment 4131 * is set below. */ 4132 pszComment = NULL; 4133 rc = vmdkCreateRawImage(pImage, pRaw, cbSize); 4134 } 4135 else 4136 { 4137 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 4138 { 4139 /* Stream optimized sparse image (monolithic). */ 4140 rc = vmdkCreateStreamImage(pImage, cbSize, uImageFlags, 4141 pfnProgress, pvUser, uPercentStart, 4142 uPercentSpan * 95 / 100); 4143 } 4144 else 4145 { 4146 /* Regular fixed or sparse image (monolithic or split). */ 4147 rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags, 4148 pfnProgress, pvUser, uPercentStart, 4149 uPercentSpan * 95 / 100); 4150 } 4151 } 4152 4153 if (RT_FAILURE(rc)) 4154 goto out; 4155 4156 if (RT_SUCCESS(rc) && pfnProgress) 4157 pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100); 4158 4159 pImage->cbSize = cbSize; 4160 4161 for (unsigned i = 0; i < pImage->cExtents; i++) 4162 { 4163 PVMDKEXTENT pExtent = &pImage->pExtents[i]; 4164 4165 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess, 4166 pExtent->cNominalSectors, pExtent->enmType, 4167 pExtent->pszBasename, pExtent->uSectorOffset); 4168 if (RT_FAILURE(rc)) 4169 { 4170 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename); 4171 goto out; 4172 } 4173 } 4174 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor); 4175 4176 if ( pPCHSGeometry->cCylinders != 0 4177 && pPCHSGeometry->cHeads != 0 4178 && pPCHSGeometry->cSectors != 0) 4179 { 4180 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry); 4181 if (RT_FAILURE(rc)) 4182 goto out; 4183 } 4184 if ( pLCHSGeometry->cCylinders != 0 4185 && pLCHSGeometry->cHeads != 0 4186 && pLCHSGeometry->cSectors != 0) 4187 { 4188 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry); 4189 if (RT_FAILURE(rc)) 4190 goto out; 4191 } 4192 4193 pImage->LCHSGeometry = *pLCHSGeometry; 4194 pImage->PCHSGeometry = *pPCHSGeometry; 4195 4196 pImage->ImageUuid = *pUuid; 4197 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4198 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid); 4199 if (RT_FAILURE(rc)) 4200 { 4201 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename); 4202 goto out; 4203 } 4204 RTUuidClear(&pImage->ParentUuid); 4205 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4206 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid); 4207 if (RT_FAILURE(rc)) 4208 { 4209 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename); 4210 goto out; 4211 } 4212 RTUuidClear(&pImage->ModificationUuid); 4213 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4214 VMDK_DDB_MODIFICATION_UUID, 4215 &pImage->ModificationUuid); 4216 if (RT_FAILURE(rc)) 4217 { 4218 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename); 4219 goto out; 4220 } 4221 RTUuidClear(&pImage->ParentModificationUuid); 4222 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4223 VMDK_DDB_PARENT_MODIFICATION_UUID, 4224 &pImage->ParentModificationUuid); 4225 if (RT_FAILURE(rc)) 4226 { 4227 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename); 4228 goto out; 4229 } 4230 4231 rc = vmdkAllocateGrainTableCache(pImage); 4232 if (RT_FAILURE(rc)) 4233 goto out; 4234 4235 rc = vmdkSetImageComment(pImage, pszComment); 4236 if (RT_FAILURE(rc)) 4237 { 4238 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename); 4239 goto out; 4240 } 4241 4242 if (RT_SUCCESS(rc) && pfnProgress) 4243 pfnProgress(pvUser, uPercentStart + uPercentSpan * 99 / 100); 4244 4245 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 4246 { 4247 /* streamOptimized is a bit special, we cannot trigger the flush 4248 * until all data has been written. So we write the necessary 4249 * information explicitly. */ 4250 pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64( pImage->Descriptor.aLines[pImage->Descriptor.cLines] 4251 - pImage->Descriptor.aLines[0], 512)); 4252 rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0); 4253 if (RT_FAILURE(rc)) 4254 { 4255 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename); 4256 goto out; 4257 } 4258 4259 rc = vmdkWriteDescriptor(pImage); 4260 if (RT_FAILURE(rc)) 4261 { 4262 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename); 4263 goto out; 4264 } 4265 } 4266 else 4267 rc = vmdkFlushImage(pImage); 4268 4269 out: 4270 if (RT_SUCCESS(rc) && pfnProgress) 4271 pfnProgress(pvUser, uPercentStart + uPercentSpan); 4272 4273 if (RT_FAILURE(rc)) 4274 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS); 4275 return rc; 4276 } 4277 4278 /** 4279 * Internal: Update image comment. 4280 */ 4281 static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment) 4282 { 4283 char *pszCommentEncoded; 4284 if (pszComment) 4285 { 4286 pszCommentEncoded = vmdkEncodeString(pszComment); 4287 if (!pszCommentEncoded) 4288 return VERR_NO_MEMORY; 4289 } 4290 else 4291 pszCommentEncoded = NULL; 4292 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, 4293 "ddb.comment", pszCommentEncoded); 4294 if (pszComment) 4295 RTStrFree(pszCommentEncoded); 4296 if (RT_FAILURE(rc)) 4297 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename); 4298 return VINF_SUCCESS; 4299 } 4300 4301 /** 4083 4302 * Internal. Clear the grain table buffer for real stream optimized writing. 4084 4303 */ 4085 static void vmdk sClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)4304 static void vmdkStreamClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent) 4086 4305 { 4087 4306 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE; … … 4094 4313 * Internal. Flush the grain table buffer for real stream optimized writing. 4095 4314 */ 4096 static int vmdk sFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,4097 uint32_t uGDEntry)4315 static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, 4316 uint32_t uGDEntry) 4098 4317 { 4099 4318 int rc = VINF_SUCCESS; … … 4122 4341 return VINF_SUCCESS; 4123 4342 4124 uint64_t uFileOffset ;4125 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset);4126 AssertRC(rc);4343 uint64_t uFileOffset = pExtent->uAppendPosition; 4344 if (!uFileOffset) 4345 return VERR_INTERNAL_ERROR; 4127 4346 /* Align to sector, as the previous write could have been any size. */ 4128 4347 uFileOffset = RT_ALIGN_64(uFileOffset, 512); … … 4161 4380 } 4162 4381 Assert(!(uFileOffset % 512)); 4382 pExtent->uAppendPosition = RT_ALIGN_64(uFileOffset, 512); 4163 4383 return rc; 4164 }4165 4166 /**4167 * Internal. Free all allocated space for representing a real stream optimized4168 * image, and optionally delete the image from disk.4169 */4170 static int vmdksFreeImage(PVMDKIMAGE pImage, bool fDelete)4171 {4172 int rc = VINF_SUCCESS;4173 4174 /* Freeing a never allocated image (e.g. because the open failed) is4175 * not signalled as an error. After all nothing bad happens. */4176 if (pImage)4177 {4178 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))4179 {4180 /* Check if all extents are clean. */4181 for (unsigned i = 0; i < pImage->cExtents; i++)4182 {4183 Assert(!pImage->pExtents[i].fUncleanShutdown);4184 }4185 }4186 4187 /* No need to write any pending data if the file will be deleted or if4188 * the new file wasn't successfully created. */4189 if (!fDelete && pImage->pExtents && pImage->pExtents[0].cGTEntries)4190 {4191 PVMDKEXTENT pExtent = &pImage->pExtents[0];4192 uint32_t uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries;4193 if (uLastGDEntry != pExtent->cGDEntries - 1)4194 {4195 rc = vmdksFlushGT(pImage, pExtent, uLastGDEntry);4196 AssertRC(rc);4197 vmdksClearGT(pImage, pExtent);4198 for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++)4199 {4200 rc = vmdksFlushGT(pImage, pExtent, i);4201 AssertRC(rc);4202 }4203 }4204 4205 uint64_t uFileOffset;4206 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset);4207 AssertRC(rc);4208 uFileOffset = RT_ALIGN_64(uFileOffset, 512);4209 4210 /* Grain directory marker. */4211 uint8_t aMarker[512];4212 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];4213 memset(pMarker, '\0', sizeof(aMarker));4214 pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64(pExtent->cGDEntries * sizeof(uint32_t)), 512));4215 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);4216 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,4217 aMarker, sizeof(aMarker), NULL);4218 AssertRC(rc);4219 uFileOffset += 512;4220 4221 /* Write grain directory in little endian style. The array will4222 * not be used after this, so convert in place. */4223 uint32_t *pGDTmp = pExtent->pGD;4224 for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++)4225 *pGDTmp = RT_H2LE_U32(*pGDTmp);4226 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,4227 pExtent->pGD,4228 pExtent->cGDEntries * sizeof(uint32_t),4229 NULL);4230 AssertRC(rc);4231 4232 pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);4233 pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset);4234 uFileOffset = RT_ALIGN_64(uFileOffset + pExtent->cGDEntries * sizeof(uint32_t), 512);4235 4236 /* Footer marker. */4237 memset(pMarker, '\0', sizeof(aMarker));4238 pMarker->uSector = VMDK_BYTE2SECTOR(512);4239 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);4240 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,4241 aMarker, sizeof(aMarker), NULL);4242 AssertRC(rc);4243 4244 uFileOffset += 512;4245 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset);4246 AssertRC(rc);4247 4248 uFileOffset += 512;4249 /* End-of-stream marker. */4250 memset(pMarker, '\0', sizeof(aMarker));4251 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset,4252 aMarker, sizeof(aMarker), NULL);4253 AssertRC(rc);4254 }4255 4256 if (pImage->pExtents != NULL)4257 {4258 for (unsigned i = 0 ; i < pImage->cExtents; i++)4259 vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);4260 RTMemFree(pImage->pExtents);4261 pImage->pExtents = NULL;4262 }4263 pImage->cExtents = 0;4264 if (pImage->pFile != NULL)4265 vmdkFileClose(pImage, &pImage->pFile, fDelete);4266 vmdkFileCheckAllClose(pImage);4267 4268 if (pImage->pGTCache)4269 {4270 RTMemFree(pImage->pGTCache);4271 pImage->pGTCache = NULL;4272 }4273 if (pImage->pDescData)4274 {4275 RTMemFree(pImage->pDescData);4276 pImage->pDescData = NULL;4277 }4278 }4279 4280 LogFlowFunc(("returns %Rrc\n", rc));4281 return rc;4282 }4283 4284 /**4285 * Internal: Create a real stream optimized VMDK using only linear writes.4286 */4287 static int vmdksCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,4288 unsigned uImageFlags, const char *pszComment,4289 PCVDGEOMETRY pPCHSGeometry,4290 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,4291 PFNVDPROGRESS pfnProgress, void *pvUser,4292 unsigned uPercentStart, unsigned uPercentSpan)4293 {4294 int rc;4295 4296 pImage->uImageFlags = uImageFlags;4297 4298 /* Try to get error interface. */4299 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);4300 if (pImage->pInterfaceError)4301 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);4302 4303 /* Get I/O interface. */4304 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);4305 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);4306 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);4307 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);4308 4309 PVMDKEXTENT pExtent;4310 char *pszBasenameSubstr, *pszBasedirectory, *pszBasename;4311 size_t cbBasenameSubstr;4312 4313 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,4314 &pImage->Descriptor);4315 if (RT_FAILURE(rc))4316 {4317 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);4318 goto out;4319 }4320 4321 rc = vmdkCreateExtents(pImage, 1);4322 if (RT_FAILURE(rc))4323 {4324 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);4325 goto out;4326 }4327 4328 /* Basename strings needed for constructing the extent names. */4329 pszBasenameSubstr = RTPathFilename(pImage->pszFilename);4330 AssertPtr(pszBasenameSubstr);4331 cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;4332 4333 /* No separate descriptor file. */4334 pImage->pFile = NULL;4335 4336 /* Set up all extents. */4337 pExtent = &pImage->pExtents[0];4338 4339 /* Set up fullname/basename for extent description. Cannot use StrDup4340 * for basename, as it is not guaranteed that the memory can be freed4341 * with RTMemTmpFree, which must be used as in other code paths4342 * StrDup is not usable. */4343 pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);4344 if (!pszBasename)4345 {4346 rc = VERR_NO_MEMORY;4347 goto out;4348 }4349 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);4350 pExtent->pszBasename = pszBasename;4351 4352 pszBasedirectory = RTStrDup(pImage->pszFilename);4353 RTPathStripFilename(pszBasedirectory);4354 char *pszFullname;4355 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszBasedirectory,4356 RTPATH_SLASH, pExtent->pszBasename);4357 RTStrFree(pszBasedirectory);4358 if (RT_FAILURE(rc))4359 goto out;4360 pExtent->pszFullname = pszFullname;4361 4362 /* Create file for extent. */4363 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,4364 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,4365 true /* fCreate */),4366 false /* fAsyncIO */);4367 if (RT_FAILURE(rc))4368 {4369 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);4370 goto out;4371 }4372 4373 /* Place descriptor file information. */4374 pExtent->uDescriptorSector = 1;4375 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);4376 /* The descriptor is part of the (only) extent. */4377 pExtent->pDescData = pImage->pDescData;4378 pImage->pDescData = NULL;4379 4380 uint64_t cSectorsPerGDE, cSectorsPerGD;4381 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;4382 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));4383 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);4384 pExtent->cGTEntries = 512;4385 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;4386 pExtent->cSectorsPerGDE = cSectorsPerGDE;4387 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;4388 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));4389 4390 /* The spec says version is 1 for all VMDKs, but the vast4391 * majority of streamOptimized VMDKs actually contain4392 * version 3 - so go with the majority. Both are acepted. */4393 pExtent->uVersion = 3;4394 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;4395 pExtent->fFooter = true;4396 4397 pExtent->enmAccess = VMDKACCESS_READONLY;4398 pExtent->fUncleanShutdown = false;4399 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);4400 pExtent->uSectorOffset = 0;4401 pExtent->fMetaDirty = true;4402 4403 /* Create grain directory, without preallocating it straight away. It will4404 * be constructed on the fly when writing out the data and written when4405 * closing the image. The end effect is that the full grain directory is4406 * allocated, which is a requirement of the VMDK specs. */4407 rc = vmdkCreateGrainDirectory(pImage, pExtent,4408 RT_MAX( pExtent->uDescriptorSector4409 + pExtent->cDescriptorSectors,4410 1),4411 false /* fPreAlloc */);4412 if (RT_FAILURE(rc))4413 {4414 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);4415 goto out;4416 }4417 4418 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",4419 "streamOptimized");4420 if (RT_FAILURE(rc))4421 {4422 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);4423 goto out;4424 }4425 4426 if (pfnProgress)4427 pfnProgress(pvUser, uPercentStart + uPercentSpan * 20 / 100);4428 4429 pImage->cbSize = cbSize;4430 4431 Assert(pImage->cExtents == 1);4432 4433 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,4434 pExtent->cNominalSectors, pExtent->enmType,4435 pExtent->pszBasename, pExtent->uSectorOffset);4436 if (RT_FAILURE(rc))4437 {4438 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);4439 goto out;4440 }4441 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);4442 4443 if ( pPCHSGeometry->cCylinders != 04444 && pPCHSGeometry->cHeads != 04445 && pPCHSGeometry->cSectors != 0)4446 {4447 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);4448 if (RT_FAILURE(rc))4449 goto out;4450 }4451 if ( pLCHSGeometry->cCylinders != 04452 && pLCHSGeometry->cHeads != 04453 && pLCHSGeometry->cSectors != 0)4454 {4455 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);4456 if (RT_FAILURE(rc))4457 goto out;4458 }4459 4460 pImage->LCHSGeometry = *pLCHSGeometry;4461 pImage->PCHSGeometry = *pPCHSGeometry;4462 4463 pImage->ImageUuid = *pUuid;4464 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4465 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);4466 if (RT_FAILURE(rc))4467 {4468 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);4469 goto out;4470 }4471 RTUuidClear(&pImage->ParentUuid);4472 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4473 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);4474 if (RT_FAILURE(rc))4475 {4476 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);4477 goto out;4478 }4479 RTUuidClear(&pImage->ModificationUuid);4480 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4481 VMDK_DDB_MODIFICATION_UUID,4482 &pImage->ModificationUuid);4483 if (RT_FAILURE(rc))4484 {4485 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);4486 goto out;4487 }4488 RTUuidClear(&pImage->ParentModificationUuid);4489 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4490 VMDK_DDB_PARENT_MODIFICATION_UUID,4491 &pImage->ParentModificationUuid);4492 if (RT_FAILURE(rc))4493 {4494 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);4495 goto out;4496 }4497 4498 rc = vmdkAllocateGrainTableCache(pImage);4499 if (RT_FAILURE(rc))4500 goto out;4501 4502 rc = vmdkSetImageComment(pImage, pszComment);4503 if (RT_FAILURE(rc))4504 {4505 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);4506 goto out;4507 }4508 4509 if (pfnProgress)4510 pfnProgress(pvUser, uPercentStart + uPercentSpan * 50 / 100);4511 4512 /* Now that all descriptor entries are complete, shrink it to the minimum4513 * size. It will never be changed afterwards anyway. */4514 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64( pImage->Descriptor.aLines[pImage->Descriptor.cLines]4515 - pImage->Descriptor.aLines[0], 512));4516 rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0);4517 if (RT_FAILURE(rc))4518 {4519 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);4520 goto out;4521 }4522 4523 if (pfnProgress)4524 pfnProgress(pvUser, uPercentStart + uPercentSpan * 70 / 100);4525 4526 rc = vmdkWriteDescriptor(pImage);4527 if (RT_FAILURE(rc))4528 {4529 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);4530 goto out;4531 }4532 4533 /* Skip over the overhead area. */4534 rc = vmdkFileSetSize(pImage, pExtent->pFile,4535 VMDK_SECTOR2BYTE(pExtent->cOverheadSectors));4536 4537 out:4538 if (RT_SUCCESS(rc) && pfnProgress)4539 pfnProgress(pvUser, uPercentStart + uPercentSpan);4540 4541 if (RT_FAILURE(rc))4542 vmdksFreeImage(pImage, rc != VERR_ALREADY_EXISTS);4543 return rc;4544 }4545 4546 /**4547 * Internal: The actual code for creating any VMDK variant currently in4548 * existence on hosted environments.4549 */4550 static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,4551 unsigned uImageFlags, const char *pszComment,4552 PCVDGEOMETRY pPCHSGeometry,4553 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,4554 PFNVDPROGRESS pfnProgress, void *pvUser,4555 unsigned uPercentStart, unsigned uPercentSpan)4556 {4557 int rc;4558 4559 pImage->uImageFlags = uImageFlags;4560 4561 /* Try to get error interface. */4562 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);4563 if (pImage->pInterfaceError)4564 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);4565 4566 /* Get I/O interface. */4567 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);4568 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);4569 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);4570 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);4571 4572 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,4573 &pImage->Descriptor);4574 if (RT_FAILURE(rc))4575 {4576 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);4577 goto out;4578 }4579 4580 if ( (uImageFlags & VD_IMAGE_FLAGS_FIXED)4581 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))4582 {4583 /* Raw disk image (includes raw partition). */4584 const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment;4585 /* As the comment is misused, zap it so that no garbage comment4586 * is set below. */4587 pszComment = NULL;4588 rc = vmdkCreateRawImage(pImage, pRaw, cbSize);4589 }4590 else4591 {4592 /* Regular fixed or sparse image (monolithic or split). */4593 rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags,4594 pfnProgress, pvUser, uPercentStart,4595 uPercentSpan * 95 / 100);4596 }4597 4598 if (RT_FAILURE(rc))4599 goto out;4600 4601 if (RT_SUCCESS(rc) && pfnProgress)4602 pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);4603 4604 pImage->cbSize = cbSize;4605 4606 for (unsigned i = 0; i < pImage->cExtents; i++)4607 {4608 PVMDKEXTENT pExtent = &pImage->pExtents[i];4609 4610 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,4611 pExtent->cNominalSectors, pExtent->enmType,4612 pExtent->pszBasename, pExtent->uSectorOffset);4613 if (RT_FAILURE(rc))4614 {4615 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);4616 goto out;4617 }4618 }4619 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);4620 4621 if ( pPCHSGeometry->cCylinders != 04622 && pPCHSGeometry->cHeads != 04623 && pPCHSGeometry->cSectors != 0)4624 {4625 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);4626 if (RT_FAILURE(rc))4627 goto out;4628 }4629 if ( pLCHSGeometry->cCylinders != 04630 && pLCHSGeometry->cHeads != 04631 && pLCHSGeometry->cSectors != 0)4632 {4633 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);4634 if (RT_FAILURE(rc))4635 goto out;4636 }4637 4638 pImage->LCHSGeometry = *pLCHSGeometry;4639 pImage->PCHSGeometry = *pPCHSGeometry;4640 4641 pImage->ImageUuid = *pUuid;4642 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4643 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);4644 if (RT_FAILURE(rc))4645 {4646 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);4647 goto out;4648 }4649 RTUuidClear(&pImage->ParentUuid);4650 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4651 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);4652 if (RT_FAILURE(rc))4653 {4654 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);4655 goto out;4656 }4657 RTUuidClear(&pImage->ModificationUuid);4658 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4659 VMDK_DDB_MODIFICATION_UUID,4660 &pImage->ModificationUuid);4661 if (RT_FAILURE(rc))4662 {4663 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);4664 goto out;4665 }4666 RTUuidClear(&pImage->ParentModificationUuid);4667 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,4668 VMDK_DDB_PARENT_MODIFICATION_UUID,4669 &pImage->ParentModificationUuid);4670 if (RT_FAILURE(rc))4671 {4672 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);4673 goto out;4674 }4675 4676 rc = vmdkAllocateGrainTableCache(pImage);4677 if (RT_FAILURE(rc))4678 goto out;4679 4680 rc = vmdkSetImageComment(pImage, pszComment);4681 if (RT_FAILURE(rc))4682 {4683 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);4684 goto out;4685 }4686 4687 if (RT_SUCCESS(rc) && pfnProgress)4688 pfnProgress(pvUser, uPercentStart + uPercentSpan * 99 / 100);4689 4690 rc = vmdkFlushImage(pImage);4691 4692 out:4693 if (RT_SUCCESS(rc) && pfnProgress)4694 pfnProgress(pvUser, uPercentStart + uPercentSpan);4695 4696 if (RT_FAILURE(rc))4697 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);4698 return rc;4699 }4700 4701 /**4702 * Internal: Update image comment.4703 */4704 static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)4705 {4706 char *pszCommentEncoded;4707 if (pszComment)4708 {4709 pszCommentEncoded = vmdkEncodeString(pszComment);4710 if (!pszCommentEncoded)4711 return VERR_NO_MEMORY;4712 }4713 else4714 pszCommentEncoded = NULL;4715 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,4716 "ddb.comment", pszCommentEncoded);4717 if (pszComment)4718 RTStrFree(pszCommentEncoded);4719 if (RT_FAILURE(rc))4720 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);4721 return VINF_SUCCESS;4722 4384 } 4723 4385 … … 4736 4398 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 4737 4399 { 4738 /* Mark all extents as clean. */ 4739 for (unsigned i = 0; i < pImage->cExtents; i++) 4400 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 4740 4401 { 4741 if ( ( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE 4742 #ifdef VBOX_WITH_VMDK_ESX 4743 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE 4744 #endif /* VBOX_WITH_VMDK_ESX */ 4745 ) 4746 && pImage->pExtents[i].fUncleanShutdown) 4402 /* Check if all extents are clean. */ 4403 for (unsigned i = 0; i < pImage->cExtents; i++) 4747 4404 { 4748 pImage->pExtents[i].fUncleanShutdown = false; 4749 pImage->pExtents[i].fMetaDirty = true; 4405 Assert(!pImage->pExtents[i].fUncleanShutdown); 4750 4406 } 4751 4407 } 4752 } 4753 vmdkFlushImage(pImage); 4408 else 4409 { 4410 /* Mark all extents as clean. */ 4411 for (unsigned i = 0; i < pImage->cExtents; i++) 4412 { 4413 if ( ( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE 4414 #ifdef VBOX_WITH_VMDK_ESX 4415 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE 4416 #endif /* VBOX_WITH_VMDK_ESX */ 4417 ) 4418 && pImage->pExtents[i].fUncleanShutdown) 4419 { 4420 pImage->pExtents[i].fUncleanShutdown = false; 4421 pImage->pExtents[i].fMetaDirty = true; 4422 } 4423 4424 /* From now on it's not safe to append any more data. */ 4425 pImage->pExtents[i].uAppendPosition = 0; 4426 } 4427 } 4428 } 4429 4430 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 4431 { 4432 /* No need to write any pending data if the file will be deleted 4433 * or if the new file wasn't successfully created. */ 4434 if (!fDelete && pImage->pExtents && pImage->pExtents[0].cGTEntries) 4435 { 4436 PVMDKEXTENT pExtent = &pImage->pExtents[0]; 4437 uint32_t uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries; 4438 if (uLastGDEntry != pExtent->cGDEntries - 1) 4439 { 4440 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry); 4441 AssertRC(rc); 4442 vmdkStreamClearGT(pImage, pExtent); 4443 for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++) 4444 { 4445 rc = vmdkStreamFlushGT(pImage, pExtent, i); 4446 AssertRC(rc); 4447 } 4448 } 4449 4450 uint64_t uFileOffset = pExtent->uAppendPosition; 4451 if (!uFileOffset) 4452 return VERR_INTERNAL_ERROR; 4453 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 4454 4455 /* From now on it's not safe to append any more data. */ 4456 pExtent->uAppendPosition = 0; 4457 4458 /* Grain directory marker. */ 4459 uint8_t aMarker[512]; 4460 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0]; 4461 memset(pMarker, '\0', sizeof(aMarker)); 4462 pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64(pExtent->cGDEntries * sizeof(uint32_t)), 512)); 4463 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD); 4464 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4465 aMarker, sizeof(aMarker), NULL); 4466 AssertRC(rc); 4467 uFileOffset += 512; 4468 4469 /* Write grain directory in little endian style. The array will 4470 * not be used after this, so convert in place. */ 4471 uint32_t *pGDTmp = pExtent->pGD; 4472 for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++) 4473 *pGDTmp = RT_H2LE_U32(*pGDTmp); 4474 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4475 pExtent->pGD, 4476 pExtent->cGDEntries * sizeof(uint32_t), 4477 NULL); 4478 AssertRC(rc); 4479 4480 pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset); 4481 pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset); 4482 uFileOffset = RT_ALIGN_64( uFileOffset 4483 + pExtent->cGDEntries * sizeof(uint32_t), 4484 512); 4485 4486 /* Footer marker. */ 4487 memset(pMarker, '\0', sizeof(aMarker)); 4488 pMarker->uSector = VMDK_BYTE2SECTOR(512); 4489 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER); 4490 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4491 aMarker, sizeof(aMarker), NULL); 4492 AssertRC(rc); 4493 4494 uFileOffset += 512; 4495 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset); 4496 AssertRC(rc); 4497 4498 uFileOffset += 512; 4499 /* End-of-stream marker. */ 4500 memset(pMarker, '\0', sizeof(aMarker)); 4501 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4502 aMarker, sizeof(aMarker), NULL); 4503 AssertRC(rc); 4504 } 4505 } 4506 else 4507 vmdkFlushImage(pImage); 4754 4508 4755 4509 if (pImage->pExtents != NULL) … … 4805 4559 { 4806 4560 case VMDKETYPE_HOSTED_SPARSE: 4807 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0); 4808 if (RT_FAILURE(rc)) 4809 goto out; 4810 if (pExtent->fFooter) 4561 if (!pExtent->fFooter) 4811 4562 { 4812 uint64_t cbSize; 4813 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbSize); 4563 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0); 4814 4564 if (RT_FAILURE(rc)) 4815 4565 goto out; 4816 cbSize = RT_ALIGN_64(cbSize, 512); 4817 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, cbSize - 2*512); 4566 } 4567 else 4568 { 4569 uint64_t uFileOffset = pExtent->uAppendPosition; 4570 /* Simply skip writing anything if the streamOptimized 4571 * image hasn't been just created. */ 4572 if (!uFileOffset) 4573 break; 4574 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 4575 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 4576 uFileOffset); 4818 4577 if (RT_FAILURE(rc)) 4819 4578 goto out; … … 4986 4745 int rc; 4987 4746 4747 /* For newly created streamOptimized images this must be a no-op. */ 4748 if ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED 4749 && pExtent->uAppendPosition) 4750 { 4751 *puExtentSector = 0; 4752 return VINF_SUCCESS; 4753 } 4754 4988 4755 uGDIndex = uSector / pExtent->cSectorsPerGDE; 4989 4756 if (uGDIndex >= pExtent->cGDEntries) … … 5092 4859 PVMDKGTCACHE pCache = pImage->pGTCache; 5093 4860 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock; 5094 uint64_t cbExtentSize;4861 uint64_t uFileOffset; 5095 4862 uint32_t uGTHash, uGTBlockIndex; 5096 4863 PVMDKGTCACHEENTRY pGTCacheEntry; … … 5111 4878 * entry. So there is absolutely no data in this area. Allocate 5112 4879 * a new grain table and put the reference to it in the GDs. */ 5113 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize); 5114 if (RT_FAILURE(rc)) 5115 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 5116 Assert(!(cbExtentSize % 512)); 5117 cbExtentSize = RT_ALIGN_64(cbExtentSize, 512); 5118 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize); 5119 /* For writable streamOptimized extents the final sector is the 5120 * end-of-stream marker. Will be re-added after the grain table. 5121 * If the file has a footer it also will be re-added before EOS. */ 5122 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 5123 { 5124 uint64_t uEOSOff = 0; 5125 uGTSector--; 5126 if (pExtent->fFooter) 5127 { 5128 uGTSector--; 5129 uEOSOff = 512; 5130 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, VMDK_SECTOR2BYTE(uGTSector) + pExtent->cGTEntries * sizeof(uint32_t)); 5131 if (RT_FAILURE(rc)) 5132 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write footer after grain table in '%s'"), pExtent->pszFullname); 5133 } 5134 pExtent->uLastGrainSector = 0; 5135 uint8_t aEOS[512]; 5136 memset(aEOS, '\0', sizeof(aEOS)); 5137 rc = vmdkFileWriteSync(pImage, pExtent->pFile, 5138 VMDK_SECTOR2BYTE(uGTSector) + pExtent->cGTEntries * sizeof(uint32_t) + uEOSOff, 5139 aEOS, sizeof(aEOS), NULL); 5140 if (RT_FAILURE(rc)) 5141 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write end-of stream marker after grain table in '%s'"), pExtent->pszFullname); 5142 } 4880 uFileOffset = pExtent->uAppendPosition; 4881 if (!uFileOffset) 4882 return VERR_INTERNAL_ERROR; 4883 Assert(!(uFileOffset % 512)); 4884 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 4885 uGTSector = VMDK_BYTE2SECTOR(uFileOffset); 4886 4887 pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t); 4888 5143 4889 /* Normally the grain table is preallocated for hosted sparse extents 5144 4890 * that support more than 32 bit sector numbers. So this shouldn't … … 5146 4892 if (uGTSector > UINT32_MAX) 5147 4893 return VERR_VD_VMDK_INVALID_HEADER; 4894 5148 4895 /* Write grain table by writing the required number of grain table 5149 4896 * cache chunks. Avoids dynamic memory allocation, but is a bit … … 5161 4908 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname); 5162 4909 } 4910 pExtent->uAppendPosition = RT_ALIGN_64( pExtent->uAppendPosition 4911 + pExtent->cGTEntries * sizeof(uint32_t), 4912 512); 4913 5163 4914 if (pExtent->pRGD) 5164 4915 { 5165 4916 AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER); 5166 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize); 5167 if (RT_FAILURE(rc)) 5168 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 5169 Assert(!(cbExtentSize % 512)); 5170 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize); 5171 /* For writable streamOptimized extents the final sector is the 5172 * end-of-stream marker. Will be re-added after the grain table. 5173 * If the file has a footer it also will be re-added before EOS. */ 5174 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 5175 { 5176 uint64_t uEOSOff = 0; 5177 uRGTSector--; 5178 if (pExtent->fFooter) 5179 { 5180 uRGTSector--; 5181 uEOSOff = 512; 5182 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, VMDK_SECTOR2BYTE(uRGTSector) + pExtent->cGTEntries * sizeof(uint32_t)); 5183 if (RT_FAILURE(rc)) 5184 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write footer after redundant grain table in '%s'"), pExtent->pszFullname); 5185 } 5186 pExtent->uLastGrainSector = 0; 5187 uint8_t aEOS[512]; 5188 memset(aEOS, '\0', sizeof(aEOS)); 5189 rc = vmdkFileWriteSync(pImage, pExtent->pFile, 5190 VMDK_SECTOR2BYTE(uRGTSector) + pExtent->cGTEntries * sizeof(uint32_t) + uEOSOff, 5191 aEOS, sizeof(aEOS), NULL); 5192 if (RT_FAILURE(rc)) 5193 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write end-of stream marker after redundant grain table in '%s'"), pExtent->pszFullname); 5194 } 4917 uFileOffset = pExtent->uAppendPosition; 4918 if (!uFileOffset) 4919 return VERR_INTERNAL_ERROR; 4920 Assert(!(uFileOffset % 512)); 4921 uRGTSector = VMDK_BYTE2SECTOR(uFileOffset); 4922 4923 pExtent->uAppendPosition += pExtent->cGTEntries * sizeof(uint32_t); 4924 5195 4925 /* Normally the redundant grain table is preallocated for hosted 5196 4926 * sparse extents that support more than 32 bit sector numbers. So … … 5198 4928 if (uRGTSector > UINT32_MAX) 5199 4929 return VERR_VD_VMDK_INVALID_HEADER; 4930 5200 4931 /* Write backup grain table by writing the required number of grain 5201 4932 * table cache chunks. Avoids dynamic memory allocation, but is a … … 5212 4943 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname); 5213 4944 } 4945 4946 pExtent->uAppendPosition = pExtent->uAppendPosition 4947 + pExtent->cGTEntries * sizeof(uint32_t); 5214 4948 } 5215 4949 … … 5240 4974 } 5241 4975 5242 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize);5243 if ( RT_FAILURE(rc))5244 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);5245 Assert(!( cbExtentSize% 512));4976 uFileOffset = pExtent->uAppendPosition; 4977 if (!uFileOffset) 4978 return VERR_INTERNAL_ERROR; 4979 Assert(!(uFileOffset % 512)); 5246 4980 5247 4981 /* Write the data. Always a full grain, or we're in big trouble. */ 5248 4982 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 5249 4983 { 5250 /* For streamOptimized extents this is a little more difficult, as the 5251 * cached data also needs to be updated, to handle updating the last 5252 * written block properly. Also we're trying to avoid unnecessary gaps. 5253 * Additionally the end-of-stream marker needs to be written. */ 5254 if (!pExtent->uLastGrainSector) 5255 { 5256 cbExtentSize -= 512; 5257 if (pExtent->fFooter) 5258 cbExtentSize -= 512; 5259 } 5260 else 5261 cbExtentSize = VMDK_SECTOR2BYTE(pExtent->uLastGrainSector) + pExtent->cbLastGrainWritten; 5262 Assert(cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)); 4984 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 4985 return vmdkError(pImage, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname); 4986 4987 /* Invalidate cache, just in case some code incorrectly allows mixing 4988 * of reads and writes. Normally shouldn't be needed. */ 4989 pExtent->uGrainSector = 0; 4990 pExtent->uLastGrainSector = 0; 4991 4992 /* Write compressed data block and the markers. */ 5263 4993 uint32_t cbGrain = 0; 5264 rc = vmdkFileDeflateSync(pImage, pExtent, cbExtentSize,4994 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, 5265 4995 pvBuf, cbWrite, uSector, &cbGrain); 5266 4996 if (RT_FAILURE(rc)) 5267 4997 { 5268 pExtent->uGrainSector = 0;5269 pExtent->uLastGrainSector = 0;5270 4998 AssertRC(rc); 5271 4999 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname); 5272 5000 } 5273 pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(cbExtentSize);5274 5001 pExtent->uLastGrainWritten = uSector / pExtent->cSectorsPerGrain; 5275 pExtent->cbLastGrainWritten = cbGrain; 5276 memcpy(pExtent->pvGrain, pvBuf, cbWrite); 5277 pExtent->uGrainSector = uSector; 5278 5279 uint64_t uEOSOff = 0; 5280 if (pExtent->fFooter) 5281 { 5282 uEOSOff = 512; 5283 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, cbExtentSize + RT_ALIGN(cbGrain, 512)); 5284 if (RT_FAILURE(rc)) 5285 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write footer after allocated data block in '%s'"), pExtent->pszFullname); 5286 } 5287 uint8_t aEOS[512]; 5288 memset(aEOS, '\0', sizeof(aEOS)); 5289 rc = vmdkFileWriteSync(pImage, pExtent->pFile, 5290 cbExtentSize + RT_ALIGN(cbGrain, 512) + uEOSOff, 5291 aEOS, sizeof(aEOS), NULL); 5292 if (RT_FAILURE(rc)) 5293 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write end-of stream marker after allocated data block in '%s'"), pExtent->pszFullname); 5002 pExtent->uAppendPosition += cbGrain; 5294 5003 } 5295 5004 else 5296 5005 { 5297 rc = vmdkFileWriteSync(pImage, pExtent->pFile, cbExtentSize, pvBuf, cbWrite, NULL); 5006 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 5007 pvBuf, cbWrite, NULL); 5298 5008 if (RT_FAILURE(rc)) 5299 5009 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname); 5010 pExtent->uAppendPosition += cbWrite; 5300 5011 } 5301 5012 … … 5326 5037 } 5327 5038 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE; 5328 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR( cbExtentSize));5329 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR( cbExtentSize);5039 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(uFileOffset)); 5040 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(uFileOffset); 5330 5041 /* Update grain table on disk. */ 5331 5042 rc = vmdkFileWriteSync(pImage, pExtent->pFile, … … 5350 5061 } 5351 5062 #endif /* VBOX_WITH_VMDK_ESX */ 5063 return rc; 5064 } 5065 5066 /** 5067 * Internal. Writes the grain and also if necessary the grain tables. 5068 * Uses the grain table cache as a true grain table. 5069 */ 5070 static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, 5071 uint64_t uSector, const void *pvBuf, 5072 uint64_t cbWrite) 5073 { 5074 uint32_t uGrain; 5075 uint32_t uGDEntry, uLastGDEntry; 5076 uint32_t cbGrain = 0; 5077 uint32_t uCacheLine, uCacheEntry; 5078 const void *pData = pvBuf; 5079 int rc; 5080 5081 /* Very strict requirements: always write at least one full grain, with 5082 * proper alignment. Everything else would require reading of already 5083 * written data, which we don't support for obvious reasons. The only 5084 * exception is the last grain, and only if the image size specifies 5085 * that only some portion holds data. In any case the write must be 5086 * within the image limits, no "overshoot" allowed. */ 5087 if ( cbWrite == 0 5088 || ( cbWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) 5089 && pExtent->cNominalSectors - uSector >= pExtent->cSectorsPerGrain) 5090 || uSector % pExtent->cSectorsPerGrain 5091 || uSector + VMDK_BYTE2SECTOR(cbWrite) > pExtent->cNominalSectors) 5092 return VERR_INVALID_PARAMETER; 5093 5094 /* Clip write range to at most the rest of the grain. */ 5095 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain)); 5096 5097 /* Do not allow to go back. */ 5098 uGrain = uSector / pExtent->cSectorsPerGrain; 5099 uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; 5100 uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE; 5101 uGDEntry = uGrain / pExtent->cGTEntries; 5102 uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries; 5103 if (uGrain < pExtent->uLastGrainWritten) 5104 return VERR_VD_VMDK_INVALID_WRITE; 5105 5106 /* Zero byte write optimization. Since we don't tell VBoxHDD that we need 5107 * to allocate something, we also need to detect the situation ourself. */ 5108 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES) 5109 && ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbWrite * 8) == -1) 5110 return VINF_SUCCESS; 5111 5112 if (uGDEntry != uLastGDEntry) 5113 { 5114 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry); 5115 if (RT_FAILURE(rc)) 5116 return rc; 5117 vmdkStreamClearGT(pImage, pExtent); 5118 for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++) 5119 { 5120 rc = vmdkStreamFlushGT(pImage, pExtent, i); 5121 if (RT_FAILURE(rc)) 5122 return rc; 5123 } 5124 } 5125 5126 uint64_t uFileOffset; 5127 uFileOffset = pExtent->uAppendPosition; 5128 if (!uFileOffset) 5129 return VERR_INTERNAL_ERROR; 5130 /* Align to sector, as the previous write could have been any size. */ 5131 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 5132 5133 /* Paranoia check: extent type, grain table buffer presence and 5134 * grain table buffer space. Also grain table entry must be clear. */ 5135 if ( pExtent->enmType != VMDKETYPE_HOSTED_SPARSE 5136 || !pImage->pGTCache 5137 || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE 5138 || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry]) 5139 return VERR_INTERNAL_ERROR; 5140 5141 /* Update grain table entry. */ 5142 pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset); 5143 5144 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 5145 { 5146 memcpy(pExtent->pvGrain, pvBuf, cbWrite); 5147 memset((char *)pExtent->pvGrain + cbWrite, '\0', 5148 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite); 5149 pData = pExtent->pvGrain; 5150 } 5151 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData, 5152 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), 5153 uSector, &cbGrain); 5154 if (RT_FAILURE(rc)) 5155 { 5156 pExtent->uGrainSector = 0; 5157 pExtent->uLastGrainSector = 0; 5158 AssertRC(rc); 5159 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname); 5160 } 5161 pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(uFileOffset); 5162 pExtent->uLastGrainWritten = uGrain; 5163 pExtent->uAppendPosition += cbGrain; 5164 5352 5165 return rc; 5353 5166 } … … 5414 5227 pGrainAlloc->fGTUpdateNeeded = false; 5415 5228 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE; 5416 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc-> cbExtentSize));5417 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc-> cbExtentSize);5229 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset)); 5230 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset); 5418 5231 /* Update grain table on disk. */ 5419 5232 rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile, … … 5487 5300 PVMDKGTCACHE pCache = pImage->pGTCache; 5488 5301 uint64_t uGDIndex, uGTSector, uRGTSector; 5489 uint64_t cbExtentSize;5302 uint64_t uFileOffset; 5490 5303 PVMDKGRAINALLOCASYNC pGrainAlloc = NULL; 5491 5304 int rc; … … 5518 5331 * entry. So there is absolutely no data in this area. Allocate 5519 5332 * a new grain table and put the reference to it in the GDs. */ 5520 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize); 5521 if (RT_FAILURE(rc)) 5522 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 5523 Assert(!(cbExtentSize % 512)); 5524 5525 pGrainAlloc->cbExtentOld = cbExtentSize; 5526 5527 cbExtentSize = RT_ALIGN_64(cbExtentSize, 512); 5528 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize); 5333 uFileOffset = pExtent->uAppendPosition; 5334 if (!uFileOffset) 5335 return VERR_INTERNAL_ERROR; 5336 Assert(!(uFileOffset % 512)); 5337 5338 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 5339 uGTSector = VMDK_BYTE2SECTOR(uFileOffset); 5529 5340 5530 5341 /* Normally the grain table is preallocated for hosted sparse extents … … 5536 5347 /* Write grain table by writing the required number of grain table 5537 5348 * cache chunks. Allocate memory dynamically here or we flood the 5538 * metadata cache with very small entries. 5539 */ 5540 size_t cbGTDataTmp = (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE) * VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t); 5349 * metadata cache with very small entries. */ 5350 size_t cbGTDataTmp = pExtent->cGTEntries * sizeof(uint32_t); 5541 5351 uint32_t *paGTDataTmp = (uint32_t *)RTMemTmpAllocZ(cbGTDataTmp); 5542 5352 … … 5556 5366 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname); 5557 5367 } 5368 pExtent->uAppendPosition = RT_ALIGN_64( pExtent->uAppendPosition 5369 + cbGTDataTmp, 512); 5558 5370 5559 5371 if (pExtent->pRGD) 5560 5372 { 5561 5373 AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER); 5562 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize);5563 if ( RT_FAILURE(rc))5564 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);5565 Assert(!( cbExtentSize% 512));5566 uRGTSector = VMDK_BYTE2SECTOR( cbExtentSize);5374 uFileOffset = pExtent->uAppendPosition; 5375 if (!uFileOffset) 5376 return VERR_INTERNAL_ERROR; 5377 Assert(!(uFileOffset % 512)); 5378 uRGTSector = VMDK_BYTE2SECTOR(uFileOffset); 5567 5379 5568 5380 /* Normally the redundant grain table is preallocated for hosted … … 5574 5386 return VERR_VD_VMDK_INVALID_HEADER; 5575 5387 } 5576 /* Write backup grain table by writing the required number of grain 5577 * table cache chunks. */ 5388 5389 /* Write grain table by writing the required number of grain table 5390 * cache chunks. Allocate memory dynamically here or we flood the 5391 * metadata cache with very small entries. */ 5578 5392 rc = vmdkFileWriteMetaAsync(pImage, pExtent->pFile, 5579 5393 VMDK_SECTOR2BYTE(uRGTSector), … … 5587 5401 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname); 5588 5402 } 5403 5404 pExtent->uAppendPosition = pExtent->uAppendPosition + cbGTDataTmp; 5589 5405 } 5590 5406 … … 5627 5443 pGrainAlloc->uRGTSector = uRGTSector; 5628 5444 5629 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbExtentSize); 5630 if (RT_FAILURE(rc)) 5631 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 5632 Assert(!(cbExtentSize % 512)); 5633 5634 if (!pGrainAlloc->cbExtentOld) 5635 pGrainAlloc->cbExtentOld = cbExtentSize; 5636 5637 pGrainAlloc->cbExtentSize = cbExtentSize; 5445 uFileOffset = pExtent->uAppendPosition; 5446 if (!uFileOffset) 5447 return VERR_INTERNAL_ERROR; 5448 Assert(!(uFileOffset % 512)); 5449 5450 pGrainAlloc->uGrainOffset = uFileOffset; 5638 5451 5639 5452 /* Write the data. Always a full grain, or we're in big trouble. */ 5640 5453 rc = vmdkFileWriteUserAsync(pImage, pExtent->pFile, 5641 cbExtentSize, pIoCtx, cbWrite,5454 uFileOffset, pIoCtx, cbWrite, 5642 5455 vmdkAllocGrainAsyncComplete, pGrainAlloc); 5643 5456 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS) … … 5688 5501 } 5689 5502 return pszNewStr; 5690 }5691 5692 5693 /** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */5694 static int vmdksCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,5695 PVDINTERFACE pVDIfsImage)5696 {5697 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));5698 int rc = VINF_SUCCESS;5699 5700 if ( !VALID_PTR(pszFilename)5701 || !*pszFilename5702 || strchr(pszFilename, '"'))5703 {5704 rc = VERR_INVALID_PARAMETER;5705 goto out;5706 }5707 5708 /* Always return failure, to avoid opening other VMDK files via this5709 * special VMDK streamOptimized format backend. */5710 rc = VERR_VD_VMDK_INVALID_HEADER;5711 5712 out:5713 LogFlowFunc(("returns %Rrc\n", rc));5714 return rc;5715 }5716 5717 5718 /** @copydoc VBOXHDDBACKEND::pfnOpen */5719 static int vmdksOpen(const char *pszFilename, unsigned uOpenFlags,5720 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,5721 void **ppBackendData)5722 {5723 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));5724 int rc;5725 5726 rc = VERR_NOT_SUPPORTED;5727 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));5728 return rc;5729 }5730 5731 /** @copydoc VBOXHDDBACKEND::pfnCreate */5732 static int vmdksCreate(const char *pszFilename, uint64_t cbSize,5733 unsigned uImageFlags, const char *pszComment,5734 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,5735 PCRTUUID pUuid, unsigned uOpenFlags,5736 unsigned uPercentStart, unsigned uPercentSpan,5737 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,5738 PVDINTERFACE pVDIfsOperation, void **ppBackendData)5739 {5740 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p", pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));5741 int rc;5742 PVMDKIMAGE pImage;5743 5744 PFNVDPROGRESS pfnProgress = NULL;5745 void *pvUser = NULL;5746 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,5747 VDINTERFACETYPE_PROGRESS);5748 PVDINTERFACEPROGRESS pCbProgress = NULL;5749 if (pIfProgress)5750 {5751 pCbProgress = VDGetInterfaceProgress(pIfProgress);5752 pfnProgress = pCbProgress->pfnProgress;5753 pvUser = pIfProgress->pvUser;5754 }5755 5756 /* Check open flags. No flags are supported. */5757 if (uOpenFlags != VD_OPEN_FLAGS_NORMAL)5758 {5759 rc = VERR_INVALID_PARAMETER;5760 goto out;5761 }5762 5763 /* Check image flags. No flags are supported. */5764 if (uImageFlags != VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)5765 {5766 rc = VERR_INVALID_PARAMETER;5767 goto out;5768 }5769 5770 /* Check size. Maximum 2TB-64K. */5771 if ( !cbSize5772 || cbSize >= _1T * 2 - _64K)5773 {5774 rc = VERR_VD_INVALID_SIZE;5775 goto out;5776 }5777 5778 /* Check remaining arguments. */5779 if ( !VALID_PTR(pszFilename)5780 || !*pszFilename5781 || strchr(pszFilename, '"')5782 || !VALID_PTR(pPCHSGeometry)5783 || !VALID_PTR(pLCHSGeometry))5784 {5785 rc = VERR_INVALID_PARAMETER;5786 goto out;5787 }5788 5789 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));5790 if (!pImage)5791 {5792 rc = VERR_NO_MEMORY;5793 goto out;5794 }5795 pImage->pszFilename = pszFilename;5796 pImage->pFile = NULL;5797 pImage->pExtents = NULL;5798 pImage->pFiles = NULL;5799 pImage->pGTCache = NULL;5800 pImage->pDescData = NULL;5801 pImage->pVDIfsDisk = pVDIfsDisk;5802 pImage->pVDIfsImage = pVDIfsImage;5803 /* Descriptors for stream optimized images are small, so don't waste5804 * space in the resulting image and allocate a small buffer. */5805 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(4);5806 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);5807 if (!pImage->pDescData)5808 {5809 rc = VERR_NO_MEMORY;5810 goto out;5811 }5812 5813 rc = vmdksCreateImage(pImage, cbSize, uImageFlags, pszComment,5814 pPCHSGeometry, pLCHSGeometry, pUuid,5815 pfnProgress, pvUser, uPercentStart, uPercentSpan);5816 if (RT_SUCCESS(rc))5817 {5818 /* Image is always writable. */5819 *ppBackendData = pImage;5820 }5821 else5822 {5823 RTMemFree(pImage->pDescData);5824 RTMemFree(pImage);5825 }5826 5827 out:5828 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));5829 return rc;5830 }5831 5832 /** @copydoc VBOXHDDBACKEND::pfnClose */5833 static int vmdksClose(void *pBackendData, bool fDelete)5834 {5835 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));5836 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;5837 int rc;5838 5839 rc = vmdksFreeImage(pImage, fDelete);5840 RTMemFree(pImage);5841 5842 LogFlowFunc(("returns %Rrc\n", rc));5843 return rc;5844 }5845 5846 /** @copydoc VBOXHDDBACKEND::pfnRead */5847 static int vmdksRead(void *pBackendData, uint64_t uOffset, void *pvBuf,5848 size_t cbToRead, size_t *pcbActuallyRead)5849 {5850 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));5851 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;5852 int rc;5853 5854 rc = VERR_NOT_SUPPORTED;5855 LogFlowFunc(("returns %Rrc\n", rc));5856 return rc;5857 }5858 5859 /** @copydoc VBOXHDDBACKEND::pfnWrite */5860 static int vmdksWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,5861 size_t cbToWrite, size_t *pcbWriteProcess,5862 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)5863 {5864 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));5865 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;5866 PVMDKEXTENT pExtent;5867 uint64_t uSector;5868 uint32_t uGrain;5869 uint32_t uGDEntry, uLastGDEntry;5870 uint32_t cbGrain = 0;5871 uint32_t uCacheLine, uCacheEntry;5872 const void *pData = pvBuf;5873 int rc;5874 5875 AssertPtr(pImage);5876 Assert(uOffset % 512 == 0);5877 Assert(cbToWrite % 512 == 0);5878 5879 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)5880 {5881 rc = VERR_VD_IMAGE_READ_ONLY;5882 goto out;5883 }5884 5885 pExtent = &pImage->pExtents[0];5886 uSector = VMDK_BYTE2SECTOR(uOffset);5887 5888 /* Very strict requirements: always write at least one full grain, with5889 * proper alignment. Everything else would require reading of already5890 * written data, which we don't support for obvious reasons. The only5891 * exception is the last grain, and only if the image size specifies5892 * that only some portion holds data. In any case the write must be5893 * within the image limits, no "overshoot" allowed. */5894 if ( cbToWrite == 05895 || ( cbToWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)5896 && pImage->cbSize - uOffset >= VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))5897 || uOffset % VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)5898 || uOffset + cbToWrite > pImage->cbSize)5899 {5900 rc = VERR_INVALID_PARAMETER;5901 goto out;5902 }5903 5904 /* Clip write range to at most the rest of the grain. */5905 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain));5906 5907 /* Do not allow to go back. */5908 uGrain = VMDK_BYTE2SECTOR(uOffset) / pExtent->cSectorsPerGrain;5909 uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;5910 uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE;5911 uGDEntry = uGrain / pExtent->cGTEntries;5912 uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries;5913 if (uGrain < pExtent->uLastGrainWritten)5914 {5915 rc = VERR_VD_VMDK_INVALID_WRITE;5916 goto out;5917 }5918 5919 /* Zero byte write optimization. Since we don't tell VBoxHDD that we need5920 * to allocate something, we also need to detect the situation ourself. */5921 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)5922 && ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)5923 {5924 rc = VINF_SUCCESS;5925 if (pcbWriteProcess)5926 *pcbWriteProcess = cbToWrite;5927 goto out;5928 }5929 5930 if (uGDEntry != uLastGDEntry)5931 {5932 rc = vmdksFlushGT(pImage, pExtent, uLastGDEntry);5933 if (RT_FAILURE(rc))5934 goto out;5935 vmdksClearGT(pImage, pExtent);5936 for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++)5937 {5938 rc = vmdksFlushGT(pImage, pExtent, i);5939 if (RT_FAILURE(rc))5940 goto out;5941 }5942 }5943 5944 /* Check access permissions as defined in the extent descriptor.5945 * May sound a bit paradoxical, but we created the image with a5946 * readonly extent since the resulting image is kind of "write once". */5947 if (pExtent->enmAccess != VMDKACCESS_READONLY)5948 {5949 rc = VERR_VD_VMDK_INVALID_STATE;5950 goto out;5951 }5952 5953 uint64_t uFileOffset;5954 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset);5955 if (RT_FAILURE(rc))5956 goto out;5957 /* Align to sector, as the previous write could have been any size. */5958 uFileOffset = RT_ALIGN_64(uFileOffset, 512);5959 5960 /* Paranoia check: extent type, grain table buffer presence and5961 * grain table buffer space. Also grain table entry must be clear. */5962 if ( pExtent->enmType != VMDKETYPE_HOSTED_SPARSE5963 || !pImage->pGTCache5964 || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE5965 || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry])5966 {5967 rc = VERR_INTERNAL_ERROR;5968 goto out;5969 }5970 5971 /* Update grain table entry. */5972 pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset);5973 5974 if (cbToWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))5975 {5976 memcpy(pExtent->pvGrain, pvBuf, cbToWrite);5977 memset((char *)pExtent->pvGrain + cbToWrite, '\0',5978 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite);5979 pData = pExtent->pvGrain;5980 }5981 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,5982 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),5983 uSector, &cbGrain);5984 if (RT_FAILURE(rc))5985 {5986 pExtent->uGrainSector = 0;5987 pExtent->uLastGrainSector = 0;5988 AssertRC(rc);5989 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);5990 }5991 pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(uFileOffset);5992 pExtent->uLastGrainWritten = uGrain;5993 pExtent->cbLastGrainWritten = cbGrain;5994 5995 if (pcbWriteProcess)5996 *pcbWriteProcess = cbToWrite;5997 5998 out:5999 LogFlowFunc(("returns %Rrc\n", rc));6000 return rc;6001 }6002 6003 /** @copydoc VBOXHDDBACKEND::pfnFlush */6004 static int vmdksFlush(void *pBackendData)6005 {6006 LogFlowFunc(("pBackendData=%#p\n", pBackendData));6007 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6008 int rc;6009 6010 AssertPtr(pImage);6011 6012 /* Pure dummy operation, closing takes care of everything. */6013 rc = VINF_SUCCESS;6014 LogFlowFunc(("returns %Rrc\n", rc));6015 return rc;6016 }6017 6018 /** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */6019 static int vmdksSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)6020 {6021 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));6022 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6023 int rc;6024 6025 AssertPtr(pImage);6026 6027 if (pImage)6028 {6029 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6030 rc = VERR_VD_IMAGE_READ_ONLY;6031 else6032 rc = VERR_NOT_SUPPORTED;6033 }6034 else6035 rc = VERR_VD_NOT_OPENED;6036 6037 LogFlowFunc(("returns %Rrc\n", rc));6038 return rc;6039 }6040 6041 /** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */6042 static int vmdksSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)6043 {6044 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));6045 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6046 int rc;6047 6048 AssertPtr(pImage);6049 6050 if (pImage)6051 {6052 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6053 rc = VERR_VD_IMAGE_READ_ONLY;6054 else6055 rc = VERR_NOT_SUPPORTED;6056 }6057 else6058 rc = VERR_VD_NOT_OPENED;6059 6060 LogFlowFunc(("returns %Rrc\n", rc));6061 return rc;6062 }6063 6064 /** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */6065 static int vmdksSetOpenFlags(void *pBackendData, unsigned uOpenFlags)6066 {6067 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));6068 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6069 int rc = VINF_SUCCESS;6070 6071 /* Image must be opened and the new flags must be the same as before. */6072 if (!pImage || pImage->uOpenFlags != uOpenFlags)6073 {6074 rc = VERR_INVALID_PARAMETER;6075 goto out;6076 }6077 6078 out:6079 LogFlowFunc(("returns %Rrc\n", rc));6080 return rc;6081 }6082 6083 /** @copydoc VBOXHDDBACKEND::pfnSetComment */6084 static int vmdksSetComment(void *pBackendData, const char *pszComment)6085 {6086 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));6087 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6088 int rc;6089 6090 AssertPtr(pImage);6091 6092 if (pImage)6093 {6094 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6095 rc = VERR_VD_IMAGE_READ_ONLY;6096 else6097 rc = VERR_NOT_SUPPORTED;6098 }6099 else6100 rc = VERR_VD_NOT_OPENED;6101 6102 LogFlowFunc(("returns %Rrc\n", rc));6103 return rc;6104 }6105 6106 /** @copydoc VBOXHDDBACKEND::pfnSetUuid */6107 static int vmdksSetUuid(void *pBackendData, PCRTUUID pUuid)6108 {6109 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));6110 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6111 int rc;6112 6113 LogFlowFunc(("%RTuuid\n", pUuid));6114 AssertPtr(pImage);6115 6116 if (pImage)6117 {6118 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6119 rc = VERR_VD_IMAGE_READ_ONLY;6120 else6121 rc = VERR_NOT_SUPPORTED;6122 }6123 else6124 rc = VERR_VD_NOT_OPENED;6125 6126 LogFlowFunc(("returns %Rrc\n", rc));6127 return rc;6128 }6129 6130 /** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */6131 static int vmdksSetModificationUuid(void *pBackendData, PCRTUUID pUuid)6132 {6133 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));6134 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6135 int rc;6136 6137 AssertPtr(pImage);6138 6139 if (pImage)6140 {6141 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6142 rc = VERR_VD_IMAGE_READ_ONLY;6143 else6144 rc = VERR_NOT_SUPPORTED;6145 }6146 else6147 rc = VERR_VD_NOT_OPENED;6148 6149 LogFlowFunc(("returns %Rrc\n", rc));6150 return rc;6151 }6152 6153 /** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */6154 static int vmdksSetParentUuid(void *pBackendData, PCRTUUID pUuid)6155 {6156 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));6157 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6158 int rc;6159 6160 AssertPtr(pImage);6161 6162 if (pImage)6163 {6164 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6165 rc = VERR_VD_IMAGE_READ_ONLY;6166 else6167 rc = VERR_NOT_SUPPORTED;6168 }6169 else6170 rc = VERR_VD_NOT_OPENED;6171 6172 LogFlowFunc(("returns %Rrc\n", rc));6173 return rc;6174 }6175 6176 /** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */6177 static int vmdksSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)6178 {6179 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));6180 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;6181 int rc;6182 6183 AssertPtr(pImage);6184 6185 if (pImage)6186 {6187 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)6188 rc = VERR_VD_IMAGE_READ_ONLY;6189 else6190 rc = VERR_NOT_SUPPORTED;6191 }6192 else6193 rc = VERR_VD_NOT_OPENED;6194 6195 LogFlowFunc(("returns %Rrc\n", rc));6196 return rc;6197 5503 } 6198 5504 … … 6263 5569 goto out; 6264 5570 } 6265 6266 5571 6267 5572 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE)); … … 6697 6002 goto out; 6698 6003 } 6699 6700 6004 6701 6005 /* Clip read range to remain in this extent. */ … … 6810 6114 6811 6115 /* Check access permissions as defined in the extent descriptor. */ 6812 if (pExtent->enmAccess != VMDKACCESS_READWRITE) 6116 if ( pExtent->enmAccess != VMDKACCESS_READWRITE 6117 && ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6118 && !pImage->pExtents[0].uAppendPosition 6119 && pExtent->enmAccess != VMDKACCESS_READONLY)) 6813 6120 { 6814 6121 rc = VERR_VD_VMDK_INVALID_STATE; … … 6837 6144 if (uSectorExtentAbs == 0) 6838 6145 { 6839 if ( cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))6146 if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 6840 6147 { 6841 /* Full block write to a previously unallocated block. 6842 * Check if the caller wants to avoid the automatic alloc. */ 6843 if (!(fWrite & VD_WRITE_NO_ALLOC)) 6148 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 6844 6149 { 6845 /* Allocate GT and find out where to store the grain. */ 6846 rc = vmdkAllocGrain(pImage, pExtent, uSectorExtentRel, 6847 pvBuf, cbToWrite); 6150 /* Full block write to a previously unallocated block. 6151 * Check if the caller wants feedback. */ 6152 if (!(fWrite & VD_WRITE_NO_ALLOC)) 6153 { 6154 /* Allocate GT and store the grain. */ 6155 rc = vmdkAllocGrain(pImage, pExtent, 6156 uSectorExtentRel, 6157 pvBuf, cbToWrite); 6158 } 6159 else 6160 rc = VERR_VD_BLOCK_FREE; 6161 *pcbPreRead = 0; 6162 *pcbPostRead = 0; 6848 6163 } 6849 6164 else 6165 { 6166 /* Clip write range to remain in this extent. */ 6167 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 6168 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain); 6169 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead; 6850 6170 rc = VERR_VD_BLOCK_FREE; 6851 *pcbPreRead = 0; 6852 *pcbPostRead = 0; 6171 } 6853 6172 } 6854 6173 else 6855 6174 { 6856 /* Clip write range to remain in this extent. */ 6857 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 6858 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain); 6859 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead; 6860 rc = VERR_VD_BLOCK_FREE; 6175 rc = vmdkStreamAllocGrain(pImage, pExtent, 6176 uSectorExtentRel, 6177 pvBuf, cbToWrite); 6861 6178 } 6862 6179 } … … 6865 6182 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6866 6183 { 6867 uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain; 6868 uSectorExtentAbs -= uSectorInGrain; 6869 uint64_t uLBA = uSectorExtentRel; 6870 if ( pExtent->uGrainSector != uSectorExtentAbs 6871 || pExtent->uGrainSector != pExtent->uLastGrainSector) 6872 { 6873 rc = vmdkFileInflateSync(pImage, pExtent, 6874 VMDK_SECTOR2BYTE(uSectorExtentAbs), 6875 pExtent->pvGrain, 6876 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), 6877 &uLBA, NULL); 6878 if (RT_FAILURE(rc)) 6879 { 6880 pExtent->uGrainSector = 0; 6881 pExtent->uLastGrainSector = 0; 6882 AssertRC(rc); 6883 goto out; 6884 } 6885 pExtent->uGrainSector = uSectorExtentAbs; 6886 pExtent->uLastGrainSector = uSectorExtentAbs; 6887 Assert(uLBA == uSectorExtentRel); 6888 } 6889 memcpy((uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), pvBuf, cbToWrite); 6890 uint32_t cbGrain = 0; 6891 rc = vmdkFileDeflateSync(pImage, pExtent, 6892 VMDK_SECTOR2BYTE(uSectorExtentAbs), 6893 pExtent->pvGrain, 6894 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), 6895 uLBA, &cbGrain); 6896 if (RT_FAILURE(rc)) 6897 { 6898 pExtent->uGrainSector = 0; 6899 pExtent->uLastGrainSector = 0; 6900 AssertRC(rc); 6901 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname); 6902 } 6903 pExtent->uLastGrainSector = uSectorExtentAbs; 6904 pExtent->uLastGrainWritten = uSectorExtentRel / pExtent->cSectorsPerGrain; 6905 pExtent->cbLastGrainWritten = cbGrain; 6906 6907 uint64_t uEOSOff = 0; 6908 if (pExtent->fFooter) 6909 { 6910 uEOSOff = 512; 6911 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, VMDK_SECTOR2BYTE(uSectorExtentAbs) + RT_ALIGN(cbGrain, 512)); 6912 if (RT_FAILURE(rc)) 6913 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write footer after data block in '%s'"), pExtent->pszFullname); 6914 } 6915 uint8_t aEOS[512]; 6916 memset(aEOS, '\0', sizeof(aEOS)); 6917 rc = vmdkFileWriteSync(pImage, pExtent->pFile, 6918 VMDK_SECTOR2BYTE(uSectorExtentAbs) + RT_ALIGN(cbGrain, 512) + uEOSOff, 6919 aEOS, sizeof(aEOS), NULL); 6920 if (RT_FAILURE(rc)) 6921 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write end-of stream marker after data block in '%s'"), pExtent->pszFullname); 6184 /* A partial write to a streamOptimized image is simply 6185 * invalid. It requires rewriting already compressed data 6186 * which is somewhere between expensive and impossible. */ 6187 rc = VERR_VD_VMDK_INVALID_STATE; 6188 pExtent->uGrainSector = 0; 6189 pExtent->uLastGrainSector = 0; 6190 AssertRC(rc); 6922 6191 } 6923 6192 else … … 6956 6225 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 6957 6226 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6958 int rc ;6227 int rc = VINF_SUCCESS; 6959 6228 6960 6229 AssertPtr(pImage); 6961 6230 6962 rc = vmdkFlushImage(pImage); 6231 if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 6232 rc = vmdkFlushImage(pImage); 6233 6963 6234 LogFlowFunc(("returns %Rrc\n", rc)); 6964 6235 return rc; … … 7068 6339 goto out; 7069 6340 } 6341 if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6342 { 6343 rc = VERR_NOT_SUPPORTED; 6344 goto out; 6345 } 7070 6346 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry); 7071 6347 if (RT_FAILURE(rc)) … … 7123 6399 { 7124 6400 rc = VERR_VD_IMAGE_READ_ONLY; 6401 goto out; 6402 } 6403 if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6404 { 6405 rc = VERR_NOT_SUPPORTED; 7125 6406 goto out; 7126 6407 } … … 7188 6469 { 7189 6470 rc = VERR_INVALID_PARAMETER; 6471 goto out; 6472 } 6473 6474 /* StreamOptimized images need special treatment: reopen is prohibited. */ 6475 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6476 { 6477 if (pImage->uOpenFlags == uOpenFlags) 6478 rc = VINF_SUCCESS; 6479 else 6480 rc = VERR_INVALID_PARAMETER; 7190 6481 goto out; 7191 6482 } … … 7253 6544 goto out; 7254 6545 } 6546 if (pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 6547 { 6548 rc = VERR_NOT_SUPPORTED; 6549 goto out; 6550 } 7255 6551 7256 6552 if (pImage) … … 7299 6595 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 7300 6596 { 7301 pImage->ImageUuid = *pUuid; 7302 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 7303 VMDK_DDB_IMAGE_UUID, pUuid); 7304 if (RT_FAILURE(rc)) 7305 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename); 7306 rc = VINF_SUCCESS; 6597 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 6598 { 6599 pImage->ImageUuid = *pUuid; 6600 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 6601 VMDK_DDB_IMAGE_UUID, pUuid); 6602 if (RT_FAILURE(rc)) 6603 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename); 6604 rc = VINF_SUCCESS; 6605 } 6606 else 6607 rc = VERR_NOT_SUPPORTED; 7307 6608 } 7308 6609 else … … 7350 6651 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 7351 6652 { 7352 /* 7353 * Only change the modification uuid if it changed. 7354 * Avoids a lot of unneccessary 1-byte writes during 7355 * vmdkFlush. 7356 */ 7357 if (RTUuidCompare(&pImage->ModificationUuid, pUuid)) 6653 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 7358 6654 { 7359 pImage->ModificationUuid = *pUuid; 7360 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 7361 VMDK_DDB_MODIFICATION_UUID, pUuid); 7362 if (RT_FAILURE(rc)) 7363 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename); 6655 /* Only touch the modification uuid if it changed. */ 6656 if (RTUuidCompare(&pImage->ModificationUuid, pUuid)) 6657 { 6658 pImage->ModificationUuid = *pUuid; 6659 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 6660 VMDK_DDB_MODIFICATION_UUID, pUuid); 6661 if (RT_FAILURE(rc)) 6662 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename); 6663 } 6664 rc = VINF_SUCCESS; 7364 6665 } 7365 rc = VINF_SUCCESS; 6666 else 6667 rc = VERR_NOT_SUPPORTED; 7366 6668 } 7367 6669 else … … 7409 6711 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 7410 6712 { 7411 pImage->ParentUuid = *pUuid; 7412 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 7413 VMDK_DDB_PARENT_UUID, pUuid); 7414 if (RT_FAILURE(rc)) 7415 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 7416 rc = VINF_SUCCESS; 6713 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 6714 { 6715 pImage->ParentUuid = *pUuid; 6716 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 6717 VMDK_DDB_PARENT_UUID, pUuid); 6718 if (RT_FAILURE(rc)) 6719 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 6720 rc = VINF_SUCCESS; 6721 } 6722 else 6723 rc = VERR_NOT_SUPPORTED; 7417 6724 } 7418 6725 else … … 7460 6767 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 7461 6768 { 7462 pImage->ParentModificationUuid = *pUuid; 7463 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 7464 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid); 7465 if (RT_FAILURE(rc)) 7466 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 7467 rc = VINF_SUCCESS; 6769 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 6770 { 6771 pImage->ParentModificationUuid = *pUuid; 6772 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 6773 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid); 6774 if (RT_FAILURE(rc)) 6775 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 6776 rc = VINF_SUCCESS; 6777 } 6778 else 6779 rc = VERR_NOT_SUPPORTED; 7468 6780 } 7469 6781 else … … 7737 7049 if (pExtent->fFooter) 7738 7050 { 7739 uint64_t cbSize; 7740 rc = vmdkFileGetSize(pImage, pExtent->pFile, &cbSize); 7741 if (RT_FAILURE(rc)) 7051 uint64_t uFileOffset = pExtent->uAppendPosition; 7052 if (!uFileOffset) 7053 { 7054 rc = VERR_INTERNAL_ERROR; 7742 7055 goto out; 7743 cbSize = RT_ALIGN_64(cbSize, 512); 7744 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, cbSize - 2*512); 7056 } 7057 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 7058 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset); 7745 7059 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)) 7746 7060 goto out; … … 7789 7103 return rc; 7790 7104 } 7791 7792 7793 VBOXHDDBACKEND g_VmdkStreamBackend =7794 {7795 /* pszBackendName */7796 "VMDKstream",7797 /* cbSize */7798 sizeof(VBOXHDDBACKEND),7799 /* uBackendCaps */7800 VD_CAP_UUID | VD_CAP_CREATE_DYNAMIC | VD_CAP_FILE | VD_CAP_VFS,7801 /* papszFileExtensions */7802 s_apszVmdkFileExtensions,7803 /* paConfigInfo */7804 NULL,7805 /* hPlugin */7806 NIL_RTLDRMOD,7807 /* pfnCheckIfValid */7808 vmdksCheckIfValid,7809 /* pfnOpen */7810 vmdksOpen,7811 /* pfnCreate */7812 vmdksCreate,7813 /* pfnRename */7814 NULL,7815 /* pfnClose */7816 vmdksClose,7817 /* pfnRead */7818 vmdksRead,7819 /* pfnWrite */7820 vmdksWrite,7821 /* pfnFlush */7822 vmdksFlush,7823 /* pfnGetVersion */7824 vmdkGetVersion,7825 /* pfnGetSize */7826 vmdkGetSize,7827 /* pfnGetFileSize */7828 vmdkGetFileSize,7829 /* pfnGetPCHSGeometry */7830 vmdkGetPCHSGeometry,7831 /* pfnSetPCHSGeometry */7832 vmdksSetPCHSGeometry,7833 /* pfnGetLCHSGeometry */7834 vmdkGetLCHSGeometry,7835 /* pfnSetLCHSGeometry */7836 vmdksSetLCHSGeometry,7837 /* pfnGetImageFlags */7838 vmdkGetImageFlags,7839 /* pfnGetOpenFlags */7840 vmdkGetOpenFlags,7841 /* pfnSetOpenFlags */7842 vmdksSetOpenFlags,7843 /* pfnGetComment */7844 vmdkGetComment,7845 /* pfnSetComment */7846 vmdksSetComment,7847 /* pfnGetUuid */7848 vmdkGetUuid,7849 /* pfnSetUuid */7850 vmdksSetUuid,7851 /* pfnGetModificationUuid */7852 vmdkGetModificationUuid,7853 /* pfnSetModificationUuid */7854 vmdksSetModificationUuid,7855 /* pfnGetParentUuid */7856 vmdkGetParentUuid,7857 /* pfnSetParentUuid */7858 vmdksSetParentUuid,7859 /* pfnGetParentModificationUuid */7860 vmdkGetParentModificationUuid,7861 /* pfnSetParentModificationUuid */7862 vmdksSetParentModificationUuid,7863 /* pfnDump */7864 vmdkDump,7865 /* pfnGetTimeStamp */7866 NULL,7867 /* pfnGetParentTimeStamp */7868 NULL,7869 /* pfnSetParentTimeStamp */7870 NULL,7871 /* pfnGetParentFilename */7872 NULL,7873 /* pfnSetParentFilename */7874 NULL,7875 /* pfnIsAsyncIOSupported */7876 NULL,7877 /* pfnAsyncRead */7878 NULL,7879 /* pfnAsyncWrite */7880 NULL,7881 /* pfnAsyncFlush */7882 NULL,7883 /* pfnComposeLocation */7884 genericFileComposeLocation,7885 /* pfnComposeName */7886 genericFileComposeName,7887 /* pfnCompact */7888 NULL,7889 /* pfnResize */7890 NULL7891 };7892 7105 7893 7106
Note:
See TracChangeset
for help on using the changeset viewer.