VirtualBox

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

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

Merge async I/O for VMDK backend from private branch

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