VirtualBox

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

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

Storage/VMDK: fix querying comment if no comment is set. Must return empty string.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 191.7 KB
Line 
1/* $Id: VmdkHDDCore.cpp 15565 2008-12-16 09:45:10Z 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 && pszCommentEncoded)
4714 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
4715 else
4716 {
4717 if (pszComment)
4718 *pszComment = '\0';
4719 rc = VINF_SUCCESS;
4720 }
4721 if (pszCommentEncoded)
4722 RTStrFree((char *)(void *)pszCommentEncoded);
4723 }
4724 else
4725 rc = VERR_VD_NOT_OPENED;
4726
4727out:
4728 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
4729 return rc;
4730}
4731
4732/** @copydoc VBOXHDDBACKEND::pfnSetComment */
4733static int vmdkSetComment(void *pBackendData, const char *pszComment)
4734{
4735 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
4736 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4737 int rc;
4738
4739 AssertPtr(pImage);
4740
4741 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4742 {
4743 rc = VERR_VD_IMAGE_READ_ONLY;
4744 goto out;
4745 }
4746
4747 if (pImage)
4748 rc = vmdkSetImageComment(pImage, pszComment);
4749 else
4750 rc = VERR_VD_NOT_OPENED;
4751
4752out:
4753 LogFlowFunc(("returns %Rrc\n", rc));
4754 return rc;
4755}
4756
4757/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
4758static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
4759{
4760 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4761 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4762 int rc;
4763
4764 AssertPtr(pImage);
4765
4766 if (pImage)
4767 {
4768 *pUuid = pImage->ImageUuid;
4769 rc = VINF_SUCCESS;
4770 }
4771 else
4772 rc = VERR_VD_NOT_OPENED;
4773
4774 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4775 return rc;
4776}
4777
4778/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
4779static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
4780{
4781 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4782 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4783 int rc;
4784
4785 LogFlowFunc(("%RTuuid\n", pUuid));
4786 AssertPtr(pImage);
4787
4788 if (pImage)
4789 {
4790 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4791 {
4792 pImage->ImageUuid = *pUuid;
4793 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4794 VMDK_DDB_IMAGE_UUID, pUuid);
4795 if (RT_FAILURE(rc))
4796 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
4797 rc = VINF_SUCCESS;
4798 }
4799 else
4800 rc = VERR_VD_IMAGE_READ_ONLY;
4801 }
4802 else
4803 rc = VERR_VD_NOT_OPENED;
4804
4805 LogFlowFunc(("returns %Rrc\n", rc));
4806 return rc;
4807}
4808
4809/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
4810static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
4811{
4812 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4813 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4814 int rc;
4815
4816 AssertPtr(pImage);
4817
4818 if (pImage)
4819 {
4820 *pUuid = pImage->ModificationUuid;
4821 rc = VINF_SUCCESS;
4822 }
4823 else
4824 rc = VERR_VD_NOT_OPENED;
4825
4826 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4827 return rc;
4828}
4829
4830/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
4831static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
4832{
4833 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4834 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4835 int rc;
4836
4837 AssertPtr(pImage);
4838
4839 if (pImage)
4840 {
4841 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4842 {
4843 pImage->ModificationUuid = *pUuid;
4844 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4845 VMDK_DDB_MODIFICATION_UUID, pUuid);
4846 if (RT_FAILURE(rc))
4847 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
4848 rc = VINF_SUCCESS;
4849 }
4850 else
4851 rc = VERR_VD_IMAGE_READ_ONLY;
4852 }
4853 else
4854 rc = VERR_VD_NOT_OPENED;
4855
4856 LogFlowFunc(("returns %Rrc\n", rc));
4857 return rc;
4858}
4859
4860/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
4861static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
4862{
4863 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4864 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4865 int rc;
4866
4867 AssertPtr(pImage);
4868
4869 if (pImage)
4870 {
4871 *pUuid = pImage->ParentUuid;
4872 rc = VINF_SUCCESS;
4873 }
4874 else
4875 rc = VERR_VD_NOT_OPENED;
4876
4877 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4878 return rc;
4879}
4880
4881/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
4882static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
4883{
4884 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4885 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4886 int rc;
4887
4888 AssertPtr(pImage);
4889
4890 if (pImage)
4891 {
4892 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4893 {
4894 pImage->ParentUuid = *pUuid;
4895 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4896 VMDK_DDB_PARENT_UUID, pUuid);
4897 if (RT_FAILURE(rc))
4898 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
4899 rc = VINF_SUCCESS;
4900 }
4901 else
4902 rc = VERR_VD_IMAGE_READ_ONLY;
4903 }
4904 else
4905 rc = VERR_VD_NOT_OPENED;
4906
4907 LogFlowFunc(("returns %Rrc\n", rc));
4908 return rc;
4909}
4910
4911/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
4912static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
4913{
4914 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4915 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4916 int rc;
4917
4918 AssertPtr(pImage);
4919
4920 if (pImage)
4921 {
4922 *pUuid = pImage->ParentModificationUuid;
4923 rc = VINF_SUCCESS;
4924 }
4925 else
4926 rc = VERR_VD_NOT_OPENED;
4927
4928 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4929 return rc;
4930}
4931
4932/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
4933static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
4934{
4935 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4936 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4937 int rc;
4938
4939 AssertPtr(pImage);
4940
4941 if (pImage)
4942 {
4943 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4944 {
4945 pImage->ParentModificationUuid = *pUuid;
4946 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4947 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
4948 if (RT_FAILURE(rc))
4949 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
4950 rc = VINF_SUCCESS;
4951 }
4952 else
4953 rc = VERR_VD_IMAGE_READ_ONLY;
4954 }
4955 else
4956 rc = VERR_VD_NOT_OPENED;
4957
4958 LogFlowFunc(("returns %Rrc\n", rc));
4959 return rc;
4960}
4961
4962/** @copydoc VBOXHDDBACKEND::pfnDump */
4963static void vmdkDump(void *pBackendData)
4964{
4965 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4966
4967 AssertPtr(pImage);
4968 if (pImage)
4969 {
4970 RTLogPrintf("Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
4971 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
4972 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
4973 VMDK_BYTE2SECTOR(pImage->cbSize));
4974 RTLogPrintf("Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
4975 RTLogPrintf("Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
4976 RTLogPrintf("Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
4977 RTLogPrintf("Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
4978 }
4979}
4980
4981
4982static int vmdkGetTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
4983{
4984 int rc = VERR_NOT_IMPLEMENTED;
4985 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4986 return rc;
4987}
4988
4989static int vmdkGetParentTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
4990{
4991 int rc = VERR_NOT_IMPLEMENTED;
4992 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4993 return rc;
4994}
4995
4996static int vmdkSetParentTimeStamp(void *pvBackendData, PCRTTIMESPEC pTimeStamp)
4997{
4998 int rc = VERR_NOT_IMPLEMENTED;
4999 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5000 return rc;
5001}
5002
5003static int vmdkGetParentFilename(void *pvBackendData, char **ppszParentFilename)
5004{
5005 int rc = VERR_NOT_IMPLEMENTED;
5006 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5007 return rc;
5008}
5009
5010static int vmdkSetParentFilename(void *pvBackendData, const char *pszParentFilename)
5011{
5012 int rc = VERR_NOT_IMPLEMENTED;
5013 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5014 return rc;
5015}
5016
5017static bool vmdkIsAsyncIOSupported(void *pvBackendData)
5018{
5019 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5020 bool fAsyncIOSupported = false;
5021
5022 if (pImage)
5023 {
5024 /* We only support async I/O support if the image only consists of FLAT or ZERO extents. */
5025 fAsyncIOSupported = true;
5026 for (unsigned i = 0; i < pImage->cExtents; i++)
5027 {
5028 if ( (pImage->pExtents[i].enmType != VMDKETYPE_FLAT)
5029 && (pImage->pExtents[i].enmType != VMDKETYPE_ZERO))
5030 {
5031 fAsyncIOSupported = false;
5032 break; /* Stop search */
5033 }
5034 }
5035 }
5036
5037 return fAsyncIOSupported;
5038}
5039
5040static int vmdkAsyncRead(void *pvBackendData, uint64_t uOffset, size_t cbRead,
5041 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
5042{
5043 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5044 PVMDKEXTENT pExtent;
5045 int rc = VINF_SUCCESS;
5046 unsigned cTasksToSubmit = 0;
5047 PPDMDATASEG paSegCurrent = paSeg;
5048 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5049 unsigned uOffsetInCurrentSegment = 0;
5050
5051 AssertPtr(pImage);
5052 Assert(uOffset % 512 == 0);
5053 Assert(cbRead % 512 == 0);
5054
5055 if ( uOffset + cbRead > pImage->cbSize
5056 || cbRead == 0)
5057 {
5058 rc = VERR_INVALID_PARAMETER;
5059 goto out;
5060 }
5061
5062 while (cbRead && cSeg)
5063 {
5064 unsigned cbToRead;
5065 uint64_t uSectorExtentRel;
5066
5067 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5068 &pExtent, &uSectorExtentRel);
5069 if (RT_FAILURE(rc))
5070 goto out;
5071
5072 /* Check access permissions as defined in the extent descriptor. */
5073 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
5074 {
5075 rc = VERR_VD_VMDK_INVALID_STATE;
5076 goto out;
5077 }
5078
5079 /* Clip read range to remain in this extent. */
5080 cbToRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5081 /* Clip read range to remain into current data segment. */
5082 cbToRead = RT_MIN(cbToRead, cbLeftInCurrentSegment);
5083
5084 switch (pExtent->enmType)
5085 {
5086 case VMDKETYPE_FLAT:
5087 {
5088 /* Setup new task. */
5089 void *pTask;
5090 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareRead(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
5091 VMDK_SECTOR2BYTE(uSectorExtentRel),
5092 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
5093 cbToRead, &pTask);
5094 if (RT_FAILURE(rc))
5095 {
5096 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
5097 goto out;
5098 }
5099
5100 /* Check for enough room first. */
5101 if (cTasksToSubmit >= pImage->cTask)
5102 {
5103 /* We reached maximum, resize array. Try to realloc memory first. */
5104 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
5105
5106 if (!apTaskNew)
5107 {
5108 /* We failed. Allocate completely new. */
5109 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
5110 if (!apTaskNew)
5111 {
5112 /* Damn, we are out of memory. */
5113 rc = VERR_NO_MEMORY;
5114 goto out;
5115 }
5116
5117 /* Copy task handles over. */
5118 for (unsigned i = 0; i < cTasksToSubmit; i++)
5119 apTaskNew[i] = pImage->apTask[i];
5120
5121 /* Free old memory. */
5122 RTMemFree(pImage->apTask);
5123 }
5124
5125 pImage->cTask = cTasksToSubmit + 10;
5126 pImage->apTask = apTaskNew;
5127 }
5128
5129 pImage->apTask[cTasksToSubmit] = pTask;
5130 cTasksToSubmit++;
5131 break;
5132 }
5133 case VMDKETYPE_ZERO:
5134 memset((uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment, 0, cbToRead);
5135 break;
5136 default:
5137 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
5138 }
5139
5140 cbRead -= cbToRead;
5141 uOffset += cbToRead;
5142 cbLeftInCurrentSegment -= cbToRead;
5143 uOffsetInCurrentSegment += cbToRead;
5144 /* Go to next extent if there is no space left in current one. */
5145 if (!cbLeftInCurrentSegment)
5146 {
5147 uOffsetInCurrentSegment = 0;
5148 paSegCurrent++;
5149 cSeg--;
5150 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5151 }
5152 }
5153
5154 AssertMsg(cbRead == 0, ("No segment left but there is still data to read\n"));
5155
5156 if (cTasksToSubmit == 0)
5157 {
5158 /* The request was completely in a ZERO extent nothing to do. */
5159 rc = VINF_VD_ASYNC_IO_FINISHED;
5160 }
5161 else
5162 {
5163 /* Submit tasks. */
5164 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5165 pImage->apTask, cTasksToSubmit,
5166 NULL, pvUser,
5167 NULL /* Nothing required after read. */);
5168 AssertMsgRC(rc, ("Failed to enqueue tasks rc=%Rrc\n", rc));
5169 }
5170
5171out:
5172 LogFlowFunc(("returns %Rrc\n", rc));
5173 return rc;
5174}
5175
5176static int vmdkAsyncWrite(void *pvBackendData, uint64_t uOffset, size_t cbWrite,
5177 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
5178{
5179 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5180 PVMDKEXTENT pExtent;
5181 int rc = VINF_SUCCESS;
5182 unsigned cTasksToSubmit = 0;
5183 PPDMDATASEG paSegCurrent = paSeg;
5184 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5185 unsigned uOffsetInCurrentSegment = 0;
5186
5187 AssertPtr(pImage);
5188 Assert(uOffset % 512 == 0);
5189 Assert(cbWrite % 512 == 0);
5190
5191 if ( uOffset + cbWrite > pImage->cbSize
5192 || cbWrite == 0)
5193 {
5194 rc = VERR_INVALID_PARAMETER;
5195 goto out;
5196 }
5197
5198 while (cbWrite && cSeg)
5199 {
5200 unsigned cbToWrite;
5201 uint64_t uSectorExtentRel;
5202
5203 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5204 &pExtent, &uSectorExtentRel);
5205 if (RT_FAILURE(rc))
5206 goto out;
5207
5208 /* Check access permissions as defined in the extent descriptor. */
5209 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
5210 {
5211 rc = VERR_VD_VMDK_INVALID_STATE;
5212 goto out;
5213 }
5214
5215 /* Clip write range to remain in this extent. */
5216 cbToWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5217 /* Clip write range to remain into current data segment. */
5218 cbToWrite = RT_MIN(cbToWrite, cbLeftInCurrentSegment);
5219
5220 switch (pExtent->enmType)
5221 {
5222 case VMDKETYPE_FLAT:
5223 {
5224 /* Setup new task. */
5225 void *pTask;
5226 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareWrite(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
5227 VMDK_SECTOR2BYTE(uSectorExtentRel),
5228 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
5229 cbToWrite, &pTask);
5230 if (RT_FAILURE(rc))
5231 {
5232 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
5233 goto out;
5234 }
5235
5236 /* Check for enough room first. */
5237 if (cTasksToSubmit >= pImage->cTask)
5238 {
5239 /* We reached maximum, resize array. Try to realloc memory first. */
5240 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
5241
5242 if (!apTaskNew)
5243 {
5244 /* We failed. Allocate completely new. */
5245 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
5246 if (!apTaskNew)
5247 {
5248 /* Damn, we are out of memory. */
5249 rc = VERR_NO_MEMORY;
5250 goto out;
5251 }
5252
5253 /* Copy task handles over. */
5254 for (unsigned i = 0; i < cTasksToSubmit; i++)
5255 apTaskNew[i] = pImage->apTask[i];
5256
5257 /* Free old memory. */
5258 RTMemFree(pImage->apTask);
5259 }
5260
5261 pImage->cTask = cTasksToSubmit + 10;
5262 pImage->apTask = apTaskNew;
5263 }
5264
5265 pImage->apTask[cTasksToSubmit] = pTask;
5266 cTasksToSubmit++;
5267 break;
5268 }
5269 case VMDKETYPE_ZERO:
5270 /* Nothing left to do. */
5271 break;
5272 default:
5273 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
5274 }
5275
5276 cbWrite -= cbToWrite;
5277 uOffset += cbToWrite;
5278 cbLeftInCurrentSegment -= cbToWrite;
5279 uOffsetInCurrentSegment += cbToWrite;
5280 /* Go to next extent if there is no space left in current one. */
5281 if (!cbLeftInCurrentSegment)
5282 {
5283 uOffsetInCurrentSegment = 0;
5284 paSegCurrent++;
5285 cSeg--;
5286 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5287 }
5288 }
5289
5290 AssertMsg(cbWrite == 0, ("No segment left but there is still data to read\n"));
5291
5292 if (cTasksToSubmit == 0)
5293 {
5294 /* The request was completely in a ZERO extent nothing to do. */
5295 rc = VINF_VD_ASYNC_IO_FINISHED;
5296 }
5297 else
5298 {
5299 /* Submit tasks. */
5300 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5301 pImage->apTask, cTasksToSubmit,
5302 NULL, pvUser,
5303 NULL /* Nothing required after read. */);
5304 AssertMsgRC(rc, ("Failed to enqueue tasks rc=%Rrc\n", rc));
5305 }
5306
5307out:
5308 LogFlowFunc(("returns %Rrc\n", rc));
5309 return rc;
5310
5311}
5312
5313
5314VBOXHDDBACKEND g_VmdkBackend =
5315{
5316 /* pszBackendName */
5317 "VMDK",
5318 /* cbSize */
5319 sizeof(VBOXHDDBACKEND),
5320 /* uBackendCaps */
5321 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
5322 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE |VD_CAP_ASYNC,
5323 /* papszFileExtensions */
5324 s_apszVmdkFileExtensions,
5325 /* paConfigInfo */
5326 NULL,
5327 /* hPlugin */
5328 NIL_RTLDRMOD,
5329 /* pfnCheckIfValid */
5330 vmdkCheckIfValid,
5331 /* pfnOpen */
5332 vmdkOpen,
5333 /* pfnCreate */
5334 vmdkCreate,
5335 /* pfnRename */
5336 vmdkRename,
5337 /* pfnClose */
5338 vmdkClose,
5339 /* pfnRead */
5340 vmdkRead,
5341 /* pfnWrite */
5342 vmdkWrite,
5343 /* pfnFlush */
5344 vmdkFlush,
5345 /* pfnGetVersion */
5346 vmdkGetVersion,
5347 /* pfnGetImageType */
5348 vmdkGetImageType,
5349 /* pfnGetSize */
5350 vmdkGetSize,
5351 /* pfnGetFileSize */
5352 vmdkGetFileSize,
5353 /* pfnGetPCHSGeometry */
5354 vmdkGetPCHSGeometry,
5355 /* pfnSetPCHSGeometry */
5356 vmdkSetPCHSGeometry,
5357 /* pfnGetLCHSGeometry */
5358 vmdkGetLCHSGeometry,
5359 /* pfnSetLCHSGeometry */
5360 vmdkSetLCHSGeometry,
5361 /* pfnGetImageFlags */
5362 vmdkGetImageFlags,
5363 /* pfnGetOpenFlags */
5364 vmdkGetOpenFlags,
5365 /* pfnSetOpenFlags */
5366 vmdkSetOpenFlags,
5367 /* pfnGetComment */
5368 vmdkGetComment,
5369 /* pfnSetComment */
5370 vmdkSetComment,
5371 /* pfnGetUuid */
5372 vmdkGetUuid,
5373 /* pfnSetUuid */
5374 vmdkSetUuid,
5375 /* pfnGetModificationUuid */
5376 vmdkGetModificationUuid,
5377 /* pfnSetModificationUuid */
5378 vmdkSetModificationUuid,
5379 /* pfnGetParentUuid */
5380 vmdkGetParentUuid,
5381 /* pfnSetParentUuid */
5382 vmdkSetParentUuid,
5383 /* pfnGetParentModificationUuid */
5384 vmdkGetParentModificationUuid,
5385 /* pfnSetParentModificationUuid */
5386 vmdkSetParentModificationUuid,
5387 /* pfnDump */
5388 vmdkDump,
5389 /* pfnGetTimeStamp */
5390 vmdkGetTimeStamp,
5391 /* pfnGetParentTimeStamp */
5392 vmdkGetParentTimeStamp,
5393 /* pfnSetParentTimeStamp */
5394 vmdkSetParentTimeStamp,
5395 /* pfnGetParentFilename */
5396 vmdkGetParentFilename,
5397 /* pfnSetParentFilename */
5398 vmdkSetParentFilename,
5399 /* pfnIsAsyncIOSupported */
5400 vmdkIsAsyncIOSupported,
5401 /* pfnAsyncRead */
5402 vmdkAsyncRead,
5403 /* pfnAsyncWrite */
5404 vmdkAsyncWrite,
5405 /* pfnComposeLocation */
5406 genericFileComposeLocation,
5407 /* pfnComposeName */
5408 genericFileComposeName
5409};
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