VirtualBox

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

Last change on this file since 72577 was 70853, checked in by vboxsync, 7 years ago

Storage/VMDK: Quick fix for handling descriptors of VMDK images created by Amazon EC2 VM Export

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

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