VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

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