VirtualBox

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

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

API/HardDisk, Storage/VBoxHDD, Frontend/VBoxManage: eliminated base image type, which led to much unnecessary code duplication. Was triggered by VBoxManage finally being able to create all image variants the backends can support.

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