Changeset 7654 in vbox for trunk/src/VBox/Devices/Storage
- Timestamp:
- Mar 31, 2008 12:13:35 PM (17 years ago)
- svn:sync-xref-src-repo-rev:
- 29164
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/RawHDDCore.cpp
r7245 r7654 1 1 /** $Id$ */ 2 2 /** @file 3 * VMDKDisk image, Core Code.3 * Raw Disk image, Core Code. 4 4 */ 5 5 … … 19 19 * Header Files * 20 20 *******************************************************************************/ 21 #define LOG_GROUP LOG_GROUP_VD_ VMDK21 #define LOG_GROUP LOG_GROUP_VD_RAW 22 22 #include "VBoxHDD-newInternal.h" 23 23 #include <VBox/err.h> … … 26 26 #include <iprt/assert.h> 27 27 #include <iprt/alloc.h> 28 #include <iprt/uuid.h>29 28 #include <iprt/file.h> 30 #include <iprt/path.h>31 #include <iprt/string.h>32 #include <iprt/rand.h>33 29 34 30 … … 37 33 *******************************************************************************/ 38 34 39 /** VMDK descriptor DDB entry for PCHS cylinders. */40 #define VMDK_DDB_GEO_PCHS_CYLINDERS "ddb.geometry.cylinders"41 42 /** VMDK descriptor DDB entry for PCHS heads. */43 #define VMDK_DDB_GEO_PCHS_HEADS "ddb.geometry.heads"44 45 /** VMDK descriptor DDB entry for PCHS sectors. */46 #define VMDK_DDB_GEO_PCHS_SECTORS "ddb.geometry.sectors"47 48 /** VMDK descriptor DDB entry for LCHS cylinders. */49 #define VMDK_DDB_GEO_LCHS_CYLINDERS "ddb.geometry.biosCylinders"50 51 /** VMDK descriptor DDB entry for LCHS heads. */52 #define VMDK_DDB_GEO_LCHS_HEADS "ddb.geometry.biosHeads"53 54 /** VMDK descriptor DDB entry for LCHS sectors. */55 #define VMDK_DDB_GEO_LCHS_SECTORS "ddb.geometry.biosSectors"56 57 /** VMDK descriptor DDB entry for image UUID. */58 #define VMDK_DDB_IMAGE_UUID "ddb.uuid.image"59 60 /** VMDK descriptor DDB entry for image modification UUID. */61 #define VMDK_DDB_MODIFICATION_UUID "ddb.uuid.modification"62 63 /** VMDK descriptor DDB entry for parent image UUID. */64 #define VMDK_DDB_PARENT_UUID "ddb.uuid.parent"65 66 /** VMDK descriptor DDB entry for parent image modification UUID. */67 #define VMDK_DDB_PARENT_MODIFICATION_UUID "ddb.uuid.parentmodification"68 69 35 /** 70 * Magic number for hosted images created by VMware Workstation 4, VMware 71 * Workstation 5, VMware Server or VMware Player. 36 * Raw image data structure. 72 37 */ 73 #define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */ 74 75 /** VMDK hosted sparse extent header. */ 76 #pragma pack(1) 77 typedef struct SparseExtentHeader 78 { 79 uint32_t magicNumber; 80 uint32_t version; 81 uint32_t flags; 82 uint64_t capacity; 83 uint64_t grainSize; 84 uint64_t descriptorOffset; 85 uint64_t descriptorSize; 86 uint32_t numGTEsPerGT; 87 uint64_t rgdOffset; 88 uint64_t gdOffset; 89 uint64_t overHead; 90 bool uncleanShutdown; 91 char singleEndLineChar; 92 char nonEndLineChar; 93 char doubleEndLineChar1; 94 char doubleEndLineChar2; 95 uint8_t pad[435]; 96 } SparseExtentHeader; 97 #pragma pack() 98 99 /** VMDK capacity for a single chunk when 2G splitting is turned on. Should be 100 * divisible by the default grain size (64K) */ 101 #define VMDK_2G_SPLIT_SIZE (2047 * 1024 * 1024) 102 103 104 #ifdef VBOX_WITH_VMDK_ESX 105 106 /** @todo the ESX code is not tested, not used, and lacks error messages. */ 107 108 /** 109 * Magic number for images created by VMware GSX Server 3 or ESX Server 3. 110 */ 111 #define VMDK_ESX_SPARSE_MAGICNUMBER 0x44574f43 /* 'C' 'O' 'W' 'D' */ 112 113 #pragma pack(1) 114 typedef struct COWDisk_Header 115 { 116 uint32_t magicNumber; 117 uint32_t version; 118 uint32_t flags; 119 uint32_t numSectors; 120 uint32_t grainSize; 121 uint32_t gdOffset; 122 uint32_t numGDEntries; 123 uint32_t freeSector; 124 /* The spec incompletely documents quite a few further fields, but states 125 * that they are unused by the current format. Replace them by padding. */ 126 char reserved1[1604]; 127 uint32_t savedGeneration; 128 char reserved2[8]; 129 uint32_t uncleanShutdown; 130 char padding[396]; 131 } COWDisk_Header; 132 #pragma pack() 133 #endif /* VBOX_WITH_VMDK_ESX */ 134 135 136 /** Convert sector number/size to byte offset/size. */ 137 #define VMDK_SECTOR2BYTE(u) ((u) << 9) 138 139 /** Convert byte offset/size to sector number/size. */ 140 #define VMDK_BYTE2SECTOR(u) ((u) >> 9) 141 142 /** 143 * VMDK extent type. 144 */ 145 typedef enum VMDKETYPE 146 { 147 /** Hosted sparse extent. */ 148 VMDKETYPE_HOSTED_SPARSE = 1, 149 /** Flat extent. */ 150 VMDKETYPE_FLAT, 151 /** Zero extent. */ 152 VMDKETYPE_ZERO 153 #ifdef VBOX_WITH_VMDK_ESX 154 , 155 /** ESX sparse extent. */ 156 VMDKETYPE_ESX_SPARSE 157 #endif /* VBOX_WITH_VMDK_ESX */ 158 } VMDKETYPE, *PVMDKETYPE; 159 160 /** 161 * VMDK access type for a extent. 162 */ 163 typedef enum VMDKACCESS 164 { 165 /** No access allowed. */ 166 VMDKACCESS_NOACCESS = 0, 167 /** Read-only access. */ 168 VMDKACCESS_READONLY, 169 /** Read-write access. */ 170 VMDKACCESS_READWRITE 171 } VMDKACCESS, *PVMDKACCESS; 172 173 /** 174 * VMDK extent data structure. 175 */ 176 typedef struct VMDKEXTENT 177 { 178 /** File handle. */ 179 RTFILE File; 180 /** Base name of the image extent. */ 181 const char *pszBasename; 182 /** Full name of the image extent. */ 183 const char *pszFullname; 184 /** Number of sectors in this extent. */ 185 uint64_t cSectors; 186 /** Number of sectors per block (grain in VMDK speak). */ 187 uint64_t cSectorsPerGrain; 188 /** Starting sector number of descriptor. */ 189 uint64_t uDescriptorSector; 190 /** Size of descriptor in sectors. */ 191 uint64_t cDescriptorSectors; 192 /** Starting sector number of grain directory. */ 193 uint64_t uSectorGD; 194 /** Starting sector number of redundant grain directory. */ 195 uint64_t uSectorRGD; 196 /** Total number of metadata sectors. */ 197 uint64_t cOverheadSectors; 198 /** Nominal size (i.e. as described by the descriptor) of this extent. */ 199 uint64_t cNominalSectors; 200 /** Sector offset (i.e. as described by the descriptor) of this extent. */ 201 uint64_t uSectorOffset; 202 /** Number of entries in a grain table. */ 203 uint32_t cGTEntries; 204 /** Number of sectors reachable via a grain directory entry. */ 205 uint32_t cSectorsPerGDE; 206 /** Number of entries in the grain directory. */ 207 uint32_t cGDEntries; 208 /** Pointer to the next free sector. Legacy information. Do not use. */ 209 uint32_t uFreeSector; 210 /** Number of this extent in the list of images. */ 211 uint32_t uExtent; 212 /** Pointer to the descriptor (NULL if no descriptor in this extent). */ 213 char *pDescData; 214 /** Pointer to the grain directory. */ 215 uint32_t *pGD; 216 /** Pointer to the redundant grain directory. */ 217 uint32_t *pRGD; 218 /** Type of this extent. */ 219 VMDKETYPE enmType; 220 /** Access to this extent. */ 221 VMDKACCESS enmAccess; 222 /** Flag whether this extent is marked as unclean. */ 223 bool fUncleanShutdown; 224 /** Flag whether the metadata in the extent header needs to be updated. */ 225 bool fMetaDirty; 226 /** Reference to the image in which this extent is used. Do not use this 227 * on a regular basis to avoid passing pImage references to functions 228 * explicitly. */ 229 struct VMDKIMAGE *pImage; 230 } VMDKEXTENT, *PVMDKEXTENT; 231 232 /** 233 * Grain table cache size. Allocated per image. 234 */ 235 #define VMDK_GT_CACHE_SIZE 256 236 237 /** 238 * Grain table block size. Smaller than an actual grain table block to allow 239 * more grain table blocks to be cached without having to allocate excessive 240 * amounts of memory for the cache. 241 */ 242 #define VMDK_GT_CACHELINE_SIZE 128 243 244 245 /** 246 * Maximum number of lines in a descriptor file. Not worth the effort of 247 * making it variable. Descriptor files are generally very short (~20 lines). 248 */ 249 #define VMDK_DESCRIPTOR_LINES_MAX 100U 250 251 /** 252 * Parsed descriptor information. Allows easy access and update of the 253 * descriptor (whether separate file or not). Free form text files suck. 254 */ 255 typedef struct VMDKDESCRIPTOR 256 { 257 /** Line number of first entry of the disk descriptor. */ 258 unsigned uFirstDesc; 259 /** Line number of first entry in the extent description. */ 260 unsigned uFirstExtent; 261 /** Line number of first disk database entry. */ 262 unsigned uFirstDDB; 263 /** Total number of lines. */ 264 unsigned cLines; 265 /** Total amount of memory available for the descriptor. */ 266 size_t cbDescAlloc; 267 /** Set if descriptor has been changed and not yet written to disk. */ 268 bool fDirty; 269 /** Array of pointers to the data in the descriptor. */ 270 char *aLines[VMDK_DESCRIPTOR_LINES_MAX]; 271 /** Array of line indices pointing to the next non-comment line. */ 272 unsigned aNextLines[VMDK_DESCRIPTOR_LINES_MAX]; 273 } VMDKDESCRIPTOR, *PVMDKDESCRIPTOR; 274 275 276 /** 277 * Cache entry for translating extent/sector to a sector number in that 278 * extent. 279 */ 280 typedef struct VMDKGTCACHEENTRY 281 { 282 /** Extent number for which this entry is valid. */ 283 uint32_t uExtent; 284 /** GT data block number. */ 285 uint64_t uGTBlock; 286 /** Data part of the cache entry. */ 287 uint32_t aGTData[VMDK_GT_CACHELINE_SIZE]; 288 } VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY; 289 290 /** 291 * Cache data structure for blocks of grain table entries. For now this is a 292 * fixed size direct mapping cache, but this should be adapted to the size of 293 * the sparse image and maybe converted to a set-associative cache. The 294 * implementation below implements a write-through cache with write allocate. 295 */ 296 typedef struct VMDKGTCACHE 297 { 298 /** Cache entries. */ 299 VMDKGTCACHEENTRY aGTCache[VMDK_GT_CACHE_SIZE]; 300 /** Number of cache entries (currently unused). */ 301 unsigned cEntries; 302 } VMDKGTCACHE, *PVMDKGTCACHE; 303 304 /** 305 * Complete VMDK image data structure. Mainly a collection of extents and a few 306 * extra global data fields. 307 */ 308 typedef struct VMDKIMAGE 309 { 310 /** Pointer to the image extents. */ 311 PVMDKEXTENT pExtents; 312 /** Number of image extents. */ 313 unsigned cExtents; 314 38 typedef struct RAWIMAGE 39 { 315 40 /** Base image name. */ 316 41 const char *pszFilename; 317 /** Descriptor file if applicable. */42 /** File descriptor. */ 318 43 RTFILE File; 319 44 … … 332 57 uint64_t cbSize; 333 58 /** Physical geometry of this image. */ 334 PDMMEDIAGEOMETRY 59 PDMMEDIAGEOMETRY PCHSGeometry; 335 60 /** Logical geometry of this image. */ 336 PDMMEDIAGEOMETRY LCHSGeometry; 337 /** Image UUID. */ 338 RTUUID ImageUuid; 339 /** Image modification UUID. */ 340 RTUUID ModificationUuid; 341 /** Parent image UUID. */ 342 RTUUID ParentUuid; 343 /** Parent image modification UUID. */ 344 RTUUID ParentModificationUuid; 345 346 /** Pointer to grain table cache, if this image contains sparse extents. */ 347 PVMDKGTCACHE pGTCache; 348 /** Pointer to the descriptor (NULL if no separate descriptor file). */ 349 char *pDescData; 350 /** Allocation size of the descriptor file. */ 351 size_t cbDescAlloc; 352 /** Parsed descriptor file content. */ 353 VMDKDESCRIPTOR Descriptor; 354 } VMDKIMAGE, *PVMDKIMAGE; 61 PDMMEDIAGEOMETRY LCHSGeometry; 62 63 } RAWIMAGE, *PRAWIMAGE; 355 64 356 65 … … 359 68 *******************************************************************************/ 360 69 361 static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent); 362 static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent); 363 364 static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor); 365 static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent); 366 static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent); 367 #ifdef VBOX_WITH_VMDK_ESX 368 static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent); 369 #endif /* VBOX_WITH_VMDK_ESX */ 370 static void vmdkFreeExtentData(PVMDKEXTENT pExtent, bool fDelete); 371 372 static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents); 373 static int vmdkFlushImage(PVMDKIMAGE pImage); 374 static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment); 375 static void vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete); 70 static int rawFlushImage(PRAWIMAGE pImage); 71 static void rawFreeImage(PRAWIMAGE pImage, bool fDelete); 376 72 377 73 … … 379 75 * Internal: signal an error to the frontend. 380 76 */ 381 DECLINLINE(int) vmdkError(PVMDKIMAGE pImage, int rc, RT_SRC_POS_DECL,382 77 DECLINLINE(int) rawError(PRAWIMAGE pImage, int rc, RT_SRC_POS_DECL, 78 const char *pszFormat, ...) 383 79 { 384 80 va_list va; … … 392 88 393 89 /** 394 * Internal: truncate a string (at a UTF8 code point boundary) and encode the395 * critical non-ASCII characters.396 */397 static char *vmdkEncodeString(const char *psz)398 {399 /** @todo implement me. */400 return RTStrDup(psz);401 }402 403 /**404 * Internal: decode a string and store it into the specified string.405 */406 static int vmdkDecodeString(const char *pszEncoded, char *psz, size_t cb)407 {408 /** @todo implement me. */409 if (!cb)410 return VINF_SUCCESS;411 strncpy(psz, pszEncoded, cb);412 psz[cb - 1] = '\0';413 return VINF_SUCCESS;414 }415 416 static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent)417 {418 int rc = VINF_SUCCESS;419 unsigned i;420 uint32_t *pGD = NULL, *pRGD = NULL, *pGDTmp, *pRGDTmp;421 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);422 423 pGD = (uint32_t *)RTMemAllocZ(cbGD);424 if (!pGD)425 {426 rc = VERR_NO_MEMORY;427 goto out;428 }429 pExtent->pGD = pGD;430 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD),431 pGD, cbGD, NULL);432 AssertRC(rc);433 if (VBOX_FAILURE(rc))434 {435 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read grain directory in '%s'"), pExtent->pszFullname);436 goto out;437 }438 for (i = 0, pGDTmp = pGD; i < pExtent->cGDEntries; i++, pGDTmp++)439 *pGDTmp = RT_LE2H_U32(*pGDTmp);440 441 if (pExtent->uSectorRGD)442 {443 pRGD = (uint32_t *)RTMemAllocZ(cbGD);444 if (!pRGD)445 {446 rc = VERR_NO_MEMORY;447 goto out;448 }449 pExtent->pRGD = pRGD;450 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD),451 pRGD, cbGD, NULL);452 AssertRC(rc);453 if (VBOX_FAILURE(rc))454 {455 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read redundant grain directory in '%s'"), pExtent->pszFullname);456 goto out;457 }458 for (i = 0, pRGDTmp = pRGD; i < pExtent->cGDEntries; i++, pRGDTmp++)459 *pRGDTmp = RT_LE2H_U32(*pRGDTmp);460 461 /* Check grain table and redundant grain table for consistency. */462 size_t cbGT = pExtent->cGTEntries;463 uint32_t *pTmpGT1 = (uint32_t *)RTMemTmpAlloc(cbGT);464 if (!pTmpGT1)465 {466 rc = VERR_NO_MEMORY;467 goto out;468 }469 uint32_t *pTmpGT2 = (uint32_t *)RTMemTmpAlloc(cbGT);470 if (!pTmpGT2)471 {472 RTMemTmpFree(pTmpGT1);473 rc = VERR_NO_MEMORY;474 goto out;475 }476 477 for (i = 0, pGDTmp = pGD, pRGDTmp = pRGD;478 i < pExtent->cGDEntries;479 i++, pGDTmp++, pRGDTmp++)480 {481 /* If no grain table is allocated skip the entry. */482 if (*pGDTmp == 0 && *pRGDTmp == 0)483 continue;484 485 if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)486 {487 /* Just one grain directory entry refers to a not yet allocated488 * grain table or both grain directory copies refer to the same489 * grain table. Not allowed. */490 RTMemTmpFree(pTmpGT1);491 RTMemTmpFree(pTmpGT2);492 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);493 goto out;494 }495 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pGDTmp),496 pTmpGT1, cbGT, NULL);497 if (VBOX_FAILURE(rc))498 {499 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);500 RTMemTmpFree(pTmpGT1);501 RTMemTmpFree(pTmpGT2);502 goto out;503 }504 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pRGDTmp),505 pTmpGT2, cbGT, NULL);506 if (VBOX_FAILURE(rc))507 {508 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading backup grain table in '%s'"), pExtent->pszFullname);509 RTMemTmpFree(pTmpGT1);510 RTMemTmpFree(pTmpGT2);511 goto out;512 }513 if (memcmp(pTmpGT1, pTmpGT2, cbGT))514 {515 RTMemTmpFree(pTmpGT1);516 RTMemTmpFree(pTmpGT2);517 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistency between grain table and backup grain table in '%s'"), pExtent->pszFullname);518 goto out;519 }520 }521 522 /** @todo figure out what to do for unclean VMDKs. */523 }524 525 out:526 if (VBOX_FAILURE(rc))527 vmdkFreeGrainDirectory(pExtent);528 return rc;529 }530 531 static int vmdkCreateGrainDirectory(PVMDKEXTENT pExtent, uint64_t uStartSector,532 bool fPreAlloc)533 {534 int rc = VINF_SUCCESS;535 unsigned i;536 uint32_t *pGD = NULL, *pRGD = NULL;537 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);538 size_t cbGDRounded = RT_ALIGN_64(pExtent->cGDEntries * sizeof(uint32_t), 512);539 size_t cbGTRounded;540 uint64_t cbOverhead;541 542 if (fPreAlloc)543 cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);544 else545 cbGTRounded = 0;546 547 pGD = (uint32_t *)RTMemAllocZ(cbGD);548 if (!pGD)549 {550 rc = VERR_NO_MEMORY;551 goto out;552 }553 pExtent->pGD = pGD;554 pRGD = (uint32_t *)RTMemAllocZ(cbGD);555 if (!pRGD)556 {557 rc = VERR_NO_MEMORY;558 goto out;559 }560 pExtent->pRGD = pRGD;561 562 cbOverhead = RT_ALIGN_64(VMDK_SECTOR2BYTE(uStartSector) + 2 * (cbGDRounded + cbGTRounded), VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));563 rc = RTFileSetSize(pExtent->File, cbOverhead);564 if (VBOX_FAILURE(rc))565 goto out;566 pExtent->uSectorRGD = uStartSector;567 pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded);568 569 if (fPreAlloc)570 {571 uint32_t uGTSectorLE;572 uint64_t uOffsetSectors;573 574 uOffsetSectors = pExtent->uSectorRGD + VMDK_BYTE2SECTOR(cbGDRounded);575 for (i = 0; i < pExtent->cGDEntries; i++)576 {577 pRGD[i] = uOffsetSectors;578 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);579 /* Write the redundant grain directory entry to disk. */580 rc = RTFileWriteAt(pExtent->File,581 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE),582 &uGTSectorLE, sizeof(uGTSectorLE), NULL);583 if (VBOX_FAILURE(rc))584 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);585 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));586 }587 588 uOffsetSectors = pExtent->uSectorGD + VMDK_BYTE2SECTOR(cbGDRounded);589 for (i = 0; i < pExtent->cGDEntries; i++)590 {591 pGD[i] = uOffsetSectors;592 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);593 /* Write the grain directory entry to disk. */594 rc = RTFileWriteAt(pExtent->File,595 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE),596 &uGTSectorLE, sizeof(uGTSectorLE), NULL);597 if (VBOX_FAILURE(rc))598 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);599 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));600 }601 }602 pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);603 604 out:605 if (VBOX_FAILURE(rc))606 vmdkFreeGrainDirectory(pExtent);607 return rc;608 }609 610 static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)611 {612 if (pExtent->pGD)613 {614 RTMemFree(pExtent->pGD);615 pExtent->pGD = NULL;616 }617 if (pExtent->pRGD)618 {619 RTMemFree(pExtent->pRGD);620 pExtent->pRGD = NULL;621 }622 }623 624 static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr,625 char **ppszUnquoted, char **ppszNext)626 {627 char *pszQ;628 char *pszUnquoted;629 630 /* Skip over whitespace. */631 while (*pszStr == ' ' || *pszStr == '\t')632 pszStr++;633 if (*pszStr++ != '"')634 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);635 636 pszQ = (char *)strchr(pszStr, '"');637 if (pszQ == NULL)638 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);639 pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);640 if (!pszUnquoted)641 return VERR_NO_MEMORY;642 memcpy(pszUnquoted, pszStr, pszQ - pszStr);643 pszUnquoted[pszQ - pszStr] = '\0';644 *ppszUnquoted = pszUnquoted;645 if (ppszNext)646 *ppszNext = pszQ + 1;647 return VINF_SUCCESS;648 }649 650 static int vmdkDescInitStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,651 const char *pszLine)652 {653 char *pEnd = pDescriptor->aLines[pDescriptor->cLines];654 ssize_t cbDiff = strlen(pszLine) + 1;655 656 if ( pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1657 && pEnd - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)658 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);659 660 memcpy(pEnd, pszLine, cbDiff);661 pDescriptor->cLines++;662 pDescriptor->aLines[pDescriptor->cLines] = pEnd + cbDiff;663 pDescriptor->fDirty = true;664 665 return VINF_SUCCESS;666 }667 668 static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,669 const char *pszKey, const char **ppszValue)670 {671 size_t cbKey = strlen(pszKey);672 const char *pszValue;673 674 while (uStart != 0)675 {676 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))677 {678 /* Key matches, check for a '=' (preceded by whitespace). */679 pszValue = pDescriptor->aLines[uStart] + cbKey;680 while (*pszValue == ' ' || *pszValue == '\t')681 pszValue++;682 if (*pszValue == '=')683 {684 *ppszValue = pszValue + 1;685 break;686 }687 }688 uStart = pDescriptor->aNextLines[uStart];689 }690 return !!uStart;691 }692 693 static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,694 unsigned uStart,695 const char *pszKey, const char *pszValue)696 {697 char *pszTmp;698 size_t cbKey = strlen(pszKey);699 unsigned uLast = 0;700 701 while (uStart != 0)702 {703 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))704 {705 /* Key matches, check for a '=' (preceded by whitespace). */706 pszTmp = pDescriptor->aLines[uStart] + cbKey;707 while (*pszTmp == ' ' || *pszTmp == '\t')708 pszTmp++;709 if (*pszTmp == '=')710 {711 while (*pszTmp == ' ' || *pszTmp == '\t')712 pszTmp++;713 break;714 }715 }716 if (!pDescriptor->aNextLines[uStart])717 uLast = uStart;718 uStart = pDescriptor->aNextLines[uStart];719 }720 if (uStart)721 {722 if (pszValue)723 {724 /* Key already exists, replace existing value. */725 size_t cbOldVal = strlen(pszTmp);726 size_t cbNewVal = strlen(pszValue);727 ssize_t cbDiff = cbNewVal - cbOldVal;728 /* Check for buffer overflow. */729 if ( pDescriptor->aLines[pDescriptor->cLines]730 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)731 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);732 733 memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,734 pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);735 memcpy(pszTmp, pszValue, cbNewVal + 1);736 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)737 pDescriptor->aLines[i] += cbDiff;738 }739 else740 {741 memmove(pDescriptor->aLines[uStart], pDescriptor->aLines[uStart+1],742 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uStart+1] + 1);743 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)744 {745 pDescriptor->aLines[i-1] = pDescriptor->aLines[i];746 if (pDescriptor->aNextLines[i])747 pDescriptor->aNextLines[i-1] = pDescriptor->aNextLines[i] - 1;748 else749 pDescriptor->aNextLines[i-1] = 0;750 }751 pDescriptor->cLines--;752 /* Adjust starting line numbers of following descriptor sections. */753 if (uStart < pDescriptor->uFirstExtent)754 pDescriptor->uFirstExtent--;755 if (uStart < pDescriptor->uFirstDDB)756 pDescriptor->uFirstDDB--;757 }758 }759 else760 {761 /* Key doesn't exist, append after the last entry in this category. */762 if (!pszValue)763 {764 /* Key doesn't exist, and it should be removed. Simply a no-op. */765 return VINF_SUCCESS;766 }767 size_t cbKey = strlen(pszKey);768 size_t cbValue = strlen(pszValue);769 ssize_t cbDiff = cbKey + 1 + cbValue + 1;770 /* Check for buffer overflow. */771 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)772 || ( pDescriptor->aLines[pDescriptor->cLines]773 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))774 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);775 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)776 {777 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];778 if (pDescriptor->aNextLines[i - 1])779 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;780 else781 pDescriptor->aNextLines[i] = 0;782 }783 uStart = uLast + 1;784 pDescriptor->aNextLines[uLast] = uStart;785 pDescriptor->aNextLines[uStart] = 0;786 pDescriptor->cLines++;787 pszTmp = pDescriptor->aLines[uStart];788 memmove(pszTmp + cbDiff, pszTmp,789 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);790 memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);791 pDescriptor->aLines[uStart][cbKey] = '=';792 memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);793 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)794 pDescriptor->aLines[i] += cbDiff;795 796 /* Adjust starting line numbers of following descriptor sections. */797 if (uStart <= pDescriptor->uFirstExtent)798 pDescriptor->uFirstExtent++;799 if (uStart <= pDescriptor->uFirstDDB)800 pDescriptor->uFirstDDB++;801 }802 pDescriptor->fDirty = true;803 return VINF_SUCCESS;804 }805 806 static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,807 uint32_t *puValue)808 {809 const char *pszValue;810 811 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,812 &pszValue))813 return VERR_VDI_VALUE_NOT_FOUND;814 return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);815 }816 817 static int vmdkDescBaseGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,818 const char *pszKey, const char **ppszValue)819 {820 const char *pszValue;821 char *pszValueUnquoted;822 823 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,824 &pszValue))825 return VERR_VDI_VALUE_NOT_FOUND;826 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);827 if (VBOX_FAILURE(rc))828 return rc;829 *ppszValue = pszValueUnquoted;830 return rc;831 }832 833 static int vmdkDescBaseSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,834 const char *pszKey, const char *pszValue)835 {836 char *pszValueQuoted;837 838 int rc = RTStrAPrintf(&pszValueQuoted, "\"%s\"", pszValue);839 if (VBOX_FAILURE(rc))840 return rc;841 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, pszKey,842 pszValueQuoted);843 RTStrFree(pszValueQuoted);844 return rc;845 }846 847 static void vmdkDescExtRemoveDummy(PVMDKIMAGE pImage,848 PVMDKDESCRIPTOR pDescriptor)849 {850 unsigned uEntry = pDescriptor->uFirstExtent;851 ssize_t cbDiff;852 853 if (!uEntry)854 return;855 856 cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;857 /* Move everything including \0 in the entry marking the end of buffer. */858 memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],859 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);860 for (unsigned i = uEntry + 1; i <= pDescriptor->cLines; i++)861 {862 pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;863 if (pDescriptor->aNextLines[i])864 pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;865 else866 pDescriptor->aNextLines[i - 1] = 0;867 }868 pDescriptor->cLines--;869 if (pDescriptor->uFirstDDB)870 pDescriptor->uFirstDDB--;871 872 return;873 }874 875 static int vmdkDescExtInsert(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,876 VMDKACCESS enmAccess, uint64_t cNominalSectors,877 VMDKETYPE enmType, const char *pszBasename,878 uint64_t uSectorOffset)879 {880 static const char *apszAccess[] = { "NOACCESS", "RDONLY", "RW" };881 static const char *apszType[] = { "", "SPARSE", "FLAT", "ZERO" };882 char *pszTmp;883 unsigned uStart = pDescriptor->uFirstExtent, uLast = 0;884 char szExt[1024];885 ssize_t cbDiff;886 887 /* Find last entry in extent description. */888 while (uStart)889 {890 if (!pDescriptor->aNextLines[uStart])891 uLast = uStart;892 uStart = pDescriptor->aNextLines[uStart];893 }894 895 if (enmType == VMDKETYPE_ZERO)896 {897 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s ", apszAccess[enmAccess],898 cNominalSectors, apszType[enmType]);899 }900 else901 {902 if (!uSectorOffset)903 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\"",904 apszAccess[enmAccess], cNominalSectors,905 apszType[enmType], pszBasename);906 else907 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\" %llu",908 apszAccess[enmAccess], cNominalSectors,909 apszType[enmType], pszBasename, uSectorOffset);910 }911 cbDiff = strlen(szExt) + 1;912 913 /* Check for buffer overflow. */914 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)915 || ( pDescriptor->aLines[pDescriptor->cLines]916 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))917 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);918 919 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)920 {921 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];922 if (pDescriptor->aNextLines[i - 1])923 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;924 else925 pDescriptor->aNextLines[i] = 0;926 }927 uStart = uLast + 1;928 pDescriptor->aNextLines[uLast] = uStart;929 pDescriptor->aNextLines[uStart] = 0;930 pDescriptor->cLines++;931 pszTmp = pDescriptor->aLines[uStart];932 memmove(pszTmp + cbDiff, pszTmp,933 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);934 memcpy(pDescriptor->aLines[uStart], szExt, cbDiff);935 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)936 pDescriptor->aLines[i] += cbDiff;937 938 /* Adjust starting line numbers of following descriptor sections. */939 if (uStart <= pDescriptor->uFirstDDB)940 pDescriptor->uFirstDDB++;941 942 pDescriptor->fDirty = true;943 return VINF_SUCCESS;944 }945 946 static int vmdkDescDDBGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,947 const char *pszKey, const char **ppszValue)948 {949 const char *pszValue;950 char *pszValueUnquoted;951 952 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,953 &pszValue))954 return VERR_VDI_VALUE_NOT_FOUND;955 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);956 if (VBOX_FAILURE(rc))957 return rc;958 *ppszValue = pszValueUnquoted;959 return rc;960 }961 962 static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,963 const char *pszKey, uint32_t *puValue)964 {965 const char *pszValue;966 char *pszValueUnquoted;967 968 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,969 &pszValue))970 return VERR_VDI_VALUE_NOT_FOUND;971 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);972 if (VBOX_FAILURE(rc))973 return rc;974 rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);975 RTMemTmpFree(pszValueUnquoted);976 return rc;977 }978 979 static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,980 const char *pszKey, PRTUUID pUuid)981 {982 const char *pszValue;983 char *pszValueUnquoted;984 985 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,986 &pszValue))987 return VERR_VDI_VALUE_NOT_FOUND;988 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);989 if (VBOX_FAILURE(rc))990 return rc;991 rc = RTUuidFromStr(pUuid, pszValueUnquoted);992 RTMemTmpFree(pszValueUnquoted);993 return rc;994 }995 996 static int vmdkDescDDBSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,997 const char *pszKey, const char *pszVal)998 {999 int rc;1000 char *pszValQuoted;1001 1002 if (pszVal)1003 {1004 rc = RTStrAPrintf(&pszValQuoted, "\"%s\"", pszVal);1005 if (VBOX_FAILURE(rc))1006 return rc;1007 }1008 else1009 pszValQuoted = NULL;1010 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,1011 pszValQuoted);1012 if (pszValQuoted)1013 RTStrFree(pszValQuoted);1014 return rc;1015 }1016 1017 static int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,1018 const char *pszKey, PCRTUUID pUuid)1019 {1020 char *pszUuid;1021 1022 int rc = RTStrAPrintf(&pszUuid, "\"%Vuuid\"", pUuid);1023 if (VBOX_FAILURE(rc))1024 return rc;1025 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,1026 pszUuid);1027 RTStrFree(pszUuid);1028 return rc;1029 }1030 1031 static int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,1032 const char *pszKey, uint32_t uValue)1033 {1034 char *pszValue;1035 1036 int rc = RTStrAPrintf(&pszValue, "\"%d\"", uValue);1037 if (VBOX_FAILURE(rc))1038 return rc;1039 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,1040 pszValue);1041 RTStrFree(pszValue);1042 return rc;1043 }1044 1045 static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData,1046 size_t cbDescData,1047 PVMDKDESCRIPTOR pDescriptor)1048 {1049 int rc = VINF_SUCCESS;1050 unsigned cLine = 0, uLastNonEmptyLine = 0;1051 char *pTmp = pDescData;1052 1053 pDescriptor->cbDescAlloc = cbDescData;1054 while (*pTmp != '\0')1055 {1056 pDescriptor->aLines[cLine++] = pTmp;1057 if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)1058 {1059 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);1060 goto out;1061 }1062 1063 while (*pTmp != '\0' && *pTmp != '\n')1064 {1065 if (*pTmp == '\r')1066 {1067 if (*(pTmp + 1) != '\n')1068 {1069 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor in '%s'"), pImage->pszFilename);1070 goto out;1071 }1072 else1073 {1074 /* Get rid of CR character. */1075 *pTmp = '\0';1076 }1077 }1078 pTmp++;1079 }1080 /* Get rid of LF character. */1081 if (*pTmp == '\n')1082 {1083 *pTmp = '\0';1084 pTmp++;1085 }1086 }1087 pDescriptor->cLines = cLine;1088 /* Pointer right after the end of the used part of the buffer. */1089 pDescriptor->aLines[cLine] = pTmp;1090 1091 if (strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile"))1092 {1093 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);1094 goto out;1095 }1096 1097 /* Initialize those, because we need to be able to reopen an image. */1098 pDescriptor->uFirstDesc = 0;1099 pDescriptor->uFirstExtent = 0;1100 pDescriptor->uFirstDDB = 0;1101 for (unsigned i = 0; i < cLine; i++)1102 {1103 if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')1104 {1105 if ( !strncmp(pDescriptor->aLines[i], "RW", 2)1106 || !strncmp(pDescriptor->aLines[i], "RDONLY", 6)1107 || !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )1108 {1109 /* An extent descriptor. */1110 if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)1111 {1112 /* Incorrect ordering of entries. */1113 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);1114 goto out;1115 }1116 if (!pDescriptor->uFirstExtent)1117 {1118 pDescriptor->uFirstExtent = i;1119 uLastNonEmptyLine = 0;1120 }1121 }1122 else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))1123 {1124 /* A disk database entry. */1125 if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)1126 {1127 /* Incorrect ordering of entries. */1128 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);1129 goto out;1130 }1131 if (!pDescriptor->uFirstDDB)1132 {1133 pDescriptor->uFirstDDB = i;1134 uLastNonEmptyLine = 0;1135 }1136 }1137 else1138 {1139 /* A normal entry. */1140 if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)1141 {1142 /* Incorrect ordering of entries. */1143 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);1144 goto out;1145 }1146 if (!pDescriptor->uFirstDesc)1147 {1148 pDescriptor->uFirstDesc = i;1149 uLastNonEmptyLine = 0;1150 }1151 }1152 if (uLastNonEmptyLine)1153 pDescriptor->aNextLines[uLastNonEmptyLine] = i;1154 uLastNonEmptyLine = i;1155 }1156 }1157 1158 out:1159 return rc;1160 }1161 1162 static int vmdkDescSetPCHSGeometry(PVMDKIMAGE pImage,1163 PCPDMMEDIAGEOMETRY pPCHSGeometry)1164 {1165 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1166 VMDK_DDB_GEO_PCHS_CYLINDERS,1167 pPCHSGeometry->cCylinders);1168 if (VBOX_FAILURE(rc))1169 return rc;1170 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1171 VMDK_DDB_GEO_PCHS_HEADS,1172 pPCHSGeometry->cHeads);1173 if (VBOX_FAILURE(rc))1174 return rc;1175 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1176 VMDK_DDB_GEO_PCHS_SECTORS,1177 pPCHSGeometry->cSectors);1178 return rc;1179 }1180 1181 static int vmdkDescSetLCHSGeometry(PVMDKIMAGE pImage,1182 PCPDMMEDIAGEOMETRY pLCHSGeometry)1183 {1184 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1185 VMDK_DDB_GEO_LCHS_CYLINDERS,1186 pLCHSGeometry->cCylinders);1187 if (VBOX_FAILURE(rc))1188 return rc;1189 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1190 VMDK_DDB_GEO_LCHS_HEADS,1191 pLCHSGeometry->cHeads);1192 if (VBOX_FAILURE(rc))1193 return rc;1194 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,1195 VMDK_DDB_GEO_LCHS_SECTORS,1196 pLCHSGeometry->cSectors);1197 return rc;1198 }1199 1200 static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData,1201 size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)1202 {1203 int rc;1204 1205 pDescriptor->uFirstDesc = 0;1206 pDescriptor->uFirstExtent = 0;1207 pDescriptor->uFirstDDB = 0;1208 pDescriptor->cLines = 0;1209 pDescriptor->cbDescAlloc = cbDescData;1210 pDescriptor->fDirty = false;1211 pDescriptor->aLines[pDescriptor->cLines] = pDescData;1212 memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));1213 1214 rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");1215 if (VBOX_FAILURE(rc))1216 goto out;1217 rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");1218 if (VBOX_FAILURE(rc))1219 goto out;1220 pDescriptor->uFirstDesc = pDescriptor->cLines - 1;1221 rc = vmdkDescInitStr(pImage, pDescriptor, "");1222 if (VBOX_FAILURE(rc))1223 goto out;1224 rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");1225 if (VBOX_FAILURE(rc))1226 goto out;1227 rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");1228 if (VBOX_FAILURE(rc))1229 goto out;1230 pDescriptor->uFirstExtent = pDescriptor->cLines - 1;1231 rc = vmdkDescInitStr(pImage, pDescriptor, "");1232 if (VBOX_FAILURE(rc))1233 goto out;1234 /* The trailing space is created by VMware, too. */1235 rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");1236 if (VBOX_FAILURE(rc))1237 goto out;1238 rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");1239 if (VBOX_FAILURE(rc))1240 goto out;1241 rc = vmdkDescInitStr(pImage, pDescriptor, "");1242 if (VBOX_FAILURE(rc))1243 goto out;1244 rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");1245 if (VBOX_FAILURE(rc))1246 goto out;1247 pDescriptor->uFirstDDB = pDescriptor->cLines - 1;1248 1249 /* Now that the framework is in place, use the normal functions to insert1250 * the remaining keys. */1251 char szBuf[9];1252 RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());1253 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,1254 "CID", szBuf);1255 if (VBOX_FAILURE(rc))1256 goto out;1257 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,1258 "parentCID", "ffffffff");1259 if (VBOX_FAILURE(rc))1260 goto out;1261 1262 rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");1263 if (VBOX_FAILURE(rc))1264 goto out;1265 1266 out:1267 return rc;1268 }1269 1270 static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData,1271 size_t cbDescData)1272 {1273 int rc;1274 unsigned cExtents;1275 unsigned uLine;1276 1277 rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData,1278 &pImage->Descriptor);1279 if (VBOX_FAILURE(rc))1280 return rc;1281 1282 /* Check version, must be 1. */1283 uint32_t uVersion;1284 rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);1285 if (VBOX_FAILURE(rc))1286 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);1287 if (uVersion != 1)1288 return vmdkError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);1289 1290 /* Get image creation type and determine image flags. */1291 const char *pszCreateType;1292 rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",1293 &pszCreateType);1294 if (VBOX_FAILURE(rc))1295 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);1296 if ( !strcmp(pszCreateType, "twoGbMaxExtentSparse")1297 || !strcmp(pszCreateType, "twoGbMaxExtentFlat"))1298 pImage->uImageFlags = VD_VMDK_IMAGE_FLAGS_SPLIT_2G;1299 if ( !strcmp(pszCreateType, "partitionedDevice")1300 || !strcmp(pszCreateType, "fullDevice"))1301 pImage->uImageFlags = VD_VMDK_IMAGE_FLAGS_RAWDISK;1302 else1303 pImage->uImageFlags = 0;1304 RTStrFree((char *)(void *)pszCreateType);1305 1306 /* Count the number of extent config entries. */1307 for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;1308 uLine != 0;1309 uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)1310 /* nothing */;1311 1312 if (!pImage->pDescData && cExtents != 1)1313 {1314 /* Monolithic image, must have only one extent (already opened). */1315 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent in '%s'"), pImage->pszFilename);1316 }1317 1318 if (pImage->pDescData)1319 {1320 /* Non-monolithic image, extents need to be allocated. */1321 rc = vmdkCreateExtents(pImage, cExtents);1322 if (VBOX_FAILURE(rc))1323 return rc;1324 }1325 1326 for (unsigned i = 0, uLine = pImage->Descriptor.uFirstExtent;1327 i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])1328 {1329 char *pszLine = pImage->Descriptor.aLines[uLine];1330 1331 /* Access type of the extent. */1332 if (!strncmp(pszLine, "RW", 2))1333 {1334 pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;1335 pszLine += 2;1336 }1337 else if (!strncmp(pszLine, "RDONLY", 6))1338 {1339 pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;1340 pszLine += 6;1341 }1342 else if (!strncmp(pszLine, "NOACCESS", 8))1343 {1344 pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;1345 pszLine += 8;1346 }1347 else1348 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1349 if (*pszLine++ != ' ')1350 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1351 1352 /* Nominal size of the extent. */1353 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,1354 &pImage->pExtents[i].cNominalSectors);1355 if (VBOX_FAILURE(rc))1356 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1357 if (*pszLine++ != ' ')1358 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1359 1360 /* Type of the extent. */1361 #ifdef VBOX_WITH_VMDK_ESX1362 /** @todo Add the ESX extent types. Not necessary for now because1363 * the ESX extent types are only used inside an ESX server. They are1364 * automatically converted if the VMDK is exported. */1365 #endif /* VBOX_WITH_VMDK_ESX */1366 if (!strncmp(pszLine, "SPARSE", 6))1367 {1368 pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;1369 pszLine += 6;1370 }1371 else if (!strncmp(pszLine, "FLAT", 4))1372 {1373 pImage->pExtents[i].enmType = VMDKETYPE_FLAT;1374 pszLine += 4;1375 }1376 else if (!strncmp(pszLine, "ZERO", 4))1377 {1378 pImage->pExtents[i].enmType = VMDKETYPE_ZERO;1379 pszLine += 4;1380 }1381 else1382 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1383 if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)1384 {1385 /* This one has no basename or offset. */1386 if (*pszLine == ' ')1387 pszLine++;1388 if (*pszLine != '\0')1389 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1390 pImage->pExtents[i].pszBasename = NULL;1391 }1392 else1393 {1394 /* All other extent types have basename and optional offset. */1395 if (*pszLine++ != ' ')1396 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1397 1398 /* Basename of the image. Surrounded by quotes. */1399 char *pszBasename;1400 rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);1401 if (VBOX_FAILURE(rc))1402 return rc;1403 pImage->pExtents[i].pszBasename = pszBasename;1404 if (*pszLine == ' ')1405 {1406 pszLine++;1407 if (*pszLine != '\0')1408 {1409 /* Optional offset in extent specified. */1410 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,1411 &pImage->pExtents[i].uSectorOffset);1412 if (VBOX_FAILURE(rc))1413 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1414 }1415 }1416 1417 if (*pszLine != '\0')1418 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);1419 }1420 }1421 1422 /* Determine PCHS geometry (autogenerate if necessary). */1423 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1424 VMDK_DDB_GEO_PCHS_CYLINDERS,1425 &pImage->PCHSGeometry.cCylinders);1426 if (rc == VERR_VDI_VALUE_NOT_FOUND)1427 pImage->PCHSGeometry.cCylinders = 0;1428 else if (VBOX_FAILURE(rc))1429 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);1430 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1431 VMDK_DDB_GEO_PCHS_HEADS,1432 &pImage->PCHSGeometry.cHeads);1433 if (rc == VERR_VDI_VALUE_NOT_FOUND)1434 pImage->PCHSGeometry.cHeads = 0;1435 else if (VBOX_FAILURE(rc))1436 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);1437 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1438 VMDK_DDB_GEO_PCHS_SECTORS,1439 &pImage->PCHSGeometry.cSectors);1440 if (rc == VERR_VDI_VALUE_NOT_FOUND)1441 pImage->PCHSGeometry.cSectors = 0;1442 else if (VBOX_FAILURE(rc))1443 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);1444 if ( pImage->PCHSGeometry.cCylinders == 01445 || pImage->PCHSGeometry.cHeads == 01446 || pImage->PCHSGeometry.cHeads > 161447 || pImage->PCHSGeometry.cSectors == 01448 || pImage->PCHSGeometry.cSectors > 63)1449 {1450 /* Mark PCHS geometry as not yet valid (can't do the calculation here1451 * as the total image size isn't known yet). */1452 pImage->PCHSGeometry.cCylinders = 0;1453 pImage->PCHSGeometry.cHeads = 16;1454 pImage->PCHSGeometry.cSectors = 63;1455 }1456 1457 /* Determine LCHS geometry (set to 0 if not specified). */1458 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1459 VMDK_DDB_GEO_LCHS_CYLINDERS,1460 &pImage->LCHSGeometry.cCylinders);1461 if (rc == VERR_VDI_VALUE_NOT_FOUND)1462 pImage->LCHSGeometry.cCylinders = 0;1463 else if (VBOX_FAILURE(rc))1464 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);1465 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1466 VMDK_DDB_GEO_LCHS_HEADS,1467 &pImage->LCHSGeometry.cHeads);1468 if (rc == VERR_VDI_VALUE_NOT_FOUND)1469 pImage->LCHSGeometry.cHeads = 0;1470 else if (VBOX_FAILURE(rc))1471 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);1472 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,1473 VMDK_DDB_GEO_LCHS_SECTORS,1474 &pImage->LCHSGeometry.cSectors);1475 if (rc == VERR_VDI_VALUE_NOT_FOUND)1476 pImage->LCHSGeometry.cSectors = 0;1477 else if (VBOX_FAILURE(rc))1478 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);1479 if ( pImage->LCHSGeometry.cCylinders == 01480 || pImage->LCHSGeometry.cHeads == 01481 || pImage->LCHSGeometry.cSectors == 0)1482 {1483 pImage->LCHSGeometry.cCylinders = 0;1484 pImage->LCHSGeometry.cHeads = 0;1485 pImage->LCHSGeometry.cSectors = 0;1486 }1487 1488 /* Get image UUID. */1489 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID,1490 &pImage->ImageUuid);1491 if (rc == VERR_VDI_VALUE_NOT_FOUND)1492 {1493 /* Image without UUID. Probably created by VMware and not yet used1494 * by VirtualBox. Can only be added for images opened in read/write1495 * mode, so don't bother producing a sensible UUID otherwise. */1496 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)1497 RTUuidClear(&pImage->ImageUuid);1498 else1499 {1500 rc = RTUuidCreate(&pImage->ImageUuid);1501 if (VBOX_FAILURE(rc))1502 return rc;1503 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,1504 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);1505 if (VBOX_FAILURE(rc))1506 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);1507 }1508 }1509 else if (VBOX_FAILURE(rc))1510 return rc;1511 1512 /* Get image modification UUID. */1513 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,1514 VMDK_DDB_MODIFICATION_UUID,1515 &pImage->ModificationUuid);1516 if (rc == VERR_VDI_VALUE_NOT_FOUND)1517 {1518 /* Image without UUID. Probably created by VMware and not yet used1519 * by VirtualBox. Can only be added for images opened in read/write1520 * mode, so don't bother producing a sensible UUID otherwise. */1521 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)1522 RTUuidClear(&pImage->ModificationUuid);1523 else1524 {1525 rc = RTUuidCreate(&pImage->ModificationUuid);1526 if (VBOX_FAILURE(rc))1527 return rc;1528 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,1529 VMDK_DDB_MODIFICATION_UUID,1530 &pImage->ModificationUuid);1531 if (VBOX_FAILURE(rc))1532 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);1533 }1534 }1535 else if (VBOX_FAILURE(rc))1536 return rc;1537 1538 /* Get UUID of parent image. */1539 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID,1540 &pImage->ParentUuid);1541 if (rc == VERR_VDI_VALUE_NOT_FOUND)1542 {1543 /* Image without UUID. Probably created by VMware and not yet used1544 * by VirtualBox. Can only be added for images opened in read/write1545 * mode, so don't bother producing a sensible UUID otherwise. */1546 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)1547 RTUuidClear(&pImage->ParentUuid);1548 else1549 {1550 rc = RTUuidClear(&pImage->ParentUuid);1551 if (VBOX_FAILURE(rc))1552 return rc;1553 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,1554 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);1555 if (VBOX_FAILURE(rc))1556 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);1557 }1558 }1559 else if (VBOX_FAILURE(rc))1560 return rc;1561 1562 /* Get parent image modification UUID. */1563 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,1564 VMDK_DDB_PARENT_MODIFICATION_UUID,1565 &pImage->ParentModificationUuid);1566 if (rc == VERR_VDI_VALUE_NOT_FOUND)1567 {1568 /* Image without UUID. Probably created by VMware and not yet used1569 * by VirtualBox. Can only be added for images opened in read/write1570 * mode, so don't bother producing a sensible UUID otherwise. */1571 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)1572 RTUuidClear(&pImage->ParentModificationUuid);1573 else1574 {1575 rc = RTUuidCreate(&pImage->ParentModificationUuid);1576 if (VBOX_FAILURE(rc))1577 return rc;1578 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,1579 VMDK_DDB_PARENT_MODIFICATION_UUID,1580 &pImage->ParentModificationUuid);1581 if (VBOX_FAILURE(rc))1582 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in descriptor in '%s'"), pImage->pszFilename);1583 }1584 }1585 else if (VBOX_FAILURE(rc))1586 return rc;1587 1588 return VINF_SUCCESS;1589 }1590 1591 /**1592 * Internal: write/update the descriptor part of the image.1593 */1594 static int vmdkWriteDescriptor(PVMDKIMAGE pImage)1595 {1596 int rc = VINF_SUCCESS;1597 uint64_t cbLimit;1598 uint64_t uOffset;1599 RTFILE DescFile;1600 1601 if (pImage->pDescData)1602 {1603 /* Separate descriptor file. */1604 uOffset = 0;1605 cbLimit = 0;1606 DescFile = pImage->File;1607 }1608 else1609 {1610 /* Embedded descriptor file. */1611 uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);1612 cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);1613 cbLimit += uOffset;1614 DescFile = pImage->pExtents[0].File;1615 }1616 for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)1617 {1618 const char *psz = pImage->Descriptor.aLines[i];1619 size_t cb = strlen(psz);1620 1621 if (cbLimit && uOffset + cb + 1 > cbLimit)1622 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);1623 rc = RTFileWriteAt(DescFile, uOffset, psz, cb, NULL);1624 if (VBOX_FAILURE(rc))1625 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);1626 uOffset += cb;1627 rc = RTFileWriteAt(DescFile, uOffset, "\n", 1, NULL);1628 if (VBOX_FAILURE(rc))1629 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);1630 uOffset++;1631 }1632 if (cbLimit)1633 {1634 /* Inefficient, but simple. */1635 while (uOffset < cbLimit)1636 {1637 rc = RTFileWriteAt(DescFile, uOffset, "", 1, NULL);1638 if (VBOX_FAILURE(rc))1639 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);1640 uOffset++;1641 }1642 }1643 else1644 {1645 rc = RTFileSetSize(DescFile, uOffset);1646 if (VBOX_FAILURE(rc))1647 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);1648 }1649 pImage->Descriptor.fDirty = false;1650 return rc;1651 }1652 1653 /**1654 * Internal: read metadata belonging to a sparse extent.1655 */1656 static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent)1657 {1658 SparseExtentHeader Header;1659 uint64_t cbExtentSize, cSectorsPerGDE;1660 1661 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);1662 AssertRC(rc);1663 if (VBOX_FAILURE(rc))1664 {1665 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);1666 goto out;1667 }1668 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_SPARSE_MAGICNUMBER1669 || RT_LE2H_U32(Header.version) != 1)1670 {1671 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic/version in extent header in '%s'"), pExtent->pszFullname);1672 goto out;1673 }1674 /* The image must be a multiple of a sector in size. If not, it means the1675 * image is at least truncated, or even seriously garbled. */1676 rc = RTFileGetSize(pExtent->File, &cbExtentSize);1677 if (VBOX_FAILURE(rc))1678 {1679 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);1680 goto out;1681 }1682 if ( (RT_LE2H_U32(Header.flags) & 1)1683 && ( Header.singleEndLineChar != '\n'1684 || Header.nonEndLineChar != ' '1685 || Header.doubleEndLineChar1 != '\r'1686 || Header.doubleEndLineChar2 != '\n') )1687 {1688 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);1689 goto out;1690 }1691 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;1692 pExtent->cSectors = RT_LE2H_U64(Header.capacity);1693 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);1694 /* The spec says that this must be a power of two and greater than 8,1695 * but probably they meant not less than 8. */1696 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))1697 || pExtent->cSectorsPerGrain < 8)1698 {1699 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);1700 goto out;1701 }1702 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);1703 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);1704 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)1705 {1706 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);1707 goto out;1708 }1709 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);1710 /* This code requires that a grain table must hold a power of two multiple1711 * of the number of entries per GT cache entry. */1712 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))1713 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)1714 {1715 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);1716 goto out;1717 }1718 if (RT_LE2H_U32(Header.flags) & 2)1719 {1720 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);1721 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);1722 }1723 else1724 {1725 /** @todo this is just guesswork, the spec doesn't document this1726 * properly and I don't have a vmdk without RGD. */1727 pExtent->uSectorGD = RT_LE2H_U64(Header.rgdOffset);1728 pExtent->uSectorRGD = 0;1729 }1730 pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);1731 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;1732 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;1733 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)1734 {1735 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);1736 goto out;1737 }1738 pExtent->cSectorsPerGDE = cSectorsPerGDE;1739 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;1740 1741 rc = vmdkReadGrainDirectory(pExtent);1742 1743 out:1744 if (VBOX_FAILURE(rc))1745 vmdkFreeExtentData(pExtent, false);1746 1747 return rc;1748 }1749 1750 /**1751 * Internal: write/update the metadata for a sparse extent.1752 */1753 static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent)1754 {1755 SparseExtentHeader Header;1756 1757 memset(&Header, '\0', sizeof(Header));1758 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);1759 Header.version = RT_H2LE_U32(1);1760 Header.flags = RT_H2LE_U32(1 | ((pExtent->pRGD) ? 2 : 0));1761 Header.capacity = RT_H2LE_U64(pExtent->cSectors);1762 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);1763 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);1764 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);1765 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);1766 if (pExtent->pRGD)1767 {1768 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);1769 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);1770 }1771 else1772 {1773 /** @todo this is just guesswork, the spec doesn't document this1774 * properly and I don't have a vmdk without RGD. */1775 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorGD);1776 }1777 Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);1778 Header.uncleanShutdown = pExtent->fUncleanShutdown;1779 Header.singleEndLineChar = '\n';1780 Header.nonEndLineChar = ' ';1781 Header.doubleEndLineChar1 = '\r';1782 Header.doubleEndLineChar2 = '\n';1783 1784 int rc = RTFileWriteAt(pExtent->File, 0, &Header, sizeof(Header), NULL);1785 AssertRC(rc);1786 if (VBOX_FAILURE(rc))1787 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);1788 return rc;1789 }1790 1791 #ifdef VBOX_WITH_VMDK_ESX1792 /**1793 * Internal: unused code to read the metadata of a sparse ESX extent.1794 *1795 * Such extents never leave ESX server, so this isn't ever used.1796 */1797 static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)1798 {1799 COWDisk_Header Header;1800 uint64_t cSectorsPerGDE;1801 1802 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);1803 AssertRC(rc);1804 if (VBOX_FAILURE(rc))1805 goto out;1806 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER1807 || RT_LE2H_U32(Header.version) != 11808 || RT_LE2H_U32(Header.flags) != 3)1809 {1810 rc = VERR_VDI_INVALID_HEADER;1811 goto out;1812 }1813 pExtent->enmType = VMDKETYPE_ESX_SPARSE;1814 pExtent->cSectors = RT_LE2H_U32(Header.numSectors);1815 pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);1816 /* The spec says that this must be between 1 sector and 1MB. This code1817 * assumes it's a power of two, so check that requirement, too. */1818 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))1819 || pExtent->cSectorsPerGrain == 01820 || pExtent->cSectorsPerGrain > 2048)1821 {1822 rc = VERR_VDI_INVALID_HEADER;1823 goto out;1824 }1825 pExtent->uDescriptorSector = 0;1826 pExtent->cDescriptorSectors = 0;1827 pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);1828 pExtent->uSectorRGD = 0;1829 pExtent->cOverheadSectors = 0;1830 pExtent->cGTEntries = 4096;1831 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;1832 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)1833 {1834 rc = VERR_VDI_INVALID_HEADER;1835 goto out;1836 }1837 pExtent->cSectorsPerGDE = cSectorsPerGDE;1838 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;1839 if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))1840 {1841 /* Inconsistency detected. Computed number of GD entries doesn't match1842 * stored value. Better be safe than sorry. */1843 rc = VERR_VDI_INVALID_HEADER;1844 goto out;1845 }1846 pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);1847 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;1848 1849 rc = vmdkReadGrainDirectory(pExtent);1850 1851 out:1852 if (VBOX_FAILURE(rc))1853 vmdkFreeExtentData(pExtent, false);1854 1855 return rc;1856 }1857 #endif /* VBOX_WITH_VMDK_ESX */1858 1859 /**1860 * Internal: free the memory used by the extent data structure, optionally1861 * deleting the referenced files.1862 */1863 static void vmdkFreeExtentData(PVMDKEXTENT pExtent, bool fDelete)1864 {1865 vmdkFreeGrainDirectory(pExtent);1866 if (pExtent->pDescData)1867 {1868 RTMemFree(pExtent->pDescData);1869 pExtent->pDescData = NULL;1870 }1871 if (pExtent->File != NIL_RTFILE)1872 {1873 RTFileClose(pExtent->File);1874 pExtent->File = NIL_RTFILE;1875 if ( fDelete1876 && strcmp(pExtent->pszFullname, pExtent->pszBasename) != 01877 && pExtent->pszFullname)1878 RTFileDelete(pExtent->pszFullname);1879 }1880 if (pExtent->pszBasename)1881 {1882 RTMemTmpFree((void *)pExtent->pszBasename);1883 pExtent->pszBasename = NULL;1884 }1885 if (pExtent->pszFullname)1886 {1887 RTStrFree((char *)(void *)pExtent->pszFullname);1888 pExtent->pszFullname = NULL;1889 }1890 }1891 1892 /**1893 * Internal: allocate grain table cache if necessary for this image.1894 */1895 static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)1896 {1897 PVMDKEXTENT pExtent;1898 1899 /* Allocate grain table cache if any sparse extent is present. */1900 for (unsigned i = 0; i < pImage->cExtents; i++)1901 {1902 pExtent = &pImage->pExtents[i];1903 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE1904 #ifdef VBOX_WITH_VMDK_ESX1905 || pExtent->enmType == VMDKETYPE_ESX_SPARSE1906 #endif /* VBOX_WITH_VMDK_ESX */1907 )1908 {1909 /* Allocate grain table cache. */1910 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));1911 if (!pImage->pGTCache)1912 return VERR_NO_MEMORY;1913 for (unsigned i = 0; i < VMDK_GT_CACHE_SIZE; i++)1914 {1915 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[i];1916 pGCE->uExtent = UINT32_MAX;1917 }1918 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;1919 break;1920 }1921 }1922 1923 return VINF_SUCCESS;1924 }1925 1926 /**1927 * Internal: allocate the given number of extents.1928 */1929 static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)1930 {1931 int rc = VINF_SUCCESS;1932 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));1933 if (pImage)1934 {1935 for (unsigned i = 0; i < cExtents; i++)1936 {1937 pExtents[i].File = NIL_RTFILE;1938 pExtents[i].pszBasename = NULL;1939 pExtents[i].pszFullname = NULL;1940 pExtents[i].pGD = NULL;1941 pExtents[i].pRGD = NULL;1942 pExtents[i].pDescData = NULL;1943 pExtents[i].uExtent = i;1944 pExtents[i].pImage = pImage;1945 }1946 pImage->pExtents = pExtents;1947 pImage->cExtents = cExtents;1948 }1949 else1950 rc = VERR_NO_MEMORY;1951 1952 return rc;1953 }1954 1955 /**1956 90 * Internal: Open an image, constructing all necessary data structures. 1957 91 */ 1958 static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags) 1959 { 1960 int rc; 1961 uint32_t u32Magic; 92 static int rawOpenImage(PRAWIMAGE pImage, unsigned uOpenFlags) 93 { 94 int rc; 1962 95 RTFILE File; 1963 PVMDKEXTENT pExtent;1964 96 1965 97 pImage->uOpenFlags = uOpenFlags; 1966 1967 /** @todo check whether the same file is used somewhere else. don't open any file twice, leads to locking problems and can cause trouble with file caching. */1968 98 1969 99 /* … … 1982 112 pImage->File = File; 1983 113 1984 /* Read magic (if present). */ 1985 rc = RTFileReadAt(File, 0, &u32Magic, sizeof(u32Magic), NULL); 114 rc = RTFileGetSize(pImage->File, &pImage->cbSize); 1986 115 if (VBOX_FAILURE(rc)) 1987 { 1988 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename); 1989 goto out; 1990 } 1991 1992 /* Handle the file according to its magic number. */ 1993 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER) 1994 { 1995 /* It's a hosted sparse single-extent image. */ 1996 rc = vmdkCreateExtents(pImage, 1); 116 goto out; 117 if (pImage->cbSize % 512) 118 { 119 rc = VERR_VDI_INVALID_HEADER; 120 goto out; 121 } 122 pImage->enmImageType = VD_IMAGE_TYPE_FIXED; 123 124 out: 125 if (VBOX_FAILURE(rc)) 126 rawFreeImage(pImage, false); 127 return rc; 128 } 129 130 /** 131 * Internal: Create a raw image. 132 */ 133 static int rawCreateImage(PRAWIMAGE pImage, VDIMAGETYPE enmType, 134 uint64_t cbSize, unsigned uImageFlags, 135 const char *pszComment, 136 PCPDMMEDIAGEOMETRY pPCHSGeometry, 137 PCPDMMEDIAGEOMETRY pLCHSGeometry, 138 PFNVMPROGRESS pfnProgress, void *pvUser, 139 unsigned uPercentStart, unsigned uPercentSpan) 140 { 141 int rc; 142 RTFILE File; 143 RTFOFF cbFree = 0; 144 uint64_t uOff; 145 size_t cbBuf = 128 * _1K; 146 void *pvBuf = NULL; 147 148 if (enmType != VD_IMAGE_TYPE_FIXED) 149 { 150 rc = rawError(pImage, VERR_VDI_INVALID_TYPE, RT_SRC_POS, N_("Raw: cannot create diff image '%s'"), pImage->pszFilename); 151 goto out; 152 } 153 154 pImage->enmImageType = enmType; 155 pImage->uImageFlags = uImageFlags; 156 pImage->PCHSGeometry = *pPCHSGeometry; 157 pImage->LCHSGeometry = *pLCHSGeometry; 158 159 /* Create image file. */ 160 rc = RTFileOpen(&File, pImage->pszFilename, 161 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL); 162 if (VBOX_FAILURE(rc)) 163 { 164 rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: cannot create image '%s'"), pImage->pszFilename); 165 goto out; 166 } 167 pImage->File = File; 168 169 /* Check the free space on the disk and leave early if there is not 170 * sufficient space available. */ 171 rc = RTFsQuerySizes(pImage->pszFilename, NULL, &cbFree, NULL, NULL); 172 if (VBOX_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbSize)) 173 { 174 rc = rawError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("Raw: disk would overflow creating image '%s'"), pImage->pszFilename); 175 goto out; 176 } 177 178 /* Allocate & commit whole file if fixed image, it must be more 179 * effective than expanding file by write operations. */ 180 rc = RTFileSetSize(File, cbSize); 181 if (VBOX_FAILURE(rc)) 182 { 183 rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: setting image size failed for '%s'"), pImage->pszFilename); 184 goto out; 185 } 186 187 /* Fill image with zeroes. We do this for every fixed-size image since on 188 * some systems (for example Windows Vista), it takes ages to write a block 189 * near the end of a sparse file and the guest could complain about an ATA 190 * timeout. */ 191 pvBuf = RTMemTmpAllocZ(cbBuf); 192 if (!pvBuf) 193 { 194 rc = VERR_NO_MEMORY; 195 goto out; 196 } 197 198 uOff = 0; 199 /* Write data to all image blocks. */ 200 while (uOff < cbSize) 201 { 202 unsigned cbChunk = (unsigned)RT_MIN(cbSize, cbBuf); 203 204 rc = RTFileWriteAt(File, uOff, pvBuf, cbChunk, NULL); 1997 205 if (VBOX_FAILURE(rc)) 1998 goto out;1999 /* The opened file is passed to the extent. No separate descriptor2000 * file, so no need to keep anything open for the image. */2001 pExtent = &pImage->pExtents[0];2002 pExtent->File = File;2003 pImage->File = NIL_RTFILE;2004 rc = vmdkReadMetaSparseExtent(pExtent);2005 if (VBOX_FAILURE(rc))2006 goto out;2007 /* As we're dealing with a monolithic sparse image here, there must2008 * be a descriptor embedded in the image file. */2009 if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)2010 206 { 2011 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in'%s'"), pImage->pszFilename);207 rc = rawError(pImage, rc, RT_SRC_POS, N_("Raw: writing block failed for '%s'"), pImage->pszFilename); 2012 208 goto out; 2013 209 } 2014 /* Read the descriptor from the extent. */ 2015 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors)); 2016 if (!pExtent->pDescData) 210 211 uOff += cbChunk; 212 213 if (pfnProgress) 2017 214 { 2018 rc = VERR_NO_MEMORY; 2019 goto out; 215 rc = pfnProgress(NULL /* WARNING! pVM=NULL */, 216 uPercentStart + uOff * uPercentSpan * 98 / (cbSize * 100), 217 pvUser); 218 if (VBOX_FAILURE(rc)) 219 goto out; 2020 220 } 2021 rc = RTFileReadAt(pExtent->File, 2022 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector), 2023 pExtent->pDescData, 2024 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL); 2025 AssertRC(rc); 2026 if (VBOX_FAILURE(rc)) 2027 { 2028 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname); 2029 goto out; 2030 } 2031 2032 rc = vmdkParseDescriptor(pImage, pExtent->pDescData, 2033 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors)); 2034 if (VBOX_FAILURE(rc)) 2035 goto out; 2036 2037 /* Mark the extent as unclean if opened in read-write mode. */ 2038 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)) 2039 { 2040 pExtent->fUncleanShutdown = true; 2041 pExtent->fMetaDirty = true; 2042 } 2043 } 2044 else 2045 { 2046 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20); 2047 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc); 2048 if (!pImage->pDescData) 2049 { 2050 rc = VERR_NO_MEMORY; 2051 goto out; 2052 } 2053 2054 size_t cbRead; 2055 rc = RTFileReadAt(pImage->File, 0, pImage->pDescData, 2056 pImage->cbDescAlloc, &cbRead); 2057 if (VBOX_FAILURE(rc)) 2058 { 2059 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename); 2060 goto out; 2061 } 2062 if (cbRead == pImage->cbDescAlloc) 2063 { 2064 /* Likely the read is truncated. Better fail a bit too early 2065 * (normally the descriptor is much smaller than our buffer). */ 2066 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename); 2067 goto out; 2068 } 2069 2070 rc = vmdkParseDescriptor(pImage, pImage->pDescData, 2071 pImage->cbDescAlloc); 2072 if (VBOX_FAILURE(rc)) 2073 goto out; 2074 2075 for (unsigned i = 0; i < pImage->cExtents; i++) 2076 { 2077 PVMDKEXTENT pExtent = &pImage->pExtents[i]; 2078 2079 if (pExtent->pszBasename) 2080 { 2081 /* Hack to figure out whether the specified name in the 2082 * extent descriptor is absolute. Doesn't always work, but 2083 * should be good enough for now. */ 2084 char *pszFullname; 2085 /** @todo implement proper path absolute check. */ 2086 if (pExtent->pszBasename[0] == RTPATH_SLASH) 2087 { 2088 pszFullname = RTStrDup(pExtent->pszBasename); 2089 if (!pszFullname) 2090 { 2091 rc = VERR_NO_MEMORY; 2092 goto out; 2093 } 2094 } 2095 else 2096 { 2097 size_t cbDirname; 2098 char *pszDirname = RTStrDup(pImage->pszFilename); 2099 if (!pszDirname) 2100 { 2101 rc = VERR_NO_MEMORY; 2102 goto out; 2103 } 2104 RTPathStripFilename(pszDirname); 2105 cbDirname = strlen(pszDirname); 2106 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname, 2107 RTPATH_SLASH, pExtent->pszBasename); 2108 RTStrFree(pszDirname); 2109 if (VBOX_FAILURE(rc)) 2110 goto out; 2111 } 2112 pExtent->pszFullname = pszFullname; 2113 } 2114 else 2115 pExtent->pszFullname = NULL; 2116 2117 switch (pExtent->enmType) 2118 { 2119 case VMDKETYPE_HOSTED_SPARSE: 2120 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2121 uOpenFlags & VD_OPEN_FLAGS_READONLY 2122 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE 2123 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); 2124 if (VBOX_FAILURE(rc)) 2125 { 2126 /* Do NOT signal an appropriate error here, as the VD 2127 * layer has the choice of retrying the open if it 2128 * failed. */ 2129 goto out; 2130 } 2131 rc = vmdkReadMetaSparseExtent(pExtent); 2132 if (VBOX_FAILURE(rc)) 2133 goto out; 2134 2135 /* Mark extent as unclean if opened in read-write mode. */ 2136 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)) 2137 { 2138 pExtent->fUncleanShutdown = true; 2139 pExtent->fMetaDirty = true; 2140 } 2141 break; 2142 case VMDKETYPE_FLAT: 2143 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2144 uOpenFlags & VD_OPEN_FLAGS_READONLY 2145 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE 2146 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); 2147 if (VBOX_FAILURE(rc)) 2148 { 2149 /* Do NOT signal an appropriate error here, as the VD 2150 * layer has the choice of retrying the open if it 2151 * failed. */ 2152 goto out; 2153 } 2154 break; 2155 case VMDKETYPE_ZERO: 2156 /* Nothing to do. */ 2157 break; 2158 default: 2159 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType)); 2160 } 2161 } 2162 } 2163 2164 /* Make sure this is not reached accidentally with an error status. */ 2165 AssertRC(rc); 2166 2167 /* Determine PCHS geometry if not set. */ 2168 if (pImage->PCHSGeometry.cCylinders == 0) 2169 { 2170 uint64_t cCylinders = VMDK_BYTE2SECTOR(pImage->cbSize) 2171 / pImage->PCHSGeometry.cHeads 2172 / pImage->PCHSGeometry.cSectors; 2173 pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383); 2174 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 2175 { 2176 rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry); 2177 AssertRC(rc); 2178 } 2179 } 2180 2181 /* Update the image metadata now in case has changed. */ 2182 rc = vmdkFlushImage(pImage); 2183 if (VBOX_FAILURE(rc)) 2184 goto out; 2185 2186 /* Figure out a few per-image constants from the extents. */ 2187 pImage->cbSize = 0; 2188 for (unsigned i = 0; i < pImage->cExtents; i++) 2189 { 2190 pExtent = &pImage->pExtents[i]; 2191 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE 2192 #ifdef VBOX_WITH_VMDK_ESX 2193 || pExtent->enmType == VMDKETYPE_ESX_SPARSE 2194 #endif /* VBOX_WITH_VMDK_ESX */ 2195 ) 2196 { 2197 /* Here used to be a check whether the nominal size of an extent 2198 * is a multiple of the grain size. The spec says that this is 2199 * always the case, but unfortunately some files out there in the 2200 * wild violate the spec (e.g. ReactOS 0.3.1). */ 2201 } 2202 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors); 2203 } 2204 2205 pImage->enmImageType = VD_IMAGE_TYPE_NORMAL; 2206 for (unsigned i = 0; i < pImage->cExtents; i++) 2207 { 2208 pExtent = &pImage->pExtents[i]; 2209 if ( pImage->pExtents[i].enmType == VMDKETYPE_FLAT 2210 || pImage->pExtents[i].enmType == VMDKETYPE_ZERO) 2211 { 2212 pImage->enmImageType = VD_IMAGE_TYPE_FIXED; 2213 break; 2214 } 2215 } 2216 2217 rc = vmdkAllocateGrainTableCache(pImage); 2218 if (VBOX_FAILURE(rc)) 2219 goto out; 2220 2221 out: 2222 if (VBOX_FAILURE(rc)) 2223 vmdkFreeImage(pImage, false); 2224 return rc; 2225 } 2226 2227 /** 2228 * Internal: create VMDK images for raw disk/partition access. 2229 */ 2230 static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw, 2231 uint64_t cbSize) 2232 { 2233 int rc = VINF_SUCCESS; 2234 PVMDKEXTENT pExtent; 2235 2236 if (pRaw->fRawDisk) 2237 { 2238 /* Full raw disk access. This requires setting up a descriptor 2239 * file and open the (flat) raw disk. */ 2240 rc = vmdkCreateExtents(pImage, 1); 2241 if (VBOX_FAILURE(rc)) 2242 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename); 2243 pExtent = &pImage->pExtents[0]; 2244 /* Create raw disk descriptor file. */ 2245 rc = RTFileOpen(&pImage->File, pImage->pszFilename, 2246 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED); 2247 if (VBOX_FAILURE(rc)) 2248 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename); 2249 2250 /* Set up basename for extent description. Cannot use StrDup. */ 2251 size_t cbBasename = strlen(pRaw->pszRawDisk) + 1; 2252 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename); 2253 if (!pszBasename) 2254 return VERR_NO_MEMORY; 2255 memcpy(pszBasename, pRaw->pszRawDisk, cbBasename); 2256 pExtent->pszBasename = pszBasename; 2257 /* For raw disks the full name is identical to the base name. */ 2258 pExtent->pszFullname = RTStrDup(pszBasename); 2259 if (!pExtent->pszFullname) 2260 return VERR_NO_MEMORY; 2261 pExtent->enmType = VMDKETYPE_FLAT; 2262 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize); 2263 pExtent->uSectorOffset = 0; 2264 pExtent->enmAccess = VMDKACCESS_READWRITE; 2265 pExtent->fMetaDirty = false; 2266 2267 /* Open flat image, the raw disk. */ 2268 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2269 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); 2270 if (VBOX_FAILURE(rc)) 2271 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname); 2272 } 2273 else 2274 { 2275 /* Raw partition access. This requires setting up a descriptor 2276 * file, write the partition information to a flat extent and 2277 * open all the (flat) raw disk partitions. */ 2278 2279 /* First pass over the partitions to determine how many 2280 * extents we need. One partition can require up to 4 extents. 2281 * One to skip over unpartitioned space, one for the 2282 * partitioning data, one to skip over unpartitioned space 2283 * and one for the partition data. */ 2284 unsigned cExtents = 0; 2285 uint64_t uStart = 0; 2286 for (unsigned i = 0; i < pRaw->cPartitions; i++) 2287 { 2288 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i]; 2289 if (pPart->cbPartitionData) 2290 { 2291 if (uStart > pPart->uPartitionDataStart) 2292 return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partitioning information in '%s'"), pImage->pszFilename); 2293 else if (uStart != pPart->uPartitionDataStart) 2294 cExtents++; 2295 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData; 2296 cExtents++; 2297 } 2298 if (pPart->cbPartition) 2299 { 2300 if (uStart > pPart->uPartitionStart) 2301 return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partition data in '%s'"), pImage->pszFilename); 2302 else if (uStart != pPart->uPartitionStart) 2303 cExtents++; 2304 uStart = pPart->uPartitionStart + pPart->cbPartition; 2305 cExtents++; 2306 } 2307 } 2308 /* Another extent for filling up the rest of the image. */ 2309 if (uStart != cbSize) 2310 cExtents++; 2311 2312 rc = vmdkCreateExtents(pImage, cExtents); 2313 if (VBOX_FAILURE(rc)) 2314 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename); 2315 2316 /* Create raw partition descriptor file. */ 2317 rc = RTFileOpen(&pImage->File, pImage->pszFilename, 2318 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED); 2319 if (VBOX_FAILURE(rc)) 2320 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename); 2321 2322 /* Create base filename for the partition table extent. */ 2323 /** @todo remove fixed buffer without creating memory leaks. */ 2324 char pszPartition[1024]; 2325 const char *pszBase = RTPathFilename(pImage->pszFilename); 2326 const char *pszExt = RTPathExt(pszBase); 2327 if (pszExt == NULL) 2328 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename); 2329 char *pszBaseBase = RTStrDup(pszBase); 2330 if (!pszBaseBase) 2331 return VERR_NO_MEMORY; 2332 RTPathStripExt(pszBaseBase); 2333 RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s", 2334 pszBaseBase, pszExt); 2335 RTStrFree(pszBaseBase); 2336 2337 /* Second pass over the partitions, now define all extents. */ 2338 uint64_t uPartOffset = 0; 2339 cExtents = 0; 2340 uStart = 0; 2341 for (unsigned i = 0; i < pRaw->cPartitions; i++) 2342 { 2343 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i]; 2344 if (pPart->cbPartitionData) 2345 { 2346 if (uStart != pPart->uPartitionDataStart) 2347 { 2348 pExtent = &pImage->pExtents[cExtents++]; 2349 pExtent->pszBasename = NULL; 2350 pExtent->pszFullname = NULL; 2351 pExtent->enmType = VMDKETYPE_ZERO; 2352 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionDataStart - uStart); 2353 pExtent->uSectorOffset = 0; 2354 pExtent->enmAccess = VMDKACCESS_READWRITE; 2355 pExtent->fMetaDirty = false; 2356 } 2357 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData; 2358 pExtent = &pImage->pExtents[cExtents++]; 2359 /* Set up basename for extent description. Can't use StrDup. */ 2360 size_t cbBasename = strlen(pszPartition) + 1; 2361 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename); 2362 if (!pszBasename) 2363 return VERR_NO_MEMORY; 2364 memcpy(pszBasename, pszPartition, cbBasename); 2365 pExtent->pszBasename = pszBasename; 2366 2367 /* Set up full name for partition extent. */ 2368 size_t cbDirname; 2369 char *pszDirname = RTStrDup(pImage->pszFilename); 2370 if (!pszDirname) 2371 return VERR_NO_MEMORY; 2372 RTPathStripFilename(pszDirname); 2373 cbDirname = strlen(pszDirname); 2374 char *pszFullname; 2375 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname, 2376 RTPATH_SLASH, pExtent->pszBasename); 2377 RTStrFree(pszDirname); 2378 if (VBOX_FAILURE(rc)) 2379 return rc; 2380 pExtent->pszFullname = pszFullname; 2381 pExtent->enmType = VMDKETYPE_FLAT; 2382 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartitionData); 2383 pExtent->uSectorOffset = uPartOffset; 2384 pExtent->enmAccess = VMDKACCESS_READWRITE; 2385 pExtent->fMetaDirty = false; 2386 2387 /* Create partition table flat image. */ 2388 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2389 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED); 2390 if (VBOX_FAILURE(rc)) 2391 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname); 2392 rc = RTFileWriteAt(pExtent->File, 2393 VMDK_SECTOR2BYTE(uPartOffset), 2394 pPart->pvPartitionData, 2395 pPart->cbPartitionData, NULL); 2396 if (VBOX_FAILURE(rc)) 2397 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname); 2398 uPartOffset += VMDK_BYTE2SECTOR(pPart->cbPartitionData); 2399 } 2400 if (pPart->cbPartition) 2401 { 2402 if (uStart != pPart->uPartitionStart) 2403 { 2404 pExtent = &pImage->pExtents[cExtents++]; 2405 pExtent->pszBasename = NULL; 2406 pExtent->pszFullname = NULL; 2407 pExtent->enmType = VMDKETYPE_ZERO; 2408 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionStart - uStart); 2409 pExtent->uSectorOffset = 0; 2410 pExtent->enmAccess = VMDKACCESS_READWRITE; 2411 pExtent->fMetaDirty = false; 2412 } 2413 uStart = pPart->uPartitionStart + pPart->cbPartition; 2414 pExtent = &pImage->pExtents[cExtents++]; 2415 if (pPart->pszRawDevice) 2416 { 2417 /* Set up basename for extent descr. Can't use StrDup. */ 2418 size_t cbBasename = strlen(pPart->pszRawDevice) + 1; 2419 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename); 2420 if (!pszBasename) 2421 return VERR_NO_MEMORY; 2422 memcpy(pszBasename, pPart->pszRawDevice, cbBasename); 2423 pExtent->pszBasename = pszBasename; 2424 /* For raw disks full name is identical to base name. */ 2425 pExtent->pszFullname = RTStrDup(pszBasename); 2426 if (!pExtent->pszFullname) 2427 return VERR_NO_MEMORY; 2428 pExtent->enmType = VMDKETYPE_FLAT; 2429 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition); 2430 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uPartitionStartOffset); 2431 pExtent->enmAccess = VMDKACCESS_READWRITE; 2432 pExtent->fMetaDirty = false; 2433 2434 /* Open flat image, the raw partition. */ 2435 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2436 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); 2437 if (VBOX_FAILURE(rc)) 2438 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname); 2439 } 2440 else 2441 { 2442 pExtent->pszBasename = NULL; 2443 pExtent->pszFullname = NULL; 2444 pExtent->enmType = VMDKETYPE_ZERO; 2445 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition); 2446 pExtent->uSectorOffset = 0; 2447 pExtent->enmAccess = VMDKACCESS_READWRITE; 2448 pExtent->fMetaDirty = false; 2449 } 2450 } 2451 } 2452 /* Another extent for filling up the rest of the image. */ 2453 if (uStart != cbSize) 2454 { 2455 pExtent = &pImage->pExtents[cExtents++]; 2456 pExtent->pszBasename = NULL; 2457 pExtent->pszFullname = NULL; 2458 pExtent->enmType = VMDKETYPE_ZERO; 2459 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart); 2460 pExtent->uSectorOffset = 0; 2461 pExtent->enmAccess = VMDKACCESS_READWRITE; 2462 pExtent->fMetaDirty = false; 2463 } 2464 } 2465 2466 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", 2467 pRaw->fRawDisk ? 2468 "fullDevice" : "partitionedDevice"); 2469 if (VBOX_FAILURE(rc)) 2470 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename); 2471 return rc; 2472 } 2473 2474 /** 2475 * Internal: create a regular (i.e. file-backed) VMDK image. 2476 */ 2477 static int vmdkCreateRegularImage(PVMDKIMAGE pImage, VDIMAGETYPE enmType, 2478 uint64_t cbSize, unsigned uImageFlags, 2479 PFNVMPROGRESS pfnProgress, void *pvUser, 2480 unsigned uPercentStart, unsigned uPercentSpan) 2481 { 2482 int rc = VINF_SUCCESS; 2483 unsigned cExtents = 1; 2484 uint64_t cbOffset = 0; 2485 uint64_t cbRemaining = cbSize; 2486 2487 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G) 2488 { 2489 cExtents = cbSize / VMDK_2G_SPLIT_SIZE; 2490 /* Do proper extent computation: need one smaller extent if the total 2491 * size isn't evenly divisible by the split size. */ 2492 if (cbSize % VMDK_2G_SPLIT_SIZE) 2493 cExtents++; 2494 } 2495 rc = vmdkCreateExtents(pImage, cExtents); 2496 if (VBOX_FAILURE(rc)) 2497 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename); 2498 2499 /* Basename strings needed for constructing the extent names. */ 2500 char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename); 2501 Assert(pszBasenameSubstr); 2502 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1; 2503 2504 /* Create searate descriptor file if necessary. */ 2505 if (cExtents != 1 || enmType == VD_IMAGE_TYPE_FIXED) 2506 { 2507 rc = RTFileOpen(&pImage->File, pImage->pszFilename, 2508 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED); 2509 if (VBOX_FAILURE(rc)) 2510 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename); 2511 pImage->pszFilename = RTStrDup(pImage->pszFilename); 2512 } 2513 else 2514 pImage->File = NIL_RTFILE; 2515 2516 /* Set up all extents. */ 2517 for (unsigned i = 0; i < cExtents; i++) 2518 { 2519 PVMDKEXTENT pExtent = &pImage->pExtents[i]; 2520 uint64_t cbExtent = cbRemaining; 2521 2522 /* Set up fullname/basename for extent description. Cannot use StrDup 2523 * for basename, as it is not guaranteed that the memory can be freed 2524 * with RTMemTmpFree, which must be used as in other code paths 2525 * StrDup is not usable. */ 2526 if (cExtents == 1 && enmType != VD_IMAGE_TYPE_FIXED) 2527 { 2528 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr); 2529 if (!pszBasename) 2530 return VERR_NO_MEMORY; 2531 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr); 2532 pExtent->pszBasename = pszBasename; 2533 } 2534 else 2535 { 2536 char *pszBasenameExt = RTPathExt(pszBasenameSubstr); 2537 char *pszBasenameBase = RTStrDup(pszBasenameSubstr); 2538 RTPathStripExt(pszBasenameBase); 2539 char *pszTmp; 2540 size_t cbTmp; 2541 if (enmType == VD_IMAGE_TYPE_FIXED) 2542 { 2543 if (cExtents == 1) 2544 rc = RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase, 2545 pszBasenameExt); 2546 else 2547 rc = RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase, 2548 i+1, pszBasenameExt); 2549 } 2550 else 2551 rc = RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1, 2552 pszBasenameExt); 2553 RTStrFree(pszBasenameBase); 2554 if (VBOX_FAILURE(rc)) 2555 return rc; 2556 cbTmp = strlen(pszTmp) + 1; 2557 char *pszBasename = (char *)RTMemTmpAlloc(cbTmp); 2558 if (!pszBasename) 2559 return VERR_NO_MEMORY; 2560 memcpy(pszBasename, pszTmp, cbTmp); 2561 RTStrFree(pszTmp); 2562 pExtent->pszBasename = pszBasename; 2563 cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE); 2564 } 2565 char *pszBasedirectory = RTStrDup(pImage->pszFilename); 2566 RTPathStripFilename(pszBasedirectory); 2567 char *pszFN; 2568 rc = RTStrAPrintf(&pszFN, "%s%c%s", pszBasedirectory, RTPATH_SLASH, 2569 pExtent->pszBasename); 2570 RTStrFree(pszBasedirectory); 2571 if (VBOX_FAILURE(rc)) 2572 return rc; 2573 pExtent->pszFullname = pszFN; 2574 2575 /* Create file for extent. */ 2576 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname, 2577 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED); 2578 if (VBOX_FAILURE(rc)) 2579 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname); 2580 if (enmType == VD_IMAGE_TYPE_FIXED) 2581 { 2582 rc = RTFileSetSize(pExtent->File, cbExtent); 2583 if (VBOX_FAILURE(rc)) 2584 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname); 2585 } 2586 2587 /* Place descriptor file information (where integrated). */ 2588 if (cExtents == 1 && enmType != VD_IMAGE_TYPE_FIXED) 2589 { 2590 pExtent->uDescriptorSector = 1; 2591 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc); 2592 /* The descriptor is part of the (only) extent. */ 2593 pExtent->pDescData = pImage->pDescData; 2594 pImage->pDescData = NULL; 2595 } 2596 2597 if (enmType == VD_IMAGE_TYPE_NORMAL) 2598 { 2599 uint64_t cSectorsPerGDE, cSectorsPerGD; 2600 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; 2601 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, 65536)); 2602 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(65536); 2603 pExtent->cGTEntries = 512; 2604 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain; 2605 pExtent->cSectorsPerGDE = cSectorsPerGDE; 2606 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE; 2607 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t)); 2608 } 2609 else 2610 pExtent->enmType = VMDKETYPE_FLAT; 2611 2612 pExtent->enmAccess = VMDKACCESS_READWRITE; 2613 pExtent->fUncleanShutdown = true; 2614 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent); 2615 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(cbOffset); 2616 pExtent->fMetaDirty = true; 2617 2618 if (enmType == VD_IMAGE_TYPE_NORMAL) 2619 { 2620 rc = vmdkCreateGrainDirectory(pExtent, 2621 pExtent->uDescriptorSector 2622 + pExtent->cDescriptorSectors, 2623 true); 2624 if (VBOX_FAILURE(rc)) 2625 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname); 2626 } 2627 2628 if (VBOX_SUCCESS(rc) && pfnProgress) 2629 pfnProgress(NULL /* WARNING! pVM=NULL */, 2630 uPercentStart + i * uPercentSpan / cExtents, 2631 pvUser); 2632 2633 cbRemaining -= cbExtent; 2634 cbOffset += cbExtent; 2635 } 2636 2637 const char *pszDescType = NULL; 2638 if (enmType == VD_IMAGE_TYPE_FIXED) 2639 { 2640 pszDescType = (cExtents == 1) 2641 ? "monolithicFlat" : "twoGbMaxExtentFlat"; 2642 } 2643 else if (enmType == VD_IMAGE_TYPE_NORMAL) 2644 { 2645 pszDescType = (cExtents == 1) 2646 ? "monolithicSparse" : "twoGbMaxExtentSparse"; 2647 } 2648 else 2649 AssertMsgFailed(("invalid image type %d\n", enmType)); 2650 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", 2651 pszDescType); 2652 if (VBOX_FAILURE(rc)) 2653 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename); 2654 return rc; 2655 } 2656 2657 /** 2658 * Internal: The actual code for creating any VMDK variant currently in 2659 * existence on hosted environments. 2660 */ 2661 static int vmdkCreateImage(PVMDKIMAGE pImage, VDIMAGETYPE enmType, 2662 uint64_t cbSize, unsigned uImageFlags, 2663 const char *pszComment, 2664 PCPDMMEDIAGEOMETRY pPCHSGeometry, 2665 PCPDMMEDIAGEOMETRY pLCHSGeometry, 2666 PFNVMPROGRESS pfnProgress, void *pvUser, 2667 unsigned uPercentStart, unsigned uPercentSpan) 2668 { 2669 int rc; 2670 2671 pImage->uImageFlags = uImageFlags; 2672 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc, 2673 &pImage->Descriptor); 2674 if (VBOX_FAILURE(rc)) 2675 { 2676 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename); 2677 goto out; 2678 } 2679 2680 if ( enmType == VD_IMAGE_TYPE_FIXED 2681 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)) 2682 { 2683 /* Raw disk image (includes raw partition). */ 2684 const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment; 2685 /* As the comment is misused, zap it so that no garbage comment 2686 * is set below. */ 2687 pszComment = NULL; 2688 rc = vmdkCreateRawImage(pImage, pRaw, cbSize); 2689 } 2690 else if ( enmType == VD_IMAGE_TYPE_FIXED 2691 || enmType == VD_IMAGE_TYPE_NORMAL) 2692 { 2693 /* Regular fixed or sparse image (monolithic or split). */ 2694 rc = vmdkCreateRegularImage(pImage, enmType, cbSize, uImageFlags, 2695 pfnProgress, pvUser, uPercentStart, 2696 uPercentSpan * 95 / 100); 2697 } 2698 else 2699 { 2700 /* Unknown/invalid image type. */ 2701 rc = VERR_NOT_IMPLEMENTED; 2702 } 2703 2704 if (VBOX_FAILURE(rc)) 2705 goto out; 221 } 222 RTMemTmpFree(pvBuf); 2706 223 2707 224 if (VBOX_SUCCESS(rc) && pfnProgress) … … 2712 229 pImage->cbSize = cbSize; 2713 230 2714 for (unsigned i = 0; i < pImage->cExtents; i++) 2715 { 2716 PVMDKEXTENT pExtent = &pImage->pExtents[i]; 2717 2718 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess, 2719 pExtent->cNominalSectors, pExtent->enmType, 2720 pExtent->pszBasename, pExtent->uSectorOffset); 2721 if (VBOX_FAILURE(rc)) 2722 { 2723 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename); 2724 goto out; 2725 } 2726 } 2727 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor); 2728 2729 if ( pPCHSGeometry->cCylinders == 0 2730 || pPCHSGeometry->cHeads == 0 2731 || pPCHSGeometry->cSectors == 0) 2732 { 2733 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry); 2734 if (VBOX_FAILURE(rc)) 2735 goto out; 2736 } 2737 if ( pLCHSGeometry->cCylinders == 0 2738 || pLCHSGeometry->cHeads == 0 2739 || pLCHSGeometry->cSectors == 0) 2740 { 2741 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry); 2742 if (VBOX_FAILURE(rc)) 2743 goto out; 2744 } 2745 2746 pImage->LCHSGeometry = *pLCHSGeometry; 2747 pImage->PCHSGeometry = *pPCHSGeometry; 2748 2749 rc = RTUuidCreate(&pImage->ImageUuid); 2750 if (VBOX_FAILURE(rc)) 2751 goto out; 2752 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 2753 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid); 2754 if (VBOX_FAILURE(rc)) 2755 { 2756 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename); 2757 goto out; 2758 } 2759 RTUuidClear(&pImage->ParentUuid); 2760 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 2761 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid); 2762 if (VBOX_FAILURE(rc)) 2763 { 2764 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename); 2765 goto out; 2766 } 2767 RTUuidClear(&pImage->ModificationUuid); 2768 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 2769 VMDK_DDB_MODIFICATION_UUID, 2770 &pImage->ModificationUuid); 2771 if (VBOX_FAILURE(rc)) 2772 { 2773 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename); 2774 goto out; 2775 } 2776 RTUuidClear(&pImage->ParentModificationUuid); 2777 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 2778 VMDK_DDB_PARENT_MODIFICATION_UUID, 2779 &pImage->ParentModificationUuid); 2780 if (VBOX_FAILURE(rc)) 2781 { 2782 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename); 2783 goto out; 2784 } 2785 2786 rc = vmdkAllocateGrainTableCache(pImage); 2787 if (VBOX_FAILURE(rc)) 2788 goto out; 2789 2790 rc = vmdkSetImageComment(pImage, pszComment); 2791 if (VBOX_FAILURE(rc)) 2792 { 2793 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename); 2794 goto out; 2795 } 2796 2797 if (VBOX_SUCCESS(rc) && pfnProgress) 2798 pfnProgress(NULL /* WARNING! pVM=NULL */, 2799 uPercentStart + uPercentSpan * 99 / 100, pvUser); 2800 2801 rc = vmdkFlushImage(pImage); 231 rc = rawFlushImage(pImage); 2802 232 2803 233 out: … … 2807 237 2808 238 if (VBOX_FAILURE(rc)) 2809 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS); 2810 return rc; 2811 } 2812 2813 /** 2814 * Internal: Update image comment. 2815 */ 2816 static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment) 2817 { 2818 char *pszCommentEncoded; 2819 if (pszComment) 2820 { 2821 pszCommentEncoded = vmdkEncodeString(pszComment); 2822 if (!pszCommentEncoded) 2823 return VERR_NO_MEMORY; 2824 } 2825 else 2826 pszCommentEncoded = NULL; 2827 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, 2828 "ddb.comment", pszCommentEncoded); 2829 if (pszComment) 2830 RTStrFree(pszCommentEncoded); 2831 if (VBOX_FAILURE(rc)) 2832 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename); 2833 return VINF_SUCCESS; 239 rawFreeImage(pImage, rc != VERR_ALREADY_EXISTS); 240 return rc; 2834 241 } 2835 242 … … 2838 245 * delete the image from disk. 2839 246 */ 2840 static void vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)247 static void rawFreeImage(PRAWIMAGE pImage, bool fDelete) 2841 248 { 2842 249 Assert(pImage); … … 2845 252 { 2846 253 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 2847 { 2848 /* Mark all extents as clean. */ 2849 for (unsigned i = 0; i < pImage->cExtents; i++) 2850 { 2851 if (( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE 2852 #ifdef VBOX_WITH_VMDK_ESX 2853 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE 2854 #endif /* VBOX_WITH_VMDK_ESX */ 2855 ) 2856 && pImage->pExtents[i].fUncleanShutdown) 2857 { 2858 pImage->pExtents[i].fUncleanShutdown = false; 2859 pImage->pExtents[i].fMetaDirty = true; 2860 } 2861 } 2862 } 2863 (void)vmdkFlushImage(pImage); 2864 } 2865 if (pImage->pExtents != NULL) 2866 { 2867 for (unsigned i = 0 ; i < pImage->cExtents; i++) 2868 vmdkFreeExtentData(&pImage->pExtents[i], fDelete); 2869 RTMemFree(pImage->pExtents); 2870 pImage->pExtents = NULL; 254 rawFlushImage(pImage); 2871 255 } 2872 256 if (pImage->File != NIL_RTFILE) … … 2880 264 2881 265 /** 2882 * Internal. Flush image data (and metadata)to disk.266 * Internal. Flush image data to disk. 2883 267 */ 2884 static int vmdkFlushImage(PVMDKIMAGE pImage) 2885 { 2886 PVMDKEXTENT pExtent; 268 static int rawFlushImage(PRAWIMAGE pImage) 269 { 2887 270 int rc = VINF_SUCCESS; 2888 271 2889 /* Update descriptor if changed. */ 2890 if (pImage->Descriptor.fDirty) 2891 { 2892 rc = vmdkWriteDescriptor(pImage); 2893 if (VBOX_FAILURE(rc)) 2894 goto out; 2895 } 2896 2897 for (unsigned i = 0; i < pImage->cExtents; i++) 2898 { 2899 pExtent = &pImage->pExtents[i]; 2900 if (pExtent->File != NIL_RTFILE && pExtent->fMetaDirty) 2901 { 2902 switch (pExtent->enmType) 2903 { 2904 case VMDKETYPE_HOSTED_SPARSE: 2905 rc = vmdkWriteMetaSparseExtent(pExtent); 2906 if (VBOX_FAILURE(rc)) 2907 goto out; 2908 break; 2909 #ifdef VBOX_WITH_VMDK_ESX 2910 case VMDKETYPE_ESX_SPARSE: 2911 /** @todo update the header. */ 2912 break; 2913 #endif /* VBOX_WITH_VMDK_ESX */ 2914 case VMDKETYPE_FLAT: 2915 /* Nothing to do. */ 2916 break; 2917 case VMDKETYPE_ZERO: 2918 default: 2919 AssertMsgFailed(("extent with type %d marked as dirty\n", 2920 pExtent->enmType)); 2921 break; 2922 } 2923 } 2924 switch (pExtent->enmType) 2925 { 2926 case VMDKETYPE_HOSTED_SPARSE: 2927 #ifdef VBOX_WITH_VMDK_ESX 2928 case VMDKETYPE_ESX_SPARSE: 2929 #endif /* VBOX_WITH_VMDK_ESX */ 2930 case VMDKETYPE_FLAT: 2931 /** @todo implement proper path absolute check. */ 2932 if ( pExtent->File != NIL_RTFILE 2933 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 2934 && !(pExtent->pszBasename[0] == RTPATH_SLASH)) 2935 rc = RTFileFlush(pExtent->File); 2936 break; 2937 case VMDKETYPE_ZERO: 2938 /* No need to do anything for this extent. */ 2939 break; 2940 default: 2941 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType)); 2942 break; 2943 } 2944 } 2945 2946 out: 2947 return rc; 2948 } 2949 2950 /** 2951 * Internal. Find extent corresponding to the sector number in the disk. 2952 */ 2953 static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector, 2954 PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent) 2955 { 2956 PVMDKEXTENT pExtent = NULL; 2957 int rc = VINF_SUCCESS; 2958 2959 for (unsigned i = 0; i < pImage->cExtents; i++) 2960 { 2961 if (offSector < pImage->pExtents[i].cNominalSectors) 2962 { 2963 pExtent = &pImage->pExtents[i]; 2964 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset; 2965 break; 2966 } 2967 offSector -= pImage->pExtents[i].cNominalSectors; 2968 } 2969 2970 if (pExtent) 2971 *ppExtent = pExtent; 2972 else 2973 rc = VERR_IO_SECTOR_NOT_FOUND; 2974 2975 return rc; 2976 } 2977 2978 /** 2979 * Internal. Hash function for placing the grain table hash entries. 2980 */ 2981 static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector, 2982 unsigned uExtent) 2983 { 2984 /** @todo this hash function is quite simple, maybe use a better one which 2985 * scrambles the bits better. */ 2986 return (uSector + uExtent) % pCache->cEntries; 2987 } 2988 2989 /** 2990 * Internal. Get sector number in the extent file from the relative sector 2991 * number in the extent. 2992 */ 2993 static int vmdkGetSector(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent, 2994 uint64_t uSector, uint64_t *puExtentSector) 2995 { 2996 uint64_t uGDIndex, uGTSector, uGTBlock; 2997 uint32_t uGTHash, uGTBlockIndex; 2998 PVMDKGTCACHEENTRY pGTCacheEntry; 2999 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE]; 3000 int rc; 3001 3002 uGDIndex = uSector / pExtent->cSectorsPerGDE; 3003 if (uGDIndex >= pExtent->cGDEntries) 3004 return VERR_OUT_OF_RANGE; 3005 uGTSector = pExtent->pGD[uGDIndex]; 3006 if (!uGTSector) 3007 { 3008 /* There is no grain table referenced by this grain directory 3009 * entry. So there is absolutely no data in this area. */ 3010 *puExtentSector = 0; 3011 return VINF_SUCCESS; 3012 } 3013 3014 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE); 3015 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent); 3016 pGTCacheEntry = &pCache->aGTCache[uGTHash]; 3017 if ( pGTCacheEntry->uExtent != pExtent->uExtent 3018 || pGTCacheEntry->uGTBlock != uGTBlock) 3019 { 3020 /* Cache miss, fetch data from disk. */ 3021 rc = RTFileReadAt(pExtent->File, 3022 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp), 3023 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3024 if (VBOX_FAILURE(rc)) 3025 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname); 3026 pGTCacheEntry->uExtent = pExtent->uExtent; 3027 pGTCacheEntry->uGTBlock = uGTBlock; 3028 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++) 3029 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]); 3030 } 3031 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE; 3032 uint64_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex]; 3033 if (uGrainSector) 3034 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain; 3035 else 3036 *puExtentSector = 0; 3037 return VINF_SUCCESS; 3038 } 3039 3040 /** 3041 * Internal. Allocates a new grain table (if necessary), writes the grain 3042 * and updates the grain table. The cache is also updated by this operation. 3043 * This is separate from vmdkGetSector, because that should be as fast as 3044 * possible. Most code from vmdkGetSector also appears here. 3045 */ 3046 static int vmdkAllocGrain(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent, 3047 uint64_t uSector, const void *pvBuf, 3048 uint64_t cbWrite) 3049 { 3050 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock; 3051 uint64_t cbExtentSize; 3052 uint32_t uGTHash, uGTBlockIndex; 3053 PVMDKGTCACHEENTRY pGTCacheEntry; 3054 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE]; 3055 int rc; 3056 3057 uGDIndex = uSector / pExtent->cSectorsPerGDE; 3058 if (uGDIndex >= pExtent->cGDEntries) 3059 return VERR_OUT_OF_RANGE; 3060 uGTSector = pExtent->pGD[uGDIndex]; 3061 uRGTSector = pExtent->pRGD[uGDIndex]; 3062 if (!uGTSector) 3063 { 3064 /* There is no grain table referenced by this grain directory 3065 * entry. So there is absolutely no data in this area. Allocate 3066 * a new grain table and put the reference to it in the GDs. */ 3067 rc = RTFileGetSize(pExtent->File, &cbExtentSize); 3068 if (VBOX_FAILURE(rc)) 3069 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 3070 Assert(!(cbExtentSize % 512)); 3071 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize); 3072 /* Normally the grain table is preallocated for hosted sparse extents 3073 * that support more than 32 bit sector numbers. So this shouldn't 3074 * ever happen on a valid extent. */ 3075 if (uGTSector > UINT32_MAX) 3076 return VERR_VDI_INVALID_HEADER; 3077 /* Write grain table by writing the required number of grain table 3078 * cache chunks. Avoids dynamic memory allocation, but is a bit 3079 * slower. But as this is a pretty infrequently occurring case it 3080 * should be acceptable. */ 3081 memset(aGTDataTmp, '\0', sizeof(aGTDataTmp)); 3082 for (unsigned i = 0; 3083 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; 3084 i++) 3085 { 3086 rc = RTFileWriteAt(pExtent->File, 3087 VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp), 3088 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3089 if (VBOX_FAILURE(rc)) 3090 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname); 3091 } 3092 if (pExtent->pRGD) 3093 { 3094 rc = RTFileGetSize(pExtent->File, &cbExtentSize); 3095 if (VBOX_FAILURE(rc)) 3096 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 3097 Assert(!(cbExtentSize % 512)); 3098 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize); 3099 /* Write backup grain table by writing the required number of grain 3100 * table cache chunks. Avoids dynamic memory allocation, but is a 3101 * bit slower. But as this is a pretty infrequently occurring case 3102 * it should be acceptable. */ 3103 for (unsigned i = 0; 3104 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; 3105 i++) 3106 { 3107 rc = RTFileWriteAt(pExtent->File, 3108 VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp), 3109 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3110 if (VBOX_FAILURE(rc)) 3111 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname); 3112 } 3113 } 3114 3115 /* Update the grain directory on disk (doing it before writing the 3116 * grain table will result in a garbled extent if the operation is 3117 * aborted for some reason. Otherwise the worst that can happen is 3118 * some unused sectors in the extent. */ 3119 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector); 3120 rc = RTFileWriteAt(pExtent->File, 3121 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE), 3122 &uGTSectorLE, sizeof(uGTSectorLE), NULL); 3123 if (VBOX_FAILURE(rc)) 3124 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname); 3125 if (pExtent->pRGD) 3126 { 3127 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector); 3128 rc = RTFileWriteAt(pExtent->File, 3129 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE), 3130 &uRGTSectorLE, sizeof(uRGTSectorLE), NULL); 3131 if (VBOX_FAILURE(rc)) 3132 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname); 3133 } 3134 3135 /* As the final step update the in-memory copy of the GDs. */ 3136 pExtent->pGD[uGDIndex] = uGTSector; 3137 if (pExtent->pRGD) 3138 pExtent->pRGD[uGDIndex] = uRGTSector; 3139 } 3140 3141 rc = RTFileGetSize(pExtent->File, &cbExtentSize); 3142 if (VBOX_FAILURE(rc)) 3143 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname); 3144 Assert(!(cbExtentSize % 512)); 3145 3146 /* Write the data. */ 3147 rc = RTFileWriteAt(pExtent->File, cbExtentSize, pvBuf, cbWrite, NULL); 3148 if (VBOX_FAILURE(rc)) 3149 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname); 3150 3151 /* Update the grain table (and the cache). */ 3152 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE); 3153 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent); 3154 pGTCacheEntry = &pCache->aGTCache[uGTHash]; 3155 if ( pGTCacheEntry->uExtent != pExtent->uExtent 3156 || pGTCacheEntry->uGTBlock != uGTBlock) 3157 { 3158 /* Cache miss, fetch data from disk. */ 3159 rc = RTFileReadAt(pExtent->File, 3160 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp), 3161 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3162 if (VBOX_FAILURE(rc)) 3163 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname); 3164 pGTCacheEntry->uExtent = pExtent->uExtent; 3165 pGTCacheEntry->uGTBlock = uGTBlock; 3166 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++) 3167 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]); 3168 } 3169 else 3170 { 3171 /* Cache hit. Convert grain table block back to disk format, otherwise 3172 * the code below will write garbage for all but the updated entry. */ 3173 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++) 3174 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]); 3175 } 3176 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE; 3177 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(cbExtentSize)); 3178 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(cbExtentSize); 3179 /* Update grain table on disk. */ 3180 rc = RTFileWriteAt(pExtent->File, 3181 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp), 3182 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3183 if (VBOX_FAILURE(rc)) 3184 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname); 3185 if (pExtent->pRGD) 3186 { 3187 /* Update backup grain table on disk. */ 3188 rc = RTFileWriteAt(pExtent->File, 3189 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp), 3190 aGTDataTmp, sizeof(aGTDataTmp), NULL); 3191 if (VBOX_FAILURE(rc)) 3192 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname); 3193 } 3194 #ifdef VBOX_WITH_VMDK_ESX 3195 if (VBOX_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE) 3196 { 3197 pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite); 3198 pExtent->fMetaDirty = true; 3199 } 3200 #endif /* VBOX_WITH_VMDK_ESX */ 272 if ( pImage->File != NIL_RTFILE 273 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 274 rc = RTFileFlush(pImage->File); 275 3201 276 return rc; 3202 277 } … … 3204 279 3205 280 /** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */ 3206 static int vmdkCheckIfValid(const char *pszFilename)281 static int rawCheckIfValid(const char *pszFilename) 3207 282 { 3208 283 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename)); 3209 284 int rc = VINF_SUCCESS; 3210 PVMDKIMAGE pImage; 3211 3212 if ( !pszFilename 3213 || !*pszFilename 3214 || strchr(pszFilename, '"')) 285 286 if ( !VALID_PTR(pszFilename) 287 || !*pszFilename) 3215 288 { 3216 289 rc = VERR_INVALID_PARAMETER; … … 3218 291 } 3219 292 3220 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE)); 293 /* Always return failure, to avoid opening everything as a raw image. */ 294 rc = VERR_VDI_INVALID_HEADER; 295 296 out: 297 LogFlowFunc(("returns %Vrc\n", rc)); 298 return rc; 299 } 300 301 /** @copydoc VBOXHDDBACKEND::pfnOpen */ 302 static int rawOpen(const char *pszFilename, unsigned uOpenFlags, 303 PFNVDERROR pfnError, void *pvErrorUser, 304 void **ppBackendData) 305 { 306 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x ppBackendData=%#p\n", pszFilename, uOpenFlags, ppBackendData)); 307 int rc; 308 PRAWIMAGE pImage; 309 310 /* Check open flags. All valid flags are supported. */ 311 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK) 312 { 313 rc = VERR_INVALID_PARAMETER; 314 goto out; 315 } 316 317 /* Check remaining arguments. */ 318 if ( !VALID_PTR(pszFilename) 319 || !*pszFilename) 320 { 321 rc = VERR_INVALID_PARAMETER; 322 goto out; 323 } 324 325 326 pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE)); 3221 327 if (!pImage) 3222 328 { … … 3226 332 pImage->pszFilename = pszFilename; 3227 333 pImage->File = NIL_RTFILE; 3228 pImage->pExtents = NULL; 3229 pImage->pGTCache = NULL; 3230 pImage->pDescData = NULL; 3231 pImage->pfnError = NULL; 3232 pImage->pvErrorUser = NULL; 3233 /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as 3234 * much as possible in vmdkOpenImage. */ 3235 rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY); 3236 vmdkFreeImage(pImage, false); 334 pImage->pfnError = pfnError; 335 pImage->pvErrorUser = pvErrorUser; 336 337 rc = rawOpenImage(pImage, uOpenFlags); 338 if (VBOX_SUCCESS(rc)) 339 *ppBackendData = pImage; 3237 340 3238 341 out: 3239 LogFlowFunc(("returns %Vrc\n", rc)); 3240 return rc; 3241 } 3242 3243 /** @copydoc VBOXHDDBACKEND::pfnOpen */ 3244 static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, 3245 PFNVDERROR pfnError, void *pvErrorUser, 3246 void **ppBackendData) 3247 { 3248 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x ppBackendData=%#p\n", pszFilename, uOpenFlags, ppBackendData)); 3249 int rc; 3250 PVMDKIMAGE pImage; 342 LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData)); 343 return rc; 344 } 345 346 /** @copydoc VBOXHDDBACKEND::pfnCreate */ 347 static int rawCreate(const char *pszFilename, VDIMAGETYPE enmType, 348 uint64_t cbSize, unsigned uImageFlags, 349 const char *pszComment, 350 PCPDMMEDIAGEOMETRY pPCHSGeometry, 351 PCPDMMEDIAGEOMETRY pLCHSGeometry, 352 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress, 353 void *pvUser, unsigned uPercentStart, 354 unsigned uPercentSpan, PFNVDERROR pfnError, 355 void *pvErrorUser, void **ppBackendData) 356 { 357 LogFlowFunc(("pszFilename=\"%s\" enmType=%d cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p uOpenFlags=%#x pfnProgress=%#p pvUser=%#p uPercentStart=%u uPercentSpan=%u pfnError=%#p pvErrorUser=%#p ppBackendData=%#p", pszFilename, enmType, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, uOpenFlags, pfnProgress, pvUser, uPercentStart, uPercentSpan, pfnError, pvErrorUser, ppBackendData)); 358 int rc; 359 PRAWIMAGE pImage; 3251 360 3252 361 /* Check open flags. All valid flags are supported. */ … … 3260 369 if ( !VALID_PTR(pszFilename) 3261 370 || !*pszFilename 3262 || strchr(pszFilename, '"')) 371 || (enmType != VD_IMAGE_TYPE_FIXED) 372 || !VALID_PTR(pPCHSGeometry) 373 || !VALID_PTR(pLCHSGeometry)) 3263 374 { 3264 375 rc = VERR_INVALID_PARAMETER; … … 3266 377 } 3267 378 3268 3269 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE)); 379 pImage = (PRAWIMAGE)RTMemAllocZ(sizeof(RAWIMAGE)); 3270 380 if (!pImage) 3271 381 { … … 3275 385 pImage->pszFilename = pszFilename; 3276 386 pImage->File = NIL_RTFILE; 3277 pImage->pExtents = NULL;3278 pImage->pGTCache = NULL;3279 pImage->pDescData = NULL;3280 387 pImage->pfnError = pfnError; 3281 388 pImage->pvErrorUser = pvErrorUser; 3282 389 3283 rc = vmdkOpenImage(pImage, uOpenFlags); 3284 if (VBOX_SUCCESS(rc)) 3285 *ppBackendData = pImage; 3286 3287 out: 3288 LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData)); 3289 return rc; 3290 } 3291 3292 /** @copydoc VBOXHDDBACKEND::pfnCreate */ 3293 static int vmdkCreate(const char *pszFilename, VDIMAGETYPE enmType, 3294 uint64_t cbSize, unsigned uImageFlags, 3295 const char *pszComment, 3296 PCPDMMEDIAGEOMETRY pPCHSGeometry, 3297 PCPDMMEDIAGEOMETRY pLCHSGeometry, 3298 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress, 3299 void *pvUser, unsigned uPercentStart, 3300 unsigned uPercentSpan, PFNVDERROR pfnError, 3301 void *pvErrorUser, void **ppBackendData) 3302 { 3303 LogFlowFunc(("pszFilename=\"%s\" enmType=%d cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p uOpenFlags=%#x pfnProgress=%#p pvUser=%#p uPercentStart=%u uPercentSpan=%u pfnError=%#p pvErrorUser=%#p ppBackendData=%#p", pszFilename, enmType, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, uOpenFlags, pfnProgress, pvUser, uPercentStart, uPercentSpan, pfnError, pvErrorUser, ppBackendData)); 3304 int rc; 3305 PVMDKIMAGE pImage; 3306 3307 /* Check open flags. All valid flags are supported. */ 3308 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK) 3309 { 3310 rc = VERR_INVALID_PARAMETER; 3311 goto out; 3312 } 3313 3314 /* Check remaining arguments. */ 3315 if ( !VALID_PTR(pszFilename) 3316 || !*pszFilename 3317 || strchr(pszFilename, '"') 3318 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED) 3319 || !VALID_PTR(pPCHSGeometry) 3320 || !VALID_PTR(pLCHSGeometry)) 3321 { 3322 rc = VERR_INVALID_PARAMETER; 3323 goto out; 3324 } 3325 3326 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE)); 3327 if (!pImage) 3328 { 3329 rc = VERR_NO_MEMORY; 3330 goto out; 3331 } 3332 pImage->pszFilename = pszFilename; 3333 pImage->File = NIL_RTFILE; 3334 pImage->pExtents = NULL; 3335 pImage->pGTCache = NULL; 3336 pImage->pDescData = NULL; 3337 pImage->pfnError = pfnError; 3338 pImage->pvErrorUser = pvErrorUser; 3339 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20); 3340 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc); 3341 if (!pImage->pDescData) 3342 { 3343 rc = VERR_NO_MEMORY; 3344 goto out; 3345 } 3346 3347 rc = vmdkCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment, 3348 pPCHSGeometry, pLCHSGeometry, 3349 pfnProgress, pvUser, uPercentStart, uPercentSpan); 390 rc = rawCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment, 391 pPCHSGeometry, pLCHSGeometry, 392 pfnProgress, pvUser, uPercentStart, uPercentSpan); 3350 393 if (VBOX_SUCCESS(rc)) 3351 394 { … … 3354 397 if (uOpenFlags & VD_OPEN_FLAGS_READONLY) 3355 398 { 3356 vmdkFreeImage(pImage, false);3357 rc = vmdkOpenImage(pImage, uOpenFlags);399 rawFreeImage(pImage, false); 400 rc = rawOpenImage(pImage, uOpenFlags); 3358 401 if (VBOX_FAILURE(rc)) 3359 402 goto out; … … 3368 411 3369 412 /** @copydoc VBOXHDDBACKEND::pfnRename */ 3370 static int vmdkRename(void *pBackendData, const char *pszFilename)413 static int rawRename(void *pBackendData, const char *pszFilename) 3371 414 { 3372 415 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename)); … … 3378 421 3379 422 /** @copydoc VBOXHDDBACKEND::pfnClose */ 3380 static int vmdkClose(void *pBackendData, bool fDelete)423 static int rawClose(void *pBackendData, bool fDelete) 3381 424 { 3382 425 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete)); 3383 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;426 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3384 427 int rc = VINF_SUCCESS; 3385 428 … … 3387 430 * not signalled as an error. After all nothing bad happens. */ 3388 431 if (pImage) 3389 vmdkFreeImage(pImage, fDelete);432 rawFreeImage(pImage, fDelete); 3390 433 3391 434 LogFlowFunc(("returns %Vrc\n", rc)); … … 3394 437 3395 438 /** @copydoc VBOXHDDBACKEND::pfnRead */ 3396 static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,3397 439 static int rawRead(void *pBackendData, uint64_t uOffset, void *pvBuf, 440 size_t cbToRead, size_t *pcbActuallyRead) 3398 441 { 3399 442 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead)); 3400 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3401 PVMDKEXTENT pExtent; 3402 uint64_t uSectorExtentRel; 3403 uint64_t uSectorExtentAbs; 443 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3404 444 int rc; 3405 445 … … 3415 455 } 3416 456 3417 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset), 3418 &pExtent, &uSectorExtentRel); 3419 if (VBOX_FAILURE(rc)) 3420 goto out; 3421 3422 /* Check access permissions as defined in the extent descriptor. */ 3423 if (pExtent->enmAccess == VMDKACCESS_NOACCESS) 3424 { 3425 rc = VERR_VDI_INVALID_STATE; 3426 goto out; 3427 } 3428 3429 /* Clip read range to remain in this extent. */ 3430 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 3431 3432 /* Handle the read according to the current extent type. */ 3433 switch (pExtent->enmType) 3434 { 3435 case VMDKETYPE_HOSTED_SPARSE: 3436 #ifdef VBOX_WITH_VMDK_ESX 3437 case VMDKETYPE_ESX_SPARSE: 3438 #endif /* VBOX_WITH_VMDK_ESX */ 3439 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel, 3440 &uSectorExtentAbs); 3441 if (VBOX_FAILURE(rc)) 3442 goto out; 3443 /* Clip read range to at most the rest of the grain. */ 3444 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain)); 3445 Assert(!(cbToRead % 512)); 3446 if (uSectorExtentAbs == 0) 3447 rc = VERR_VDI_BLOCK_FREE; 3448 else 3449 rc = RTFileReadAt(pExtent->File, 3450 VMDK_SECTOR2BYTE(uSectorExtentAbs), 3451 pvBuf, cbToRead, NULL); 3452 break; 3453 case VMDKETYPE_FLAT: 3454 rc = RTFileReadAt(pExtent->File, 3455 VMDK_SECTOR2BYTE(uSectorExtentRel), 3456 pvBuf, cbToRead, NULL); 3457 break; 3458 case VMDKETYPE_ZERO: 3459 memset(pvBuf, '\0', cbToRead); 3460 break; 3461 } 457 rc = RTFileReadAt(pImage->File, uOffset, pvBuf, cbToRead, NULL); 3462 458 *pcbActuallyRead = cbToRead; 3463 459 … … 3468 464 3469 465 /** @copydoc VBOXHDDBACKEND::pfnWrite */ 3470 static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,3471 3472 466 static int rawWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, 467 size_t cbToWrite, size_t *pcbWriteProcess, 468 size_t *pcbPreRead, size_t *pcbPostRead) 3473 469 { 3474 470 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead)); 3475 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3476 PVMDKEXTENT pExtent; 3477 uint64_t uSectorExtentRel; 3478 uint64_t uSectorExtentAbs; 471 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3479 472 int rc; 3480 473 … … 3489 482 } 3490 483 3491 if (cbToWrite == 0) 484 if ( uOffset + cbToWrite > pImage->cbSize 485 || cbToWrite == 0) 3492 486 { 3493 487 rc = VERR_INVALID_PARAMETER; … … 3495 489 } 3496 490 3497 /* No size check here, will do that later when the extent is located. 3498 * There are sparse images out there which according to the spec are 3499 * invalid, because the total size is not a multiple of the grain size. 3500 * Also for sparse images which are stitched together in odd ways (not at 3501 * grain boundaries, and with the nominal size not being a multiple of the 3502 * grain size), this would prevent writing to the last grain. */ 3503 3504 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset), 3505 &pExtent, &uSectorExtentRel); 3506 if (VBOX_FAILURE(rc)) 3507 goto out; 3508 3509 /* Check access permissions as defined in the extent descriptor. */ 3510 if (pExtent->enmAccess != VMDKACCESS_READWRITE) 3511 { 3512 rc = VERR_VDI_INVALID_STATE; 3513 goto out; 3514 } 3515 3516 /** @todo implement suppressing of zero data writes (a bit tricky in this 3517 * case, as VMDK has no marker for zero blocks). We somehow need to get the 3518 * information whether the information in this area is all zeroes as of the 3519 * parent image. Then (based on the assumption that parent images are 3520 * immutable) the write can be ignored. */ 3521 3522 /* Handle the write according to the current extent type. */ 3523 switch (pExtent->enmType) 3524 { 3525 case VMDKETYPE_HOSTED_SPARSE: 3526 #ifdef VBOX_WITH_VMDK_ESX 3527 case VMDKETYPE_ESX_SPARSE: 3528 #endif /* VBOX_WITH_VMDK_ESX */ 3529 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel, 3530 &uSectorExtentAbs); 3531 if (VBOX_FAILURE(rc)) 3532 goto out; 3533 /* Clip write range to at most the rest of the grain. */ 3534 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain)); 3535 if (uSectorExtentAbs == 0) 3536 { 3537 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)) 3538 { 3539 /* Full block write to a previously unallocated block. 3540 * Allocate GT and find out where to store the grain. */ 3541 rc = vmdkAllocGrain(pImage->pGTCache, pExtent, 3542 uSectorExtentRel, pvBuf, cbToWrite); 3543 *pcbPreRead = 0; 3544 *pcbPostRead = 0; 3545 } 3546 else 3547 { 3548 /* Clip write range to remain in this extent. */ 3549 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 3550 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain); 3551 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead; 3552 rc = VERR_VDI_BLOCK_FREE; 3553 } 3554 } 3555 else 3556 rc = RTFileWriteAt(pExtent->File, 3557 VMDK_SECTOR2BYTE(uSectorExtentAbs), 3558 pvBuf, cbToWrite, NULL); 3559 break; 3560 case VMDKETYPE_FLAT: 3561 /* Clip write range to remain in this extent. */ 3562 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 3563 rc = RTFileWriteAt(pExtent->File, 3564 VMDK_SECTOR2BYTE(uSectorExtentRel), 3565 pvBuf, cbToWrite, NULL); 3566 break; 3567 case VMDKETYPE_ZERO: 3568 /* Clip write range to remain in this extent. */ 3569 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel)); 3570 break; 3571 } 491 rc = RTFileWriteAt(pImage->File, uOffset, pvBuf, cbToWrite, NULL); 3572 492 if (pcbWriteProcess) 3573 493 *pcbWriteProcess = cbToWrite; … … 3579 499 3580 500 /** @copydoc VBOXHDDBACKEND::pfnFlush */ 3581 static int vmdkFlush(void *pBackendData)501 static int rawFlush(void *pBackendData) 3582 502 { 3583 503 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3584 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;3585 int rc; 3586 3587 rc = vmdkFlushImage(pImage);504 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 505 int rc; 506 507 rc = rawFlushImage(pImage); 3588 508 LogFlowFunc(("returns %Vrc\n", rc)); 3589 509 return rc; … … 3591 511 3592 512 /** @copydoc VBOXHDDBACKEND::pfnGetVersion */ 3593 static unsigned vmdkGetVersion(void *pBackendData)513 static unsigned rawGetVersion(void *pBackendData) 3594 514 { 3595 515 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3596 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;3597 3598 Assert(pImage); 3599 3600 if (pImage) 3601 return VMDK_IMAGE_VERSION;516 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 517 518 Assert(pImage); 519 520 if (pImage) 521 return 1; 3602 522 else 3603 523 return 0; … … 3605 525 3606 526 /** @copydoc VBOXHDDBACKEND::pfnGetImageType */ 3607 static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)527 static int rawGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType) 3608 528 { 3609 529 LogFlowFunc(("pBackendData=%#p penmImageType=%#p\n", pBackendData, penmImageType)); 3610 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;530 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3611 531 int rc = VINF_SUCCESS; 3612 532 … … 3614 534 Assert(penmImageType); 3615 535 3616 if (pImage && pImage->cExtents != 0)536 if (pImage) 3617 537 *penmImageType = pImage->enmImageType; 3618 538 else … … 3624 544 3625 545 /** @copydoc VBOXHDDBACKEND::pfnGetSize */ 3626 static uint64_t vmdkGetSize(void *pBackendData)546 static uint64_t rawGetSize(void *pBackendData) 3627 547 { 3628 548 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3629 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;549 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3630 550 3631 551 Assert(pImage); … … 3638 558 3639 559 /** @copydoc VBOXHDDBACKEND::pfnGetFileSize */ 3640 static uint64_t vmdkGetFileSize(void *pBackendData)560 static uint64_t rawGetFileSize(void *pBackendData) 3641 561 { 3642 562 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3643 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;563 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3644 564 uint64_t cb = 0; 3645 565 … … 3654 574 if (VBOX_SUCCESS(rc)) 3655 575 cb += cbFile; 3656 for (unsigned i = 0; i <= pImage->cExtents; i++)3657 {3658 rc = RTFileGetSize(pImage->File, &cbFile);3659 if (VBOX_SUCCESS(rc))3660 cb += cbFile;3661 }3662 576 } 3663 577 } … … 3668 582 3669 583 /** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */ 3670 static int vmdkGetPCHSGeometry(void *pBackendData,3671 584 static int rawGetPCHSGeometry(void *pBackendData, 585 PPDMMEDIAGEOMETRY pPCHSGeometry) 3672 586 { 3673 587 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry)); 3674 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;588 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3675 589 int rc; 3676 590 … … 3695 609 3696 610 /** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */ 3697 static int vmdkSetPCHSGeometry(void *pBackendData,3698 611 static int rawSetPCHSGeometry(void *pBackendData, 612 PCPDMMEDIAGEOMETRY pPCHSGeometry) 3699 613 { 3700 614 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors)); 3701 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;615 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3702 616 int rc; 3703 617 … … 3711 625 goto out; 3712 626 } 3713 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);3714 if (VBOX_FAILURE(rc))3715 goto out;3716 627 3717 628 pImage->PCHSGeometry = *pPCHSGeometry; … … 3727 638 3728 639 /** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */ 3729 static int vmdkGetLCHSGeometry(void *pBackendData,3730 640 static int rawGetLCHSGeometry(void *pBackendData, 641 PPDMMEDIAGEOMETRY pLCHSGeometry) 3731 642 { 3732 643 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry)); 3733 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;644 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3734 645 int rc; 3735 646 … … 3754 665 3755 666 /** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */ 3756 static int vmdkSetLCHSGeometry(void *pBackendData,667 static int rawSetLCHSGeometry(void *pBackendData, 3757 668 PCPDMMEDIAGEOMETRY pLCHSGeometry) 3758 669 { 3759 670 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors)); 3760 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;671 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3761 672 int rc; 3762 673 … … 3770 681 goto out; 3771 682 } 3772 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);3773 if (VBOX_FAILURE(rc))3774 goto out;3775 683 3776 684 pImage->LCHSGeometry = *pLCHSGeometry; … … 3786 694 3787 695 /** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */ 3788 static unsigned vmdkGetImageFlags(void *pBackendData)696 static unsigned rawGetImageFlags(void *pBackendData) 3789 697 { 3790 698 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3791 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;699 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3792 700 unsigned uImageFlags; 3793 701 … … 3804 712 3805 713 /** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */ 3806 static unsigned vmdkGetOpenFlags(void *pBackendData)714 static unsigned rawGetOpenFlags(void *pBackendData) 3807 715 { 3808 716 LogFlowFunc(("pBackendData=%#p\n", pBackendData)); 3809 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;717 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3810 718 unsigned uOpenFlags; 3811 719 … … 3822 730 3823 731 /** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */ 3824 static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)732 static int rawSetOpenFlags(void *pBackendData, unsigned uOpenFlags) 3825 733 { 3826 734 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags)); 3827 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;735 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3828 736 int rc; 3829 737 const char *pszFilename; … … 3839 747 /* Implement this operation via reopening the image. */ 3840 748 pszFilename = pImage->pszFilename; 3841 vmdkFreeImage(pImage, false);3842 rc = vmdkOpenImage(pImage, uOpenFlags);749 rawFreeImage(pImage, false); 750 rc = rawOpenImage(pImage, uOpenFlags); 3843 751 3844 752 out: … … 3848 756 3849 757 /** @copydoc VBOXHDDBACKEND::pfnGetComment */ 3850 static int vmdkGetComment(void *pBackendData, char *pszComment,758 static int rawGetComment(void *pBackendData, char *pszComment, 3851 759 size_t cbComment) 3852 760 { 3853 761 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment)); 3854 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3855 int rc; 3856 3857 Assert(pImage); 3858 3859 if (pImage) 3860 { 3861 const char *pszCommentEncoded = NULL; 3862 rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor, 3863 "ddb.comment", &pszCommentEncoded); 3864 if (rc == VERR_VDI_VALUE_NOT_FOUND) 3865 pszCommentEncoded = NULL; 3866 else if (VBOX_FAILURE(rc)) 3867 goto out; 3868 762 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 763 int rc; 764 765 Assert(pImage); 766 767 if (pImage) 768 { 3869 769 if (pszComment) 3870 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment); 770 *pszComment = '\0'; 771 rc = VINF_SUCCESS; 772 } 773 else 774 rc = VERR_VDI_NOT_OPENED; 775 776 LogFlowFunc(("returns %Vrc comment='%s'\n", rc, pszComment)); 777 return rc; 778 } 779 780 /** @copydoc VBOXHDDBACKEND::pfnSetComment */ 781 static int rawSetComment(void *pBackendData, const char *pszComment) 782 { 783 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment)); 784 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 785 int rc; 786 787 Assert(pImage); 788 789 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 790 { 791 rc = VERR_VDI_IMAGE_READ_ONLY; 792 goto out; 793 } 794 795 if (pImage) 796 rc = VERR_NOT_SUPPORTED; 797 else 798 rc = VERR_VDI_NOT_OPENED; 799 800 out: 801 LogFlowFunc(("returns %Vrc\n", rc)); 802 return rc; 803 } 804 805 /** @copydoc VBOXHDDBACKEND::pfnGetUuid */ 806 static int rawGetUuid(void *pBackendData, PRTUUID pUuid) 807 { 808 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid)); 809 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 810 int rc; 811 812 Assert(pImage); 813 814 if (pImage) 815 rc = VERR_NOT_SUPPORTED; 816 else 817 rc = VERR_VDI_NOT_OPENED; 818 819 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid)); 820 return rc; 821 } 822 823 /** @copydoc VBOXHDDBACKEND::pfnSetUuid */ 824 static int rawSetUuid(void *pBackendData, PCRTUUID pUuid) 825 { 826 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid)); 827 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 828 int rc; 829 830 LogFlowFunc(("%Vuuid\n", pUuid)); 831 Assert(pImage); 832 833 if (pImage) 834 { 835 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 836 rc = VERR_NOT_SUPPORTED; 3871 837 else 3872 { 3873 *pszComment = '\0'; 3874 rc = VINF_SUCCESS; 3875 } 3876 if (pszCommentEncoded) 3877 RTStrFree((char *)(void *)pszCommentEncoded); 3878 } 3879 else 3880 rc = VERR_VDI_NOT_OPENED; 3881 3882 out: 3883 LogFlowFunc(("returns %Vrc comment='%s'\n", rc, pszComment)); 3884 return rc; 3885 } 3886 3887 /** @copydoc VBOXHDDBACKEND::pfnSetComment */ 3888 static int vmdkSetComment(void *pBackendData, const char *pszComment) 3889 { 3890 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment)); 3891 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3892 int rc; 3893 3894 Assert(pImage); 3895 3896 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) 3897 { 3898 rc = VERR_VDI_IMAGE_READ_ONLY; 3899 goto out; 3900 } 3901 3902 if (pImage) 3903 rc = vmdkSetImageComment(pImage, pszComment); 3904 else 3905 rc = VERR_VDI_NOT_OPENED; 3906 3907 out: 3908 LogFlowFunc(("returns %Vrc\n", rc)); 3909 return rc; 3910 } 3911 3912 /** @copydoc VBOXHDDBACKEND::pfnGetUuid */ 3913 static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid) 838 rc = VERR_VDI_IMAGE_READ_ONLY; 839 } 840 else 841 rc = VERR_VDI_NOT_OPENED; 842 843 LogFlowFunc(("returns %Vrc\n", rc)); 844 return rc; 845 } 846 847 /** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */ 848 static int rawGetModificationUuid(void *pBackendData, PRTUUID pUuid) 3914 849 { 3915 850 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid)); 3916 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3917 int rc; 3918 3919 Assert(pImage); 3920 3921 if (pImage) 3922 { 3923 *pUuid = pImage->ImageUuid; 3924 rc = VINF_SUCCESS; 3925 } 851 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 852 int rc; 853 854 Assert(pImage); 855 856 if (pImage) 857 rc = VERR_NOT_SUPPORTED; 3926 858 else 3927 859 rc = VERR_VDI_NOT_OPENED; … … 3931 863 } 3932 864 3933 /** @copydoc VBOXHDDBACKEND::pfnSet Uuid */3934 static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)865 /** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */ 866 static int rawSetModificationUuid(void *pBackendData, PCRTUUID pUuid) 3935 867 { 3936 868 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid)); 3937 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3938 int rc; 3939 3940 LogFlowFunc(("%Vuuid\n", pUuid)); 869 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 870 int rc; 871 3941 872 Assert(pImage); 3942 873 … … 3944 875 { 3945 876 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 3946 { 3947 pImage->ImageUuid = *pUuid; 3948 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 3949 VMDK_DDB_IMAGE_UUID, pUuid); 3950 if (VBOX_FAILURE(rc)) 3951 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename); 3952 rc = VINF_SUCCESS; 3953 } 3954 else 3955 rc = VERR_VDI_IMAGE_READ_ONLY; 3956 } 3957 else 3958 rc = VERR_VDI_NOT_OPENED; 3959 3960 LogFlowFunc(("returns %Vrc\n", rc)); 3961 return rc; 3962 } 3963 3964 /** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */ 3965 static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid) 877 rc = VERR_NOT_SUPPORTED; 878 else 879 rc = VERR_VDI_IMAGE_READ_ONLY; 880 } 881 else 882 rc = VERR_VDI_NOT_OPENED; 883 884 LogFlowFunc(("returns %Vrc\n", rc)); 885 return rc; 886 } 887 888 /** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */ 889 static int rawGetParentUuid(void *pBackendData, PRTUUID pUuid) 3966 890 { 3967 891 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid)); 3968 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 3969 int rc; 3970 3971 Assert(pImage); 3972 3973 if (pImage) 3974 { 3975 *pUuid = pImage->ModificationUuid; 3976 rc = VINF_SUCCESS; 3977 } 892 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 893 int rc; 894 895 Assert(pImage); 896 897 if (pImage) 898 rc = VERR_NOT_SUPPORTED; 3978 899 else 3979 900 rc = VERR_VDI_NOT_OPENED; … … 3983 904 } 3984 905 3985 /** @copydoc VBOXHDDBACKEND::pfnSet ModificationUuid */3986 static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)906 /** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */ 907 static int rawSetParentUuid(void *pBackendData, PCRTUUID pUuid) 3987 908 { 3988 909 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid)); 3989 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;910 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 3990 911 int rc; 3991 912 … … 3995 916 { 3996 917 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 3997 { 3998 pImage->ModificationUuid = *pUuid; 3999 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4000 VMDK_DDB_MODIFICATION_UUID, pUuid); 4001 if (VBOX_FAILURE(rc)) 4002 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename); 4003 rc = VINF_SUCCESS; 4004 } 4005 else 4006 rc = VERR_VDI_IMAGE_READ_ONLY; 4007 } 4008 else 4009 rc = VERR_VDI_NOT_OPENED; 4010 4011 LogFlowFunc(("returns %Vrc\n", rc)); 4012 return rc; 4013 } 4014 4015 /** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */ 4016 static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid) 918 rc = VERR_NOT_SUPPORTED; 919 else 920 rc = VERR_VDI_IMAGE_READ_ONLY; 921 } 922 else 923 rc = VERR_VDI_NOT_OPENED; 924 925 LogFlowFunc(("returns %Vrc\n", rc)); 926 return rc; 927 } 928 929 /** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */ 930 static int rawGetParentModificationUuid(void *pBackendData, PRTUUID pUuid) 4017 931 { 4018 932 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid)); 4019 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 4020 int rc; 4021 4022 Assert(pImage); 4023 4024 if (pImage) 4025 { 4026 *pUuid = pImage->ParentUuid; 4027 rc = VINF_SUCCESS; 4028 } 933 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 934 int rc; 935 936 Assert(pImage); 937 938 if (pImage) 939 rc = VERR_NOT_SUPPORTED; 4029 940 else 4030 941 rc = VERR_VDI_NOT_OPENED; … … 4034 945 } 4035 946 4036 /** @copydoc VBOXHDDBACKEND::pfnSetParent Uuid */4037 static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)947 /** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */ 948 static int rawSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid) 4038 949 { 4039 950 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid)); 4040 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;951 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 4041 952 int rc; 4042 953 … … 4046 957 { 4047 958 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 4048 { 4049 pImage->ParentUuid = *pUuid; 4050 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4051 VMDK_DDB_PARENT_UUID, pUuid); 4052 if (VBOX_FAILURE(rc)) 4053 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 4054 rc = VINF_SUCCESS; 4055 } 4056 else 4057 rc = VERR_VDI_IMAGE_READ_ONLY; 4058 } 4059 else 4060 rc = VERR_VDI_NOT_OPENED; 4061 4062 LogFlowFunc(("returns %Vrc\n", rc)); 4063 return rc; 4064 } 4065 4066 /** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */ 4067 static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid) 4068 { 4069 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid)); 4070 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 4071 int rc; 4072 4073 Assert(pImage); 4074 4075 if (pImage) 4076 { 4077 *pUuid = pImage->ParentModificationUuid; 4078 rc = VINF_SUCCESS; 4079 } 4080 else 4081 rc = VERR_VDI_NOT_OPENED; 4082 4083 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid)); 4084 return rc; 4085 } 4086 4087 /** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */ 4088 static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid) 4089 { 4090 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid)); 4091 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData; 4092 int rc; 4093 4094 Assert(pImage); 4095 4096 if (pImage) 4097 { 4098 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)) 4099 { 4100 pImage->ParentModificationUuid = *pUuid; 4101 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, 4102 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid); 4103 if (VBOX_FAILURE(rc)) 4104 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename); 4105 rc = VINF_SUCCESS; 4106 } 4107 else 4108 rc = VERR_VDI_IMAGE_READ_ONLY; 959 rc = VERR_NOT_SUPPORTED; 960 else 961 rc = VERR_VDI_IMAGE_READ_ONLY; 4109 962 } 4110 963 else … … 4116 969 4117 970 /** @copydoc VBOXHDDBACKEND::pfnDump */ 4118 static void vmdkDump(void *pBackendData)4119 { 4120 P VMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;971 static void rawDump(void *pBackendData) 972 { 973 PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; 4121 974 4122 975 Assert(pImage); … … 4126 979 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors, 4127 980 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors, 4128 VMDK_BYTE2SECTOR(pImage->cbSize)); 4129 RTLogPrintf("Header: uuidCreation={%Vuuid}\n", pImage->ImageUuid); 4130 RTLogPrintf("Header: uuidModification={%Vuuid}\n", pImage->ModificationUuid); 4131 RTLogPrintf("Header: uuidParent={%Vuuid}\n", pImage->ParentUuid); 4132 } 4133 } 4134 4135 4136 VBOXHDDBACKEND g_VmdkBackend = 981 pImage->cbSize / 512); 982 } 983 } 984 985 986 VBOXHDDBACKEND g_RawBackend = 4137 987 { 4138 988 /* pszBackendName */ 4139 " VMDK",989 "raw", 4140 990 /* cbSize */ 4141 991 sizeof(VBOXHDDBACKEND), 4142 992 /* pfnCheckIfValid */ 4143 vmdkCheckIfValid,993 rawCheckIfValid, 4144 994 /* pfnOpen */ 4145 vmdkOpen,995 rawOpen, 4146 996 /* pfnCreate */ 4147 vmdkCreate,997 rawCreate, 4148 998 /* pfnRename */ 4149 vmdkRename,999 rawRename, 4150 1000 /* pfnClose */ 4151 vmdkClose,1001 rawClose, 4152 1002 /* pfnRead */ 4153 vmdkRead,1003 rawRead, 4154 1004 /* pfnWrite */ 4155 vmdkWrite,1005 rawWrite, 4156 1006 /* pfnFlush */ 4157 vmdkFlush,1007 rawFlush, 4158 1008 /* pfnGetVersion */ 4159 vmdkGetVersion,1009 rawGetVersion, 4160 1010 /* pfnGetImageType */ 4161 vmdkGetImageType,1011 rawGetImageType, 4162 1012 /* pfnGetSize */ 4163 vmdkGetSize,1013 rawGetSize, 4164 1014 /* pfnGetFileSize */ 4165 vmdkGetFileSize,1015 rawGetFileSize, 4166 1016 /* pfnGetPCHSGeometry */ 4167 vmdkGetPCHSGeometry,1017 rawGetPCHSGeometry, 4168 1018 /* pfnSetPCHSGeometry */ 4169 vmdkSetPCHSGeometry,1019 rawSetPCHSGeometry, 4170 1020 /* pfnGetLCHSGeometry */ 4171 vmdkGetLCHSGeometry,1021 rawGetLCHSGeometry, 4172 1022 /* pfnSetLCHSGeometry */ 4173 vmdkSetLCHSGeometry,1023 rawSetLCHSGeometry, 4174 1024 /* pfnGetImageFlags */ 4175 vmdkGetImageFlags,1025 rawGetImageFlags, 4176 1026 /* pfnGetOpenFlags */ 4177 vmdkGetOpenFlags,1027 rawGetOpenFlags, 4178 1028 /* pfnSetOpenFlags */ 4179 vmdkSetOpenFlags,1029 rawSetOpenFlags, 4180 1030 /* pfnGetComment */ 4181 vmdkGetComment,1031 rawGetComment, 4182 1032 /* pfnSetComment */ 4183 vmdkSetComment,1033 rawSetComment, 4184 1034 /* pfnGetUuid */ 4185 vmdkGetUuid,1035 rawGetUuid, 4186 1036 /* pfnSetUuid */ 4187 vmdkSetUuid,1037 rawSetUuid, 4188 1038 /* pfnGetModificationUuid */ 4189 vmdkGetModificationUuid,1039 rawGetModificationUuid, 4190 1040 /* pfnSetModificationUuid */ 4191 vmdkSetModificationUuid,1041 rawSetModificationUuid, 4192 1042 /* pfnGetParentUuid */ 4193 vmdkGetParentUuid,1043 rawGetParentUuid, 4194 1044 /* pfnSetParentUuid */ 4195 vmdkSetParentUuid,1045 rawSetParentUuid, 4196 1046 /* pfnGetParentModificationUuid */ 4197 vmdkGetParentModificationUuid,1047 rawGetParentModificationUuid, 4198 1048 /* pfnSetParentModificationUuid */ 4199 vmdkSetParentModificationUuid,1049 rawSetParentModificationUuid, 4200 1050 /* pfnDump */ 4201 vmdkDump1051 rawDump 4202 1052 };
Note:
See TracChangeset
for help on using the changeset viewer.