VirtualBox

source: vbox/trunk/src/VBox/Storage/VMDK.cpp@ 77807

Last change on this file since 77807 was 77773, checked in by vboxsync, 5 years ago

Storage/VMDK: Limit the maximum supported descriptor size to avoid allocating excessive amounts of memory later on

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

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use