Changeset 32573 in vbox for trunk/src/VBox/Devices
- Timestamp:
- Sep 16, 2010 5:18:03 PM (14 years ago)
- Location:
- trunk/src/VBox/Devices/Storage
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/VBoxHDD.cpp
r32553 r32573 418 418 extern VBOXHDDBACKEND g_RawBackend; 419 419 extern VBOXHDDBACKEND g_VmdkBackend; 420 extern VBOXHDDBACKEND g_VmdkStreamBackend; 420 421 extern VBOXHDDBACKEND g_VDIBackend; 421 422 extern VBOXHDDBACKEND g_VhdBackend; … … 434 435 &g_RawBackend, 435 436 &g_VmdkBackend, 437 &g_VmdkStreamBackend, 436 438 &g_VDIBackend, 437 439 &g_VhdBackend, -
trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
r32553 r32573 1206 1206 } 1207 1207 pExtent->pGD = pGD; 1208 /* The VMDK 1.1 spec talks about compressed grain directories, but real1209 * life files don't have them. The spec is wrong in creative ways. */1208 /* The VMDK 1.1 spec seems to talk about compressed grain directories, 1209 * but in reality they are not compressed. */ 1210 1210 rc = vmdkFileReadSync(pImage, pExtent->pFile, 1211 1211 VMDK_SECTOR2BYTE(pExtent->uSectorGD), … … 1229 1229 } 1230 1230 pExtent->pRGD = pRGD; 1231 /* The VMDK 1.1 spec talks about compressed grain directories, but real1232 * life files don't have them. The spec is wrong in creative ways. */1231 /* The VMDK 1.1 spec seems to talk about compressed grain directories, 1232 * but in reality they are not compressed. */ 1233 1233 rc = vmdkFileReadSync(pImage, pExtent->pFile, 1234 1234 VMDK_SECTOR2BYTE(pExtent->uSectorRGD), … … 1277 1277 goto out; 1278 1278 } 1279 /* The VMDK 1.1 spec talks about compressed grain tables, but real1280 * life files don't have them. The spec is wrong in creative ways. */1279 /* The VMDK 1.1 spec seems to talk about compressed grain tables, 1280 * but in reality they are not compressed. */ 1281 1281 rc = vmdkFileReadSync(pImage, pExtent->pFile, 1282 1282 VMDK_SECTOR2BYTE(*pGDTmp), … … 1289 1289 goto out; 1290 1290 } 1291 /* The VMDK 1.1 spec talks about compressed grain tables, but real1292 * life files don't have them. The spec is wrong in creative ways. */1291 /* The VMDK 1.1 spec seems to talk about compressed grain tables, 1292 * but in reality they are not compressed. */ 1293 1293 rc = vmdkFileReadSync(pImage, pExtent->pFile, 1294 1294 VMDK_SECTOR2BYTE(*pRGDTmp), … … 1332 1332 continue; 1333 1333 1334 /* The VMDK 1.1 spec talks about compressed grain tables, but real1335 * life files don't have them. The spec is wrong in creative ways. */1334 /* The VMDK 1.1 spec seems to talk about compressed grain tables, 1335 * but in reality they are not compressed. */ 1336 1336 rc = vmdkFileReadSync(pImage, pExtent->pFile, 1337 1337 VMDK_SECTOR2BYTE(*pGDTmp), … … 2868 2868 goto out; 2869 2869 } 2870 /* disabled the size check again as there are too many too short vmdks out there */2870 /* disabled the check as there are too many truncated vmdk images out there */ 2871 2871 #ifdef VBOX_WITH_VMDK_STRICT_SIZE_CHECK 2872 2872 if ( cbExtentSize != RT_ALIGN_64(cbExtentSize, 512) … … 4046 4046 if (RT_FAILURE(rc)) 4047 4047 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename); 4048 return rc; 4049 } 4050 4051 /** 4052 * Internal. Clear the grain table buffer for real stream optimized writing. 4053 */ 4054 static void vmdksClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent) 4055 { 4056 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE; 4057 for (uint32_t i = 0; i < cCacheLines; i++) 4058 memset(&pImage->pGTCache->aGTCache[i].aGTData[0], '\0', 4059 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t)); 4060 } 4061 4062 /** 4063 * Internal. Flush the grain table buffer for real stream optimized writing. 4064 */ 4065 static int vmdksFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, 4066 uint32_t uGDEntry) 4067 { 4068 int rc = VINF_SUCCESS; 4069 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE; 4070 4071 uint64_t uFileOffset; 4072 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset); 4073 AssertRC(rc); 4074 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 4075 4076 /* Grain table marker. */ 4077 /** @todo check me! */ 4078 uint8_t aMarker[512]; 4079 memset(aMarker, '\0', sizeof(aMarker)); 4080 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0]; 4081 pMarker->cbSize = RT_H2LE_U32(pExtent->cGTEntries * sizeof(uint32_t)); 4082 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT); 4083 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4084 aMarker, sizeof(aMarker), NULL); 4085 AssertRC(rc); 4086 uFileOffset += 512; 4087 4088 if (!pExtent->pGD || pExtent->pGD[uGDEntry]) 4089 return VERR_INTERNAL_ERROR; 4090 4091 pExtent->pGD[uGDEntry] = VMDK_BYTE2SECTOR(uFileOffset); 4092 4093 for (uint32_t i = 0; i < cCacheLines; i++) 4094 { 4095 /* Convert the grain table to little endian in place, as it will not 4096 * be used at all after this function has been called. */ 4097 uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0]; 4098 for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++) 4099 *pGTTmp = RT_H2LE_U32(*pGTTmp); 4100 4101 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4102 &pImage->pGTCache->aGTCache[i].aGTData[0], 4103 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t), 4104 NULL); 4105 } 4106 return rc; 4107 } 4108 4109 /** 4110 * Internal. Free all allocated space for representing a real stream optimized 4111 * image, and optionally delete the image from disk. 4112 */ 4113 static int vmdksFreeImage(PVMDKIMAGE pImage, bool fDelete) 4114 { 4115 int rc = VINF_SUCCESS; 4116 4117 /* Freeing a never allocated image (e.g. because the open failed) is 4118 * not signalled as an error. After all nothing bad happens. */ 4119 if (pImage) 4120 { 4121 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 4122 { 4123 /* Check if all extents are clean. */ 4124 for (unsigned i = 0; i < pImage->cExtents; i++) 4125 { 4126 Assert(!pImage->pExtents[i].fUncleanShutdown); 4127 } 4128 } 4129 4130 /* No need to write any pending data if the file will be deleted. */ 4131 if (!fDelete && pImage->pExtents) 4132 { 4133 PVMDKEXTENT pExtent = &pImage->pExtents[0]; 4134 uint32_t uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries; 4135 if (uLastGDEntry != pExtent->cGDEntries) 4136 { 4137 rc = vmdksFlushGT(pImage, pExtent, uLastGDEntry); 4138 AssertRC(rc); 4139 vmdksClearGT(pImage, pExtent); 4140 for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++) 4141 { 4142 rc = vmdksFlushGT(pImage, pExtent, i); 4143 AssertRC(rc); 4144 } 4145 } 4146 4147 uint64_t uFileOffset; 4148 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset); 4149 AssertRC(rc); 4150 uFileOffset = RT_ALIGN_64(uFileOffset, 512); 4151 4152 /* Grain directory marker. */ 4153 /** @todo check me! */ 4154 uint8_t aMarker[512]; 4155 memset(aMarker, '\0', sizeof(aMarker)); 4156 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0]; 4157 pMarker->cbSize = RT_H2LE_U32(pExtent->cGDEntries * sizeof(uint32_t)); 4158 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD); 4159 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4160 aMarker, sizeof(aMarker), NULL); 4161 AssertRC(rc); 4162 uFileOffset += 512; 4163 4164 /* Write grain directory in little endian style. The array will 4165 * not be used after this, so convert in place. */ 4166 uint32_t *pGDTmp = pExtent->pGD; 4167 for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++) 4168 *pGDTmp = RT_H2LE_U32(*pGDTmp); 4169 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4170 pExtent->pGD, 4171 pExtent->cGDEntries * sizeof(uint32_t), 4172 NULL); 4173 AssertRC(rc); 4174 4175 pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset); 4176 pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset); 4177 uFileOffset = RT_ALIGN_64(uFileOffset + pExtent->cGDEntries * sizeof(uint32_t), 512); 4178 4179 /* End of stream marker. */ 4180 memset(aMarker, '\0', sizeof(aMarker)); 4181 /** @todo check me! */ 4182 rc = vmdkFileWriteSync(pImage, pExtent->pFile, uFileOffset, 4183 aMarker, sizeof(aMarker), NULL); 4184 AssertRC(rc); 4185 4186 uFileOffset += 512; 4187 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset); 4188 AssertRC(rc); 4189 } 4190 4191 if (pImage->pExtents != NULL) 4192 { 4193 for (unsigned i = 0 ; i < pImage->cExtents; i++) 4194 vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete); 4195 RTMemFree(pImage->pExtents); 4196 pImage->pExtents = NULL; 4197 } 4198 pImage->cExtents = 0; 4199 if (pImage->pFile != NULL) 4200 vmdkFileClose(pImage, &pImage->pFile, fDelete); 4201 vmdkFileCheckAllClose(pImage); 4202 4203 if (pImage->pGTCache) 4204 { 4205 RTMemFree(pImage->pGTCache); 4206 pImage->pGTCache = NULL; 4207 } 4208 if (pImage->pDescData) 4209 { 4210 RTMemFree(pImage->pDescData); 4211 pImage->pDescData = NULL; 4212 } 4213 } 4214 4215 LogFlowFunc(("returns %Rrc\n", rc)); 4216 return rc; 4217 } 4218 4219 /** 4220 * Internal: Create a real stream optimized VMDK using only linear writes. 4221 */ 4222 static int vmdksCreateImage(PVMDKIMAGE pImage, uint64_t cbSize, 4223 unsigned uImageFlags, const char *pszComment, 4224 PCVDGEOMETRY pPCHSGeometry, 4225 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid, 4226 PFNVDPROGRESS pfnProgress, void *pvUser, 4227 unsigned uPercentStart, unsigned uPercentSpan) 4228 { 4229 int rc; 4230 4231 pImage->uImageFlags = uImageFlags; 4232 4233 /* Try to get error interface. */ 4234 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR); 4235 if (pImage->pInterfaceError) 4236 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError); 4237 4238 /* Get I/O interface. */ 4239 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IO); 4240 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER); 4241 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(pImage->pInterfaceIO); 4242 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER); 4243 4244 PVMDKEXTENT pExtent; 4245 char *pszBasenameSubstr, *pszBasedirectory, *pszBasename; 4246 size_t cbBasenameSubstr; 4247 4248 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc, 4249 &pImage->Descriptor); 4250 if (RT_FAILURE(rc)) 4251 { 4252 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename); 4253 goto out; 4254 } 4255 4256 rc = vmdkCreateExtents(pImage, 1); 4257 if (RT_FAILURE(rc)) 4258 { 4259 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename); 4260 goto out; 4261 } 4262 4263 /* Basename strings needed for constructing the extent names. */ 4264 pszBasenameSubstr = RTPathFilename(pImage->pszFilename); 4265 AssertPtr(pszBasenameSubstr); 4266 cbBasenameSubstr = strlen(pszBasenameSubstr) + 1; 4267 4268 /* No separate descriptor file. */ 4269 pImage->pFile = NULL; 4270 4271 /* Set up all extents. */ 4272 pExtent = &pImage->pExtents[0]; 4273 4274 /* Set up fullname/basename for extent description. Cannot use StrDup 4275 * for basename, as it is not guaranteed that the memory can be freed 4276 * with RTMemTmpFree, which must be used as in other code paths 4277 * StrDup is not usable. */ 4278 pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr); 4279 if (!pszBasename) 4280 { 4281 rc = VERR_NO_MEMORY; 4282 goto out; 4283 } 4284 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr); 4285 pExtent->pszBasename = pszBasename; 4286 4287 pszBasedirectory = RTStrDup(pImage->pszFilename); 4288 RTPathStripFilename(pszBasedirectory); 4289 char *pszFullname; 4290 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszBasedirectory, 4291 RTPATH_SLASH, pExtent->pszBasename); 4292 RTStrFree(pszBasedirectory); 4293 if (RT_FAILURE(rc)) 4294 goto out; 4295 pExtent->pszFullname = pszFullname; 4296 4297 /* Create file for extent. */ 4298 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname, 4299 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, 4300 true /* fCreate */), 4301 false /* fAsyncIO */); 4302 if (RT_FAILURE(rc)) 4303 { 4304 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname); 4305 goto out; 4306 } 4307 4308 /* Place descriptor file information. */ 4309 pExtent->uDescriptorSector = 1; 4310 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc); 4311 /* The descriptor is part of the (only) extent. */ 4312 pExtent->pDescData = pImage->pDescData; 4313 pImage->pDescData = NULL; 4314 4315 uint64_t cSectorsPerGDE, cSectorsPerGD; 4316 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; 4317 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K)); 4318 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K); 4319 pExtent->cGTEntries = 512; 4320 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain; 4321 pExtent->cSectorsPerGDE = cSectorsPerGDE; 4322 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE; 4323 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t)); 4324 4325 /* The spec says version is 1 for all VMDKs, but the vast 4326 * majority of streamOptimized VMDKs actually contain 4327 * version 3 - so go with the majority. Both are acepted. */ 4328 pExtent->uVersion = 3; 4329 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE; 4330 pExtent->fFooter = true; 4331 4332 pExtent->enmAccess = VMDKACCESS_READONLY; 4333 pExtent->fUncleanShutdown = true; 4334 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize); 4335 pExtent->uSectorOffset = 0; 4336 pExtent->fMetaDirty = true; 4337 4338 /* Create grain directory, without preallocating it straight away. It will 4339 * be constructed on the fly when writing out the data and written when 4340 * closing the image. The end effect is that the full grain directory is 4341 * allocated, which is a requirement of the VMDK specs. */ 4342 rc = vmdkCreateGrainDirectory(pImage, pExtent, 4343 RT_MAX( pExtent->uDescriptorSector 4344 + pExtent->cDescriptorSectors, 4345 1), 4346 false /* fPreAlloc */); 4347 if (RT_FAILURE(rc)) 4348 { 4349 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname); 4350 goto out; 4351 } 4352 4353 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", 4354 "streamOptimized"); 4355 if (RT_FAILURE(rc)) 4356 { 4357 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename); 4358 goto out; 4359 } 4360 4361 if (pfnProgress) 4362 pfnProgress(pvUser, uPercentStart + uPercentSpan * 20 / 100); 4363 4364 pImage->cbSize = cbSize; 4365 4366 Assert(pImage->cExtents == 1); 4367 4368 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess, 4369 pExtent->cNominalSectors, pExtent->enmType, 4370 pExtent->pszBasename, pExtent->uSectorOffset); 4371 if (RT_FAILURE(rc)) 4372 { 4373 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename); 4374 goto out; 4375 } 4376 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor); 4377 4378 if ( pPCHSGeometry->cCylinders != 0 4379 && pPCHSGeometry->cHeads != 0 4380 && pPCHSGeometry->cSectors != 0) 4381 { 4382 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry); 4383 if (RT_FAILURE(rc)) 4384 goto out; 4385 } 4386 if ( pLCHSGeometry->cCylinders != 0 4387 && pLCHSGeometry->cHeads != 0 4388 && pLCHSGeometry->cSectors != 0) 4389 { 4390 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry); 4391 if (RT_FAILURE(rc)) 4392 goto out; 4393 } 4394 4395 pImage->LCHSGeometry = *pLCHSGeometry; 4396 pImage->PCHSGeometry = *pPCHSGeometry; 4397 4398 pImage->ImageUuid = *pUuid; 4399 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4400 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid); 4401 if (RT_FAILURE(rc)) 4402 { 4403 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename); 4404 goto out; 4405 } 4406 RTUuidClear(&pImage->ParentUuid); 4407 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4408 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid); 4409 if (RT_FAILURE(rc)) 4410 { 4411 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename); 4412 goto out; 4413 } 4414 RTUuidClear(&pImage->ModificationUuid); 4415 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4416 VMDK_DDB_MODIFICATION_UUID, 4417 &pImage->ModificationUuid); 4418 if (RT_FAILURE(rc)) 4419 { 4420 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename); 4421 goto out; 4422 } 4423 RTUuidClear(&pImage->ParentModificationUuid); 4424 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4425 VMDK_DDB_PARENT_MODIFICATION_UUID, 4426 &pImage->ParentModificationUuid); 4427 if (RT_FAILURE(rc)) 4428 { 4429 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename); 4430 goto out; 4431 } 4432 4433 rc = vmdkAllocateGrainTableCache(pImage); 4434 if (RT_FAILURE(rc)) 4435 goto out; 4436 4437 rc = vmdkSetImageComment(pImage, pszComment); 4438 if (RT_FAILURE(rc)) 4439 { 4440 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename); 4441 goto out; 4442 } 4443 4444 if (pfnProgress) 4445 pfnProgress(pvUser, uPercentStart + uPercentSpan * 50 / 100); 4446 4447 rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0); 4448 if (RT_FAILURE(rc)) 4449 { 4450 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename); 4451 goto out; 4452 } 4453 4454 if (pfnProgress) 4455 pfnProgress(pvUser, uPercentStart + uPercentSpan * 70 / 100); 4456 4457 rc = vmdkWriteDescriptor(pImage); 4458 if (RT_FAILURE(rc)) 4459 { 4460 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename); 4461 goto out; 4462 } 4463 4464 out: 4465 if (RT_SUCCESS(rc) && pfnProgress) 4466 pfnProgress(pvUser, uPercentStart + uPercentSpan); 4467 4468 if (RT_FAILURE(rc)) 4469 vmdksFreeImage(pImage, rc != VERR_ALREADY_EXISTS); 4048 4470 return rc; 4049 4471 } … … 5195 5617 } 5196 5618 return pszNewStr; 5619 } 5620 5621 5622 /** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */ 5623 static int vmdksCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk, 5624 PVDINTERFACE pVDIfsImage) 5625 { 5626 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename)); 5627 int rc = VINF_SUCCESS; 5628 5629 if ( !VALID_PTR(pszFilename) 5630 || !*pszFilename 5631 || strchr(pszFilename, '"')) 5632 { 5633 rc = VERR_INVALID_PARAMETER; 5634 goto out; 5635 } 5636 5637 /* Always return failure, to avoid opening other VMDK files via this 5638 * special VMDK streamOptimized format backend. */ 5639 rc = VERR_NOT_SUPPORTED; 5640 5641 out: 5642 LogFlowFunc(("returns %Rrc\n", rc)); 5643 return rc; 5644 } 5645 5646 5647 /** @copydoc VBOXHDDBACKEND::pfnOpen */ 5648 static int vmdksOpen(const char *pszFilename, unsigned uOpenFlags, 5649 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage, 5650 void **ppBackendData) 5651 { 5652 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData)); 5653 int rc; 5654 5655 rc = VERR_NOT_SUPPORTED; 5656 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData)); 5657 return rc; 5658 } 5659 5660 /** @copydoc VBOXHDDBACKEND::pfnCreate */ 5661 static int vmdksCreate(const char *pszFilename, uint64_t cbSize, 5662 unsigned uImageFlags, const char *pszComment, 5663 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry, 5664 PCRTUUID pUuid, unsigned uOpenFlags, 5665 unsigned uPercentStart, unsigned uPercentSpan, 5666 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage, 5667 PVDINTERFACE pVDIfsOperation, void **ppBackendData) 5668 { 5669 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)); 5670 int rc; 5671 PVMDKIMAGE pImage; 5672 5673 PFNVDPROGRESS pfnProgress = NULL; 5674 void *pvUser = NULL; 5675 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation, 5676 VDINTERFACETYPE_PROGRESS); 5677 PVDINTERFACEPROGRESS pCbProgress = NULL; 5678 if (pIfProgress) 5679 { 5680 pCbProgress = VDGetInterfaceProgress(pIfProgress); 5681 pfnProgress = pCbProgress->pfnProgress; 5682 pvUser = pIfProgress->pvUser; 5683 } 5684 5685 /* Check open flags. No flags are supported. */ 5686 if (uOpenFlags != VD_OPEN_FLAGS_NORMAL) 5687 { 5688 rc = VERR_INVALID_PARAMETER; 5689 goto out; 5690 } 5691 5692 /* Check image flags. No flags are supported. */ 5693 if (uImageFlags != VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED) 5694 { 5695 rc = VERR_INVALID_PARAMETER; 5696 goto out; 5697 } 5698 5699 /* Check size. Maximum 2TB-64K. */ 5700 if ( !cbSize 5701 || cbSize >= _1T * 2 - _64K) 5702 { 5703 rc = VERR_VD_INVALID_SIZE; 5704 goto out; 5705 } 5706 5707 /* Check remaining arguments. */ 5708 if ( !VALID_PTR(pszFilename) 5709 || !*pszFilename 5710 || strchr(pszFilename, '"') 5711 || !VALID_PTR(pPCHSGeometry) 5712 || !VALID_PTR(pLCHSGeometry)) 5713 { 5714 rc = VERR_INVALID_PARAMETER; 5715 goto out; 5716 } 5717 5718 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE)); 5719 if (!pImage) 5720 { 5721 rc = VERR_NO_MEMORY; 5722 goto out; 5723 } 5724 pImage->pszFilename = pszFilename; 5725 pImage->pFile = NULL; 5726 pImage->pExtents = NULL; 5727 pImage->pFiles = NULL; 5728 pImage->pGTCache = NULL; 5729 pImage->pDescData = NULL; 5730 pImage->pVDIfsDisk = pVDIfsDisk; 5731 pImage->pVDIfsImage = pVDIfsImage; 5732 /* Descriptors for stream optimized images are small, so don't waste 5733 * space in the resulting image and allocate a small buffer. */ 5734 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(4); 5735 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc); 5736 if (!pImage->pDescData) 5737 { 5738 rc = VERR_NO_MEMORY; 5739 goto out; 5740 } 5741 5742 rc = vmdksCreateImage(pImage, cbSize, uImageFlags, pszComment, 5743 pPCHSGeometry, pLCHSGeometry, pUuid, 5744 pfnProgress, pvUser, uPercentStart, uPercentSpan); 5745 if (RT_SUCCESS(rc)) 5746 { 5747 /* Image is always writable. */ 5748 *ppBackendData = pImage; 5749 } 5750 else 5751 { 5752 RTMemFree(pImage->pDescData); 5753 RTMemFree(pImage); 5754 } 5755 5756 out: 5757 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData)); 5758 return rc; 5759 } 5760 5761 /** @copydoc VBOXHDDBACKEND::pfnClose */ 5762 static int vmdksClose(void *pBackendData, bool fDelete) 5763 { 5764 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete)); 5765 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5766 int rc; 5767 5768 rc = vmdksFreeImage(pImage, fDelete); 5769 RTMemFree(pImage); 5770 5771 LogFlowFunc(("returns %Rrc\n", rc)); 5772 return rc; 5773 } 5774 5775 /** @copydoc VBOXHDDBACKEND::pfnRead */ 5776 static int vmdksRead(void *pBackendData, uint64_t uOffset, void *pvBuf, 5777 size_t cbToRead, size_t *pcbActuallyRead) 5778 { 5779 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead)); 5780 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5781 int rc; 5782 5783 rc = VERR_NOT_SUPPORTED; 5784 LogFlowFunc(("returns %Rrc\n", rc)); 5785 return rc; 5786 } 5787 5788 /** @copydoc VBOXHDDBACKEND::pfnWrite */ 5789 static int vmdksWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, 5790 size_t cbToWrite, size_t *pcbWriteProcess, 5791 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite) 5792 { 5793 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead)); 5794 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5795 PVMDKEXTENT pExtent; 5796 uint64_t uSector; 5797 uint32_t uGrain; 5798 uint32_t uGDEntry, uLastGDEntry; 5799 uint32_t cbGrain = 0; 5800 uint32_t uCacheLine, uCacheEntry; 5801 const void *pData = pvBuf; 5802 int rc; 5803 5804 AssertPtr(pImage); 5805 Assert(uOffset % 512 == 0); 5806 Assert(cbToWrite % 512 == 0); 5807 5808 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 5809 { 5810 rc = VERR_VD_IMAGE_READ_ONLY; 5811 goto out; 5812 } 5813 5814 pExtent = &pImage->pExtents[0]; 5815 uSector = VMDK_BYTE2SECTOR(uOffset); 5816 5817 /* Very strict requirements: always write at least one full grain, with 5818 * proper alignment. Everything else would require reading of already 5819 * written data, which we don't support for obvious reasons. The only 5820 * exception is the last grain, and only if the image size specifies 5821 * that only some portion holds data. In any case the write must be 5822 * within the image limits, no "overshoot" allowed. */ 5823 if ( cbToWrite == 0 5824 || ( cbToWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) 5825 && pImage->cbSize - uOffset >= VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 5826 || uOffset % VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) 5827 || uOffset + cbToWrite > pImage->cbSize) 5828 { 5829 rc = VERR_INVALID_PARAMETER; 5830 goto out; 5831 } 5832 5833 /* Clip write range to at most the rest of the grain. */ 5834 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain)); 5835 5836 /* Do not allow to go back. */ 5837 uGrain = VMDK_BYTE2SECTOR(uOffset) / pExtent->cSectorsPerGrain; 5838 uCacheLine = uGrain / VMDK_GT_CACHELINE_SIZE % VMDK_GT_CACHE_SIZE; 5839 uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE; 5840 uGDEntry = uGrain / pExtent->cGTEntries; 5841 uLastGDEntry = pExtent->uLastGrainWritten / pExtent->cGTEntries; 5842 if (uGrain < pExtent->uLastGrainWritten) 5843 { 5844 rc = VERR_VD_VMDK_INVALID_WRITE; 5845 goto out; 5846 } 5847 5848 if (uGDEntry != uLastGDEntry) 5849 { 5850 rc = vmdksFlushGT(pImage, pExtent, uLastGDEntry); 5851 if (RT_FAILURE(rc)) 5852 goto out; 5853 vmdksClearGT(pImage, pExtent); 5854 for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++) 5855 { 5856 rc = vmdksFlushGT(pImage, pExtent, i); 5857 if (RT_FAILURE(rc)) 5858 goto out; 5859 } 5860 } 5861 5862 /* Check access permissions as defined in the extent descriptor. 5863 * May sound a bit paradoxical, but we created the image with a 5864 * readonly extent since the resulting image is kind of "write once". */ 5865 if (pExtent->enmAccess != VMDKACCESS_READONLY) 5866 { 5867 rc = VERR_VD_VMDK_INVALID_STATE; 5868 goto out; 5869 } 5870 5871 uint64_t uFileOffset; 5872 rc = vmdkFileGetSize(pImage, pExtent->pFile, &uFileOffset); 5873 if (RT_FAILURE(rc)) 5874 goto out; 5875 if (uFileOffset % 512) 5876 { 5877 rc = VERR_INTERNAL_ERROR; 5878 goto out; 5879 } 5880 5881 /* Paranoia check: extent type, grain table buffer presence and 5882 * grain table buffer space. Also grain table entry must be clear. */ 5883 if ( pExtent->enmType != VMDKETYPE_HOSTED_SPARSE 5884 || !pImage->pGTCache 5885 || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE 5886 || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry]) 5887 { 5888 rc = VERR_INTERNAL_ERROR; 5889 goto out; 5890 } 5891 5892 /* Update grain table entry. */ 5893 pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset); 5894 5895 if (cbToWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 5896 { 5897 memcpy(pExtent->pvGrain, pvBuf, cbToWrite); 5898 memset((char *)pExtent->pvGrain + cbToWrite, '\0', 5899 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite); 5900 pData = pExtent->pvGrain; 5901 } 5902 rc = vmdkFileDeflateSync(pImage, pExtent->pFile, uFileOffset, 5903 pData, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), 5904 VMDK_MARKER_IGNORE, uSector, &cbGrain); 5905 if (RT_FAILURE(rc)) 5906 { 5907 pExtent->uGrainSector = 0; 5908 pExtent->uLastGrainSector = 0; 5909 AssertRC(rc); 5910 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname); 5911 } 5912 cbGrain = RT_ALIGN(cbGrain, 512); 5913 pExtent->uLastGrainSector = VMDK_BYTE2SECTOR(uFileOffset); 5914 pExtent->uLastGrainWritten = uGrain; 5915 pExtent->cbLastGrainWritten = cbGrain; 5916 5917 if (pcbWriteProcess) 5918 *pcbWriteProcess = cbToWrite; 5919 5920 out: 5921 LogFlowFunc(("returns %Rrc\n", rc)); 5922 return rc; 5923 } 5924 5925 /** @copydoc VBOXHDDBACKEND::pfnFlush */ 5926 static int vmdksFlush(void *pBackendData) 5927 { 5928 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 5929 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5930 int rc; 5931 5932 AssertPtr(pImage); 5933 5934 /* Pure dummy operation, closing takes care of everything. */ 5935 rc = VINF_SUCCESS; 5936 LogFlowFunc(("returns %Rrc\n", rc)); 5937 return rc; 5938 } 5939 5940 /** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */ 5941 static int vmdksSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry) 5942 { 5943 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors)); 5944 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5945 int rc; 5946 5947 AssertPtr(pImage); 5948 5949 if (pImage) 5950 { 5951 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 5952 rc = VERR_VD_IMAGE_READ_ONLY; 5953 else 5954 rc = VERR_NOT_SUPPORTED; 5955 } 5956 else 5957 rc = VERR_VD_NOT_OPENED; 5958 5959 LogFlowFunc(("returns %Rrc\n", rc)); 5960 return rc; 5961 } 5962 5963 /** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */ 5964 static int vmdksSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry) 5965 { 5966 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors)); 5967 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5968 int rc; 5969 5970 AssertPtr(pImage); 5971 5972 if (pImage) 5973 { 5974 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 5975 rc = VERR_VD_IMAGE_READ_ONLY; 5976 else 5977 rc = VERR_NOT_SUPPORTED; 5978 } 5979 else 5980 rc = VERR_VD_NOT_OPENED; 5981 5982 LogFlowFunc(("returns %Rrc\n", rc)); 5983 return rc; 5984 } 5985 5986 /** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */ 5987 static int vmdksSetOpenFlags(void *pBackendData, unsigned uOpenFlags) 5988 { 5989 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags)); 5990 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 5991 int rc = VINF_SUCCESS; 5992 5993 /* Image must be opened and the new flags must be the same as before. */ 5994 if (!pImage || pImage->uOpenFlags != uOpenFlags) 5995 { 5996 rc = VERR_INVALID_PARAMETER; 5997 goto out; 5998 } 5999 6000 out: 6001 LogFlowFunc(("returns %Rrc\n", rc)); 6002 return rc; 6003 } 6004 6005 /** @copydoc VBOXHDDBACKEND::pfnSetComment */ 6006 static int vmdksSetComment(void *pBackendData, const char *pszComment) 6007 { 6008 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment)); 6009 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6010 int rc; 6011 6012 AssertPtr(pImage); 6013 6014 if (pImage) 6015 { 6016 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 6017 rc = VERR_VD_IMAGE_READ_ONLY; 6018 else 6019 rc = VERR_NOT_SUPPORTED; 6020 } 6021 else 6022 rc = VERR_VD_NOT_OPENED; 6023 6024 LogFlowFunc(("returns %Rrc\n", rc)); 6025 return rc; 6026 } 6027 6028 /** @copydoc VBOXHDDBACKEND::pfnSetUuid */ 6029 static int vmdksSetUuid(void *pBackendData, PCRTUUID pUuid) 6030 { 6031 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid)); 6032 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6033 int rc; 6034 6035 LogFlowFunc(("%RTuuid\n", pUuid)); 6036 AssertPtr(pImage); 6037 6038 if (pImage) 6039 { 6040 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 6041 rc = VERR_VD_IMAGE_READ_ONLY; 6042 else 6043 rc = VERR_NOT_SUPPORTED; 6044 } 6045 else 6046 rc = VERR_VD_NOT_OPENED; 6047 6048 LogFlowFunc(("returns %Rrc\n", rc)); 6049 return rc; 6050 } 6051 6052 /** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */ 6053 static int vmdksSetModificationUuid(void *pBackendData, PCRTUUID pUuid) 6054 { 6055 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid)); 6056 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6057 int rc; 6058 6059 AssertPtr(pImage); 6060 6061 if (pImage) 6062 { 6063 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 6064 rc = VERR_VD_IMAGE_READ_ONLY; 6065 else 6066 rc = VERR_NOT_SUPPORTED; 6067 } 6068 else 6069 rc = VERR_VD_NOT_OPENED; 6070 6071 LogFlowFunc(("returns %Rrc\n", rc)); 6072 return rc; 6073 } 6074 6075 /** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */ 6076 static int vmdksSetParentUuid(void *pBackendData, PCRTUUID pUuid) 6077 { 6078 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid)); 6079 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6080 int rc; 6081 6082 AssertPtr(pImage); 6083 6084 if (pImage) 6085 { 6086 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 6087 rc = VERR_VD_IMAGE_READ_ONLY; 6088 else 6089 rc = VERR_NOT_SUPPORTED; 6090 } 6091 else 6092 rc = VERR_VD_NOT_OPENED; 6093 6094 LogFlowFunc(("returns %Rrc\n", rc)); 6095 return rc; 6096 } 6097 6098 /** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */ 6099 static int vmdksSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid) 6100 { 6101 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid)); 6102 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 6103 int rc; 6104 6105 AssertPtr(pImage); 6106 6107 if (pImage) 6108 { 6109 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 6110 rc = VERR_VD_IMAGE_READ_ONLY; 6111 else 6112 rc = VERR_NOT_SUPPORTED; 6113 } 6114 else 6115 rc = VERR_VD_NOT_OPENED; 6116 6117 LogFlowFunc(("returns %Rrc\n", rc)); 6118 return rc; 5197 6119 } 5198 6120 … … 6778 7700 return rc; 6779 7701 } 7702 7703 7704 VBOXHDDBACKEND g_VmdkStreamBackend = 7705 { 7706 /* pszBackendName */ 7707 "VMDKstream", 7708 /* cbSize */ 7709 sizeof(VBOXHDDBACKEND), 7710 /* uBackendCaps */ 7711 VD_CAP_UUID | VD_CAP_CREATE_DYNAMIC | VD_CAP_FILE | VD_CAP_VFS, 7712 /* papszFileExtensions */ 7713 s_apszVmdkFileExtensions, 7714 /* paConfigInfo */ 7715 NULL, 7716 /* hPlugin */ 7717 NIL_RTLDRMOD, 7718 /* pfnCheckIfValid */ 7719 vmdksCheckIfValid, 7720 /* pfnOpen */ 7721 vmdksOpen, 7722 /* pfnCreate */ 7723 vmdksCreate, 7724 /* pfnRename */ 7725 NULL, 7726 /* pfnClose */ 7727 vmdksClose, 7728 /* pfnRead */ 7729 vmdksRead, 7730 /* pfnWrite */ 7731 vmdksWrite, 7732 /* pfnFlush */ 7733 vmdksFlush, 7734 /* pfnGetVersion */ 7735 vmdkGetVersion, 7736 /* pfnGetSize */ 7737 vmdkGetSize, 7738 /* pfnGetFileSize */ 7739 vmdkGetFileSize, 7740 /* pfnGetPCHSGeometry */ 7741 vmdkGetPCHSGeometry, 7742 /* pfnSetPCHSGeometry */ 7743 vmdksSetPCHSGeometry, 7744 /* pfnGetLCHSGeometry */ 7745 vmdkGetLCHSGeometry, 7746 /* pfnSetLCHSGeometry */ 7747 vmdksSetLCHSGeometry, 7748 /* pfnGetImageFlags */ 7749 vmdkGetImageFlags, 7750 /* pfnGetOpenFlags */ 7751 vmdkGetOpenFlags, 7752 /* pfnSetOpenFlags */ 7753 vmdksSetOpenFlags, 7754 /* pfnGetComment */ 7755 vmdkGetComment, 7756 /* pfnSetComment */ 7757 vmdksSetComment, 7758 /* pfnGetUuid */ 7759 vmdkGetUuid, 7760 /* pfnSetUuid */ 7761 vmdksSetUuid, 7762 /* pfnGetModificationUuid */ 7763 vmdkGetModificationUuid, 7764 /* pfnSetModificationUuid */ 7765 vmdksSetModificationUuid, 7766 /* pfnGetParentUuid */ 7767 vmdkGetParentUuid, 7768 /* pfnSetParentUuid */ 7769 vmdksSetParentUuid, 7770 /* pfnGetParentModificationUuid */ 7771 vmdkGetParentModificationUuid, 7772 /* pfnSetParentModificationUuid */ 7773 vmdksSetParentModificationUuid, 7774 /* pfnDump */ 7775 vmdkDump, 7776 /* pfnGetTimeStamp */ 7777 NULL, 7778 /* pfnGetParentTimeStamp */ 7779 NULL, 7780 /* pfnSetParentTimeStamp */ 7781 NULL, 7782 /* pfnGetParentFilename */ 7783 NULL, 7784 /* pfnSetParentFilename */ 7785 NULL, 7786 /* pfnIsAsyncIOSupported */ 7787 NULL, 7788 /* pfnAsyncRead */ 7789 NULL, 7790 /* pfnAsyncWrite */ 7791 NULL, 7792 /* pfnAsyncFlush */ 7793 NULL, 7794 /* pfnComposeLocation */ 7795 genericFileComposeLocation, 7796 /* pfnComposeName */ 7797 genericFileComposeName, 7798 /* pfnCompact */ 7799 NULL, 7800 /* pfnResize */ 7801 NULL 7802 }; 6780 7803 6781 7804
Note:
See TracChangeset
for help on using the changeset viewer.