VirtualBox

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

Last change on this file since 11421 was 11421, checked in by vboxsync, 16 years ago

Storage/VBoxHDD-new: Added backend info, listing the supported file extensions. Implemented a testcase anf fixed a small bug which it found.

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