VirtualBox

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

Last change on this file since 33088 was 33088, checked in by vboxsync, 14 years ago

Storage/VMDK: merge the sequential streamOptimized image writing code into the normal backend

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

© 2023 Oracle
ContactPrivacy policyTerms of Use