VirtualBox

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

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

Storage/VMDK: implement creating/writing (slightly relaxed append only) of streamOptimized VMDK files.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette