VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp@ 8015

Last change on this file since 8015 was 8015, checked in by vboxsync, 17 years ago

VMDK: Fix a really nasty and indirect bug when creating split sparse VMDK files. Triggered a crash later after the header inconsistency spread (though the image became never actually invalid). Also creating fixed non-split images is fixed now and fixed images are completely written to disk (no sparse file use, see VDI).

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette