VirtualBox

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

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

Make the vmdk format error checking tighter and fix an annoying gcc warning (only for some gcc versions).

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