VirtualBox

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

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

Storage: scm updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 255.1 KB
Line 
1/* $Id: VMDK.cpp 69230 2017-10-24 15:27:30Z vboxsync $ */
2/** @file
3 * VMDK disk image, core code.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1935 N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);
1936 else
1937 {
1938 unsigned uLastNonEmptyLine = 0;
1939
1940 /* Initialize those, because we need to be able to reopen an image. */
1941 pDescriptor->uFirstDesc = 0;
1942 pDescriptor->uFirstExtent = 0;
1943 pDescriptor->uFirstDDB = 0;
1944 for (unsigned i = 0; i < pDescriptor->cLines; i++)
1945 {
1946 if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
1947 {
1948 if ( !strncmp(pDescriptor->aLines[i], "RW", 2)
1949 || !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
1950 || !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
1951 {
1952 /* An extent descriptor. */
1953 if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
1954 {
1955 /* Incorrect ordering of entries. */
1956 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1957 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1958 break;
1959 }
1960 if (!pDescriptor->uFirstExtent)
1961 {
1962 pDescriptor->uFirstExtent = i;
1963 uLastNonEmptyLine = 0;
1964 }
1965 }
1966 else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
1967 {
1968 /* A disk database entry. */
1969 if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
1970 {
1971 /* Incorrect ordering of entries. */
1972 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1973 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1974 break;
1975 }
1976 if (!pDescriptor->uFirstDDB)
1977 {
1978 pDescriptor->uFirstDDB = i;
1979 uLastNonEmptyLine = 0;
1980 }
1981 }
1982 else
1983 {
1984 /* A normal entry. */
1985 if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
1986 {
1987 /* Incorrect ordering of entries. */
1988 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1989 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1990 break;
1991 }
1992 if (!pDescriptor->uFirstDesc)
1993 {
1994 pDescriptor->uFirstDesc = i;
1995 uLastNonEmptyLine = 0;
1996 }
1997 }
1998 if (uLastNonEmptyLine)
1999 pDescriptor->aNextLines[uLastNonEmptyLine] = i;
2000 uLastNonEmptyLine = i;
2001 }
2002 }
2003 }
2004 }
2005
2006 return rc;
2007}
2008
2009static int vmdkDescSetPCHSGeometry(PVMDKIMAGE pImage,
2010 PCVDGEOMETRY pPCHSGeometry)
2011{
2012 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2013 VMDK_DDB_GEO_PCHS_CYLINDERS,
2014 pPCHSGeometry->cCylinders);
2015 if (RT_FAILURE(rc))
2016 return rc;
2017 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2018 VMDK_DDB_GEO_PCHS_HEADS,
2019 pPCHSGeometry->cHeads);
2020 if (RT_FAILURE(rc))
2021 return rc;
2022 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2023 VMDK_DDB_GEO_PCHS_SECTORS,
2024 pPCHSGeometry->cSectors);
2025 return rc;
2026}
2027
2028static int vmdkDescSetLCHSGeometry(PVMDKIMAGE pImage,
2029 PCVDGEOMETRY pLCHSGeometry)
2030{
2031 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2032 VMDK_DDB_GEO_LCHS_CYLINDERS,
2033 pLCHSGeometry->cCylinders);
2034 if (RT_FAILURE(rc))
2035 return rc;
2036 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2037 VMDK_DDB_GEO_LCHS_HEADS,
2038
2039 pLCHSGeometry->cHeads);
2040 if (RT_FAILURE(rc))
2041 return rc;
2042 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2043 VMDK_DDB_GEO_LCHS_SECTORS,
2044 pLCHSGeometry->cSectors);
2045 return rc;
2046}
2047
2048static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData,
2049 size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
2050{
2051 pDescriptor->uFirstDesc = 0;
2052 pDescriptor->uFirstExtent = 0;
2053 pDescriptor->uFirstDDB = 0;
2054 pDescriptor->cLines = 0;
2055 pDescriptor->cbDescAlloc = cbDescData;
2056 pDescriptor->fDirty = false;
2057 pDescriptor->aLines[pDescriptor->cLines] = pDescData;
2058 memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));
2059
2060 int rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");
2061 if (RT_SUCCESS(rc))
2062 rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");
2063 if (RT_SUCCESS(rc))
2064 {
2065 pDescriptor->uFirstDesc = pDescriptor->cLines - 1;
2066 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2067 }
2068 if (RT_SUCCESS(rc))
2069 rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");
2070 if (RT_SUCCESS(rc))
2071 rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");
2072 if (RT_SUCCESS(rc))
2073 {
2074 pDescriptor->uFirstExtent = pDescriptor->cLines - 1;
2075 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2076 }
2077 if (RT_SUCCESS(rc))
2078 {
2079 /* The trailing space is created by VMware, too. */
2080 rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");
2081 }
2082 if (RT_SUCCESS(rc))
2083 rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");
2084 if (RT_SUCCESS(rc))
2085 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2086 if (RT_SUCCESS(rc))
2087 rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");
2088 if (RT_SUCCESS(rc))
2089 {
2090 pDescriptor->uFirstDDB = pDescriptor->cLines - 1;
2091
2092 /* Now that the framework is in place, use the normal functions to insert
2093 * the remaining keys. */
2094 char szBuf[9];
2095 RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());
2096 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
2097 "CID", szBuf);
2098 }
2099 if (RT_SUCCESS(rc))
2100 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
2101 "parentCID", "ffffffff");
2102 if (RT_SUCCESS(rc))
2103 rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");
2104
2105 return rc;
2106}
2107
2108static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData)
2109{
2110 int rc;
2111 unsigned cExtents;
2112 unsigned uLine;
2113 unsigned i;
2114
2115 rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData,
2116 &pImage->Descriptor);
2117 if (RT_FAILURE(rc))
2118 return rc;
2119
2120 /* Check version, must be 1. */
2121 uint32_t uVersion;
2122 rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
2123 if (RT_FAILURE(rc))
2124 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);
2125 if (uVersion != 1)
2126 return vdIfError(pImage->pIfError, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);
2127
2128 /* Get image creation type and determine image flags. */
2129 char *pszCreateType = NULL; /* initialized to make gcc shut up */
2130 rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",
2131 &pszCreateType);
2132 if (RT_FAILURE(rc))
2133 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);
2134 if ( !strcmp(pszCreateType, "twoGbMaxExtentSparse")
2135 || !strcmp(pszCreateType, "twoGbMaxExtentFlat"))
2136 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
2137 else if ( !strcmp(pszCreateType, "partitionedDevice")
2138 || !strcmp(pszCreateType, "fullDevice"))
2139 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_RAWDISK;
2140 else if (!strcmp(pszCreateType, "streamOptimized"))
2141 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
2142 else if (!strcmp(pszCreateType, "vmfs"))
2143 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_ESX;
2144 RTMemTmpFree(pszCreateType);
2145
2146 /* Count the number of extent config entries. */
2147 for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
2148 uLine != 0;
2149 uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
2150 /* nothing */;
2151
2152 if (!pImage->pDescData && cExtents != 1)
2153 {
2154 /* Monolithic image, must have only one extent (already opened). */
2155 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);
2156 }
2157
2158 if (pImage->pDescData)
2159 {
2160 /* Non-monolithic image, extents need to be allocated. */
2161 rc = vmdkCreateExtents(pImage, cExtents);
2162 if (RT_FAILURE(rc))
2163 return rc;
2164 }
2165
2166 for (i = 0, uLine = pImage->Descriptor.uFirstExtent;
2167 i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
2168 {
2169 char *pszLine = pImage->Descriptor.aLines[uLine];
2170
2171 /* Access type of the extent. */
2172 if (!strncmp(pszLine, "RW", 2))
2173 {
2174 pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
2175 pszLine += 2;
2176 }
2177 else if (!strncmp(pszLine, "RDONLY", 6))
2178 {
2179 pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
2180 pszLine += 6;
2181 }
2182 else if (!strncmp(pszLine, "NOACCESS", 8))
2183 {
2184 pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
2185 pszLine += 8;
2186 }
2187 else
2188 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2189 if (*pszLine++ != ' ')
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
2192 /* Nominal size of the extent. */
2193 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
2194 &pImage->pExtents[i].cNominalSectors);
2195 if (RT_FAILURE(rc))
2196 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2197 if (*pszLine++ != ' ')
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
2200 /* Type of the extent. */
2201 if (!strncmp(pszLine, "SPARSE", 6))
2202 {
2203 pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
2204 pszLine += 6;
2205 }
2206 else if (!strncmp(pszLine, "FLAT", 4))
2207 {
2208 pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
2209 pszLine += 4;
2210 }
2211 else if (!strncmp(pszLine, "ZERO", 4))
2212 {
2213 pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
2214 pszLine += 4;
2215 }
2216 else if (!strncmp(pszLine, "VMFS", 4))
2217 {
2218 pImage->pExtents[i].enmType = VMDKETYPE_VMFS;
2219 pszLine += 4;
2220 }
2221 else
2222 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2223
2224 if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
2225 {
2226 /* This one has no basename or offset. */
2227 if (*pszLine == ' ')
2228 pszLine++;
2229 if (*pszLine != '\0')
2230 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2231 pImage->pExtents[i].pszBasename = NULL;
2232 }
2233 else
2234 {
2235 /* All other extent types have basename and optional offset. */
2236 if (*pszLine++ != ' ')
2237 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2238
2239 /* Basename of the image. Surrounded by quotes. */
2240 char *pszBasename;
2241 rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);
2242 if (RT_FAILURE(rc))
2243 return rc;
2244 pImage->pExtents[i].pszBasename = pszBasename;
2245 if (*pszLine == ' ')
2246 {
2247 pszLine++;
2248 if (*pszLine != '\0')
2249 {
2250 /* Optional offset in extent specified. */
2251 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
2252 &pImage->pExtents[i].uSectorOffset);
2253 if (RT_FAILURE(rc))
2254 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2255 }
2256 }
2257
2258 if (*pszLine != '\0')
2259 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2260 }
2261 }
2262
2263 /* Determine PCHS geometry (autogenerate if necessary). */
2264 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2265 VMDK_DDB_GEO_PCHS_CYLINDERS,
2266 &pImage->PCHSGeometry.cCylinders);
2267 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2268 pImage->PCHSGeometry.cCylinders = 0;
2269 else if (RT_FAILURE(rc))
2270 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2271 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2272 VMDK_DDB_GEO_PCHS_HEADS,
2273 &pImage->PCHSGeometry.cHeads);
2274 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2275 pImage->PCHSGeometry.cHeads = 0;
2276 else if (RT_FAILURE(rc))
2277 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2278 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2279 VMDK_DDB_GEO_PCHS_SECTORS,
2280 &pImage->PCHSGeometry.cSectors);
2281 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2282 pImage->PCHSGeometry.cSectors = 0;
2283 else if (RT_FAILURE(rc))
2284 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2285 if ( pImage->PCHSGeometry.cCylinders == 0
2286 || pImage->PCHSGeometry.cHeads == 0
2287 || pImage->PCHSGeometry.cHeads > 16
2288 || pImage->PCHSGeometry.cSectors == 0
2289 || pImage->PCHSGeometry.cSectors > 63)
2290 {
2291 /* Mark PCHS geometry as not yet valid (can't do the calculation here
2292 * as the total image size isn't known yet). */
2293 pImage->PCHSGeometry.cCylinders = 0;
2294 pImage->PCHSGeometry.cHeads = 16;
2295 pImage->PCHSGeometry.cSectors = 63;
2296 }
2297
2298 /* Determine LCHS geometry (set to 0 if not specified). */
2299 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2300 VMDK_DDB_GEO_LCHS_CYLINDERS,
2301 &pImage->LCHSGeometry.cCylinders);
2302 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2303 pImage->LCHSGeometry.cCylinders = 0;
2304 else if (RT_FAILURE(rc))
2305 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2306 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2307 VMDK_DDB_GEO_LCHS_HEADS,
2308 &pImage->LCHSGeometry.cHeads);
2309 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2310 pImage->LCHSGeometry.cHeads = 0;
2311 else if (RT_FAILURE(rc))
2312 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2313 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2314 VMDK_DDB_GEO_LCHS_SECTORS,
2315 &pImage->LCHSGeometry.cSectors);
2316 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2317 pImage->LCHSGeometry.cSectors = 0;
2318 else if (RT_FAILURE(rc))
2319 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2320 if ( pImage->LCHSGeometry.cCylinders == 0
2321 || pImage->LCHSGeometry.cHeads == 0
2322 || pImage->LCHSGeometry.cSectors == 0)
2323 {
2324 pImage->LCHSGeometry.cCylinders = 0;
2325 pImage->LCHSGeometry.cHeads = 0;
2326 pImage->LCHSGeometry.cSectors = 0;
2327 }
2328
2329 /* Get image UUID. */
2330 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID,
2331 &pImage->ImageUuid);
2332 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2333 {
2334 /* Image without UUID. Probably created by VMware and not yet used
2335 * by VirtualBox. Can only be added for images opened in read/write
2336 * mode, so don't bother producing a sensible UUID otherwise. */
2337 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2338 RTUuidClear(&pImage->ImageUuid);
2339 else
2340 {
2341 rc = RTUuidCreate(&pImage->ImageUuid);
2342 if (RT_FAILURE(rc))
2343 return rc;
2344 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2345 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
2346 if (RT_FAILURE(rc))
2347 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
2348 }
2349 }
2350 else if (RT_FAILURE(rc))
2351 return rc;
2352
2353 /* Get image modification UUID. */
2354 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
2355 VMDK_DDB_MODIFICATION_UUID,
2356 &pImage->ModificationUuid);
2357 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2358 {
2359 /* Image without UUID. Probably created by VMware and not yet used
2360 * by VirtualBox. Can only be added for images opened in read/write
2361 * mode, so don't bother producing a sensible UUID otherwise. */
2362 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2363 RTUuidClear(&pImage->ModificationUuid);
2364 else
2365 {
2366 rc = RTUuidCreate(&pImage->ModificationUuid);
2367 if (RT_FAILURE(rc))
2368 return rc;
2369 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2370 VMDK_DDB_MODIFICATION_UUID,
2371 &pImage->ModificationUuid);
2372 if (RT_FAILURE(rc))
2373 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);
2374 }
2375 }
2376 else if (RT_FAILURE(rc))
2377 return rc;
2378
2379 /* Get UUID of parent image. */
2380 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID,
2381 &pImage->ParentUuid);
2382 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2383 {
2384 /* Image without UUID. Probably created by VMware and not yet used
2385 * by VirtualBox. Can only be added for images opened in read/write
2386 * mode, so don't bother producing a sensible UUID otherwise. */
2387 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2388 RTUuidClear(&pImage->ParentUuid);
2389 else
2390 {
2391 rc = RTUuidClear(&pImage->ParentUuid);
2392 if (RT_FAILURE(rc))
2393 return rc;
2394 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2395 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
2396 if (RT_FAILURE(rc))
2397 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);
2398 }
2399 }
2400 else if (RT_FAILURE(rc))
2401 return rc;
2402
2403 /* Get parent image modification UUID. */
2404 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
2405 VMDK_DDB_PARENT_MODIFICATION_UUID,
2406 &pImage->ParentModificationUuid);
2407 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2408 {
2409 /* Image without UUID. Probably created by VMware and not yet used
2410 * by VirtualBox. Can only be added for images opened in read/write
2411 * mode, so don't bother producing a sensible UUID otherwise. */
2412 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2413 RTUuidClear(&pImage->ParentModificationUuid);
2414 else
2415 {
2416 RTUuidClear(&pImage->ParentModificationUuid);
2417 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2418 VMDK_DDB_PARENT_MODIFICATION_UUID,
2419 &pImage->ParentModificationUuid);
2420 if (RT_FAILURE(rc))
2421 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in descriptor in '%s'"), pImage->pszFilename);
2422 }
2423 }
2424 else if (RT_FAILURE(rc))
2425 return rc;
2426
2427 return VINF_SUCCESS;
2428}
2429
2430/**
2431 * Internal : Prepares the descriptor to write to the image.
2432 */
2433static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
2434 void **ppvData, size_t *pcbData)
2435{
2436 int rc = VINF_SUCCESS;
2437
2438 /*
2439 * Allocate temporary descriptor buffer.
2440 * In case there is no limit allocate a default
2441 * and increase if required.
2442 */
2443 size_t cbDescriptor = cbLimit ? cbLimit : 4 * _1K;
2444 char *pszDescriptor = (char *)RTMemAllocZ(cbDescriptor);
2445 size_t offDescriptor = 0;
2446
2447 if (!pszDescriptor)
2448 return VERR_NO_MEMORY;
2449
2450 for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
2451 {
2452 const char *psz = pImage->Descriptor.aLines[i];
2453 size_t cb = strlen(psz);
2454
2455 /*
2456 * Increase the descriptor if there is no limit and
2457 * there is not enough room left for this line.
2458 */
2459 if (offDescriptor + cb + 1 > cbDescriptor)
2460 {
2461 if (cbLimit)
2462 {
2463 rc = vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);
2464 break;
2465 }
2466 else
2467 {
2468 char *pszDescriptorNew = NULL;
2469 LogFlow(("Increasing descriptor cache\n"));
2470
2471 pszDescriptorNew = (char *)RTMemRealloc(pszDescriptor, cbDescriptor + cb + 4 * _1K);
2472 if (!pszDescriptorNew)
2473 {
2474 rc = VERR_NO_MEMORY;
2475 break;
2476 }
2477 pszDescriptor = pszDescriptorNew;
2478 cbDescriptor += cb + 4 * _1K;
2479 }
2480 }
2481
2482 if (cb > 0)
2483 {
2484 memcpy(pszDescriptor + offDescriptor, psz, cb);
2485 offDescriptor += cb;
2486 }
2487
2488 memcpy(pszDescriptor + offDescriptor, "\n", 1);
2489 offDescriptor++;
2490 }
2491
2492 if (RT_SUCCESS(rc))
2493 {
2494 *ppvData = pszDescriptor;
2495 *pcbData = offDescriptor;
2496 }
2497 else if (pszDescriptor)
2498 RTMemFree(pszDescriptor);
2499
2500 return rc;
2501}
2502
2503/**
2504 * Internal: write/update the descriptor part of the image.
2505 */
2506static int vmdkWriteDescriptor(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
2507{
2508 int rc = VINF_SUCCESS;
2509 uint64_t cbLimit;
2510 uint64_t uOffset;
2511 PVMDKFILE pDescFile;
2512 void *pvDescriptor = NULL;
2513 size_t cbDescriptor;
2514
2515 if (pImage->pDescData)
2516 {
2517 /* Separate descriptor file. */
2518 uOffset = 0;
2519 cbLimit = 0;
2520 pDescFile = pImage->pFile;
2521 }
2522 else
2523 {
2524 /* Embedded descriptor file. */
2525 uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
2526 cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
2527 pDescFile = pImage->pExtents[0].pFile;
2528 }
2529 /* Bail out if there is no file to write to. */
2530 if (pDescFile == NULL)
2531 return VERR_INVALID_PARAMETER;
2532
2533 rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
2534 if (RT_SUCCESS(rc))
2535 {
2536 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pDescFile->pStorage,
2537 uOffset, pvDescriptor,
2538 cbLimit ? cbLimit : cbDescriptor,
2539 pIoCtx, NULL, NULL);
2540 if ( RT_FAILURE(rc)
2541 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2542 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
2543 }
2544
2545 if (RT_SUCCESS(rc) && !cbLimit)
2546 {
2547 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pDescFile->pStorage, cbDescriptor);
2548 if (RT_FAILURE(rc))
2549 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
2550 }
2551
2552 if (RT_SUCCESS(rc))
2553 pImage->Descriptor.fDirty = false;
2554
2555 if (pvDescriptor)
2556 RTMemFree(pvDescriptor);
2557 return rc;
2558
2559}
2560
2561/**
2562 * Internal: validate the consistency check values in a binary header.
2563 */
2564static int vmdkValidateHeader(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, const SparseExtentHeader *pHeader)
2565{
2566 int rc = VINF_SUCCESS;
2567 if (RT_LE2H_U32(pHeader->magicNumber) != VMDK_SPARSE_MAGICNUMBER)
2568 {
2569 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic in sparse extent header in '%s'"), pExtent->pszFullname);
2570 return rc;
2571 }
2572 if (RT_LE2H_U32(pHeader->version) != 1 && RT_LE2H_U32(pHeader->version) != 3)
2573 {
2574 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);
2575 return rc;
2576 }
2577 if ( (RT_LE2H_U32(pHeader->flags) & 1)
2578 && ( pHeader->singleEndLineChar != '\n'
2579 || pHeader->nonEndLineChar != ' '
2580 || pHeader->doubleEndLineChar1 != '\r'
2581 || pHeader->doubleEndLineChar2 != '\n') )
2582 {
2583 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);
2584 return rc;
2585 }
2586 return rc;
2587}
2588
2589/**
2590 * Internal: read metadata belonging to an extent with binary header, i.e.
2591 * as found in monolithic files.
2592 */
2593static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
2594 bool fMagicAlreadyRead)
2595{
2596 SparseExtentHeader Header;
2597 int rc;
2598
2599 if (!fMagicAlreadyRead)
2600 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage, 0,
2601 &Header, sizeof(Header));
2602 else
2603 {
2604 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
2605 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
2606 RT_OFFSETOF(SparseExtentHeader, version),
2607 &Header.version,
2608 sizeof(Header)
2609 - RT_OFFSETOF(SparseExtentHeader, version));
2610 }
2611
2612 if (RT_SUCCESS(rc))
2613 {
2614 rc = vmdkValidateHeader(pImage, pExtent, &Header);
2615 if (RT_SUCCESS(rc))
2616 {
2617 uint64_t cbFile = 0;
2618
2619 if ( (RT_LE2H_U32(Header.flags) & RT_BIT(17))
2620 && RT_LE2H_U64(Header.gdOffset) == VMDK_GD_AT_END)
2621 pExtent->fFooter = true;
2622
2623 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2624 || ( pExtent->fFooter
2625 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2626 {
2627 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pExtent->pFile->pStorage, &cbFile);
2628 if (RT_FAILURE(rc))
2629 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname);
2630 }
2631
2632 if (RT_SUCCESS(rc))
2633 {
2634 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2635 pExtent->uAppendPosition = RT_ALIGN_64(cbFile, 512);
2636
2637 if ( pExtent->fFooter
2638 && ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2639 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2640 {
2641 /* Read the footer, which comes before the end-of-stream marker. */
2642 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
2643 cbFile - 2*512, &Header,
2644 sizeof(Header));
2645 if (RT_FAILURE(rc))
2646 {
2647 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading extent footer in '%s'"), pExtent->pszFullname);
2648 rc = VERR_VD_VMDK_INVALID_HEADER;
2649 }
2650
2651 if (RT_SUCCESS(rc))
2652 rc = vmdkValidateHeader(pImage, pExtent, &Header);
2653 /* Prohibit any writes to this extent. */
2654 pExtent->uAppendPosition = 0;
2655 }
2656
2657 if (RT_SUCCESS(rc))
2658 {
2659 pExtent->uVersion = RT_LE2H_U32(Header.version);
2660 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; /* Just dummy value, changed later. */
2661 pExtent->cSectors = RT_LE2H_U64(Header.capacity);
2662 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
2663 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
2664 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
2665 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
2666 pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);
2667 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
2668 pExtent->uCompression = RT_LE2H_U16(Header.compressAlgorithm);
2669 if (RT_LE2H_U32(Header.flags) & RT_BIT(1))
2670 {
2671 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
2672 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
2673 }
2674 else
2675 {
2676 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
2677 pExtent->uSectorRGD = 0;
2678 }
2679
2680 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
2681 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2682 N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);
2683
2684 if ( RT_SUCCESS(rc)
2685 && ( pExtent->uSectorGD == VMDK_GD_AT_END
2686 || pExtent->uSectorRGD == VMDK_GD_AT_END)
2687 && ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2688 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2689 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2690 N_("VMDK: cannot resolve grain directory offset in '%s'"), pExtent->pszFullname);
2691
2692 if (RT_SUCCESS(rc))
2693 {
2694 uint64_t cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
2695 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
2696 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2697 N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);
2698 else
2699 {
2700 pExtent->cSectorsPerGDE = cSectorsPerGDE;
2701 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
2702
2703 /* Fix up the number of descriptor sectors, as some flat images have
2704 * really just one, and this causes failures when inserting the UUID
2705 * values and other extra information. */
2706 if (pExtent->cDescriptorSectors != 0 && pExtent->cDescriptorSectors < 4)
2707 {
2708 /* Do it the easy way - just fix it for flat images which have no
2709 * other complicated metadata which needs space too. */
2710 if ( pExtent->uDescriptorSector + 4 < pExtent->cOverheadSectors
2711 && pExtent->cGTEntries * pExtent->cGDEntries == 0)
2712 pExtent->cDescriptorSectors = 4;
2713 }
2714 }
2715 }
2716 }
2717 }
2718 }
2719 }
2720 else
2721 {
2722 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);
2723 rc = VERR_VD_VMDK_INVALID_HEADER;
2724 }
2725
2726 if (RT_FAILURE(rc))
2727 vmdkFreeExtentData(pImage, pExtent, false);
2728
2729 return rc;
2730}
2731
2732/**
2733 * Internal: read additional metadata belonging to an extent. For those
2734 * extents which have no additional metadata just verify the information.
2735 */
2736static int vmdkReadMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
2737{
2738 int rc = VINF_SUCCESS;
2739
2740/* disabled the check as there are too many truncated vmdk images out there */
2741#ifdef VBOX_WITH_VMDK_STRICT_SIZE_CHECK
2742 uint64_t cbExtentSize;
2743 /* The image must be a multiple of a sector in size and contain the data
2744 * area (flat images only). If not, it means the image is at least
2745 * truncated, or even seriously garbled. */
2746 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pExtent->pFile->pStorage, &cbExtentSize);
2747 if (RT_FAILURE(rc))
2748 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
2749 else if ( cbExtentSize != RT_ALIGN_64(cbExtentSize, 512)
2750 && (pExtent->enmType != VMDKETYPE_FLAT || pExtent->cNominalSectors + pExtent->uSectorOffset > VMDK_BYTE2SECTOR(cbExtentSize)))
2751 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2752 N_("VMDK: file size is not a multiple of 512 in '%s', file is truncated or otherwise garbled"), pExtent->pszFullname);
2753#endif /* VBOX_WITH_VMDK_STRICT_SIZE_CHECK */
2754 if ( RT_SUCCESS(rc)
2755 && pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
2756 {
2757 /* The spec says that this must be a power of two and greater than 8,
2758 * but probably they meant not less than 8. */
2759 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
2760 || pExtent->cSectorsPerGrain < 8)
2761 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2762 N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
2763 else
2764 {
2765 /* This code requires that a grain table must hold a power of two multiple
2766 * of the number of entries per GT cache entry. */
2767 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
2768 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
2769 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2770 N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);
2771 else
2772 {
2773 rc = vmdkAllocStreamBuffers(pImage, pExtent);
2774 if (RT_SUCCESS(rc))
2775 {
2776 /* Prohibit any writes to this streamOptimized extent. */
2777 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2778 pExtent->uAppendPosition = 0;
2779
2780 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2781 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2782 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
2783 rc = vmdkReadGrainDirectory(pImage, pExtent);
2784 else
2785 {
2786 pExtent->uGrainSectorAbs = pExtent->cOverheadSectors;
2787 pExtent->cbGrainStreamRead = 0;
2788 }
2789 }
2790 }
2791 }
2792 }
2793
2794 if (RT_FAILURE(rc))
2795 vmdkFreeExtentData(pImage, pExtent, false);
2796
2797 return rc;
2798}
2799
2800/**
2801 * Internal: write/update the metadata for a sparse extent.
2802 */
2803static int vmdkWriteMetaSparseExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
2804 uint64_t uOffset, PVDIOCTX pIoCtx)
2805{
2806 SparseExtentHeader Header;
2807
2808 memset(&Header, '\0', sizeof(Header));
2809 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
2810 Header.version = RT_H2LE_U32(pExtent->uVersion);
2811 Header.flags = RT_H2LE_U32(RT_BIT(0));
2812 if (pExtent->pRGD)
2813 Header.flags |= RT_H2LE_U32(RT_BIT(1));
2814 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2815 Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
2816 Header.capacity = RT_H2LE_U64(pExtent->cSectors);
2817 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
2818 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
2819 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
2820 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
2821 if (pExtent->fFooter && uOffset == 0)
2822 {
2823 if (pExtent->pRGD)
2824 {
2825 Assert(pExtent->uSectorRGD);
2826 Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2827 Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2828 }
2829 else
2830 Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2831 }
2832 else
2833 {
2834 if (pExtent->pRGD)
2835 {
2836 Assert(pExtent->uSectorRGD);
2837 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
2838 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
2839 }
2840 else
2841 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
2842 }
2843 Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
2844 Header.uncleanShutdown = pExtent->fUncleanShutdown;
2845 Header.singleEndLineChar = '\n';
2846 Header.nonEndLineChar = ' ';
2847 Header.doubleEndLineChar1 = '\r';
2848 Header.doubleEndLineChar2 = '\n';
2849 Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
2850
2851 int rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
2852 uOffset, &Header, sizeof(Header),
2853 pIoCtx, NULL, NULL);
2854 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
2855 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
2856 return rc;
2857}
2858
2859/**
2860 * Internal: free the buffers used for streamOptimized images.
2861 */
2862static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent)
2863{
2864 if (pExtent->pvCompGrain)
2865 {
2866 RTMemFree(pExtent->pvCompGrain);
2867 pExtent->pvCompGrain = NULL;
2868 }
2869 if (pExtent->pvGrain)
2870 {
2871 RTMemFree(pExtent->pvGrain);
2872 pExtent->pvGrain = NULL;
2873 }
2874}
2875
2876/**
2877 * Internal: free the memory used by the extent data structure, optionally
2878 * deleting the referenced files.
2879 *
2880 * @returns VBox status code.
2881 * @param pImage Pointer to the image instance data.
2882 * @param pExtent The extent to free.
2883 * @param fDelete Flag whether to delete the backing storage.
2884 */
2885static int vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
2886 bool fDelete)
2887{
2888 int rc = VINF_SUCCESS;
2889
2890 vmdkFreeGrainDirectory(pExtent);
2891 if (pExtent->pDescData)
2892 {
2893 RTMemFree(pExtent->pDescData);
2894 pExtent->pDescData = NULL;
2895 }
2896 if (pExtent->pFile != NULL)
2897 {
2898 /* Do not delete raw extents, these have full and base names equal. */
2899 rc = vmdkFileClose(pImage, &pExtent->pFile,
2900 fDelete
2901 && pExtent->pszFullname
2902 && pExtent->pszBasename
2903 && strcmp(pExtent->pszFullname, pExtent->pszBasename));
2904 }
2905 if (pExtent->pszBasename)
2906 {
2907 RTMemTmpFree((void *)pExtent->pszBasename);
2908 pExtent->pszBasename = NULL;
2909 }
2910 if (pExtent->pszFullname)
2911 {
2912 RTStrFree((char *)(void *)pExtent->pszFullname);
2913 pExtent->pszFullname = NULL;
2914 }
2915 vmdkFreeStreamBuffers(pExtent);
2916
2917 return rc;
2918}
2919
2920/**
2921 * Internal: allocate grain table cache if necessary for this image.
2922 */
2923static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)
2924{
2925 PVMDKEXTENT pExtent;
2926
2927 /* Allocate grain table cache if any sparse extent is present. */
2928 for (unsigned i = 0; i < pImage->cExtents; i++)
2929 {
2930 pExtent = &pImage->pExtents[i];
2931 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
2932 {
2933 /* Allocate grain table cache. */
2934 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
2935 if (!pImage->pGTCache)
2936 return VERR_NO_MEMORY;
2937 for (unsigned j = 0; j < VMDK_GT_CACHE_SIZE; j++)
2938 {
2939 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[j];
2940 pGCE->uExtent = UINT32_MAX;
2941 }
2942 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
2943 break;
2944 }
2945 }
2946
2947 return VINF_SUCCESS;
2948}
2949
2950/**
2951 * Internal: allocate the given number of extents.
2952 */
2953static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
2954{
2955 int rc = VINF_SUCCESS;
2956 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
2957 if (pExtents)
2958 {
2959 for (unsigned i = 0; i < cExtents; i++)
2960 {
2961 pExtents[i].pFile = NULL;
2962 pExtents[i].pszBasename = NULL;
2963 pExtents[i].pszFullname = NULL;
2964 pExtents[i].pGD = NULL;
2965 pExtents[i].pRGD = NULL;
2966 pExtents[i].pDescData = NULL;
2967 pExtents[i].uVersion = 1;
2968 pExtents[i].uCompression = VMDK_COMPRESSION_NONE;
2969 pExtents[i].uExtent = i;
2970 pExtents[i].pImage = pImage;
2971 }
2972 pImage->pExtents = pExtents;
2973 pImage->cExtents = cExtents;
2974 }
2975 else
2976 rc = VERR_NO_MEMORY;
2977
2978 return rc;
2979}
2980
2981/**
2982 * Reads and processes the descriptor embedded in sparse images.
2983 *
2984 * @returns VBox status code.
2985 * @param pImage VMDK image instance.
2986 * @param pFile The sparse file handle.
2987 */
2988static int vmdkDescriptorReadSparse(PVMDKIMAGE pImage, PVMDKFILE pFile)
2989{
2990 /* It's a hosted single-extent image. */
2991 int rc = vmdkCreateExtents(pImage, 1);
2992 if (RT_SUCCESS(rc))
2993 {
2994 /* The opened file is passed to the extent. No separate descriptor
2995 * file, so no need to keep anything open for the image. */
2996 PVMDKEXTENT pExtent = &pImage->pExtents[0];
2997 pExtent->pFile = pFile;
2998 pImage->pFile = NULL;
2999 pExtent->pszFullname = RTPathAbsDup(pImage->pszFilename);
3000 if (RT_LIKELY(pExtent->pszFullname))
3001 {
3002 /* As we're dealing with a monolithic image here, there must
3003 * be a descriptor embedded in the image file. */
3004 rc = vmdkReadBinaryMetaExtent(pImage, pExtent, true /* fMagicAlreadyRead */);
3005 if ( RT_SUCCESS(rc)
3006 && pExtent->uDescriptorSector
3007 && pExtent->cDescriptorSectors)
3008 {
3009 /* HACK: extend the descriptor if it is unusually small and it fits in
3010 * the unused space after the image header. Allows opening VMDK files
3011 * with extremely small descriptor in read/write mode.
3012 *
3013 * The previous version introduced a possible regression for VMDK stream
3014 * optimized images from VMware which tend to have only a single sector sized
3015 * descriptor. Increasing the descriptor size resulted in adding the various uuid
3016 * entries required to make it work with VBox but for stream optimized images
3017 * the updated binary header wasn't written to the disk creating a mismatch
3018 * between advertised and real descriptor size.
3019 *
3020 * The descriptor size will be increased even if opened readonly now if there
3021 * enough room but the new value will not be written back to the image.
3022 */
3023 if ( pExtent->cDescriptorSectors < 3
3024 && (int64_t)pExtent->uSectorGD - pExtent->uDescriptorSector >= 4
3025 && (!pExtent->uSectorRGD || (int64_t)pExtent->uSectorRGD - pExtent->uDescriptorSector >= 4))
3026 {
3027 uint64_t cDescriptorSectorsOld = pExtent->cDescriptorSectors;
3028
3029 pExtent->cDescriptorSectors = 4;
3030 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3031 {
3032 /*
3033 * Update the on disk number now to make sure we don't introduce inconsistencies
3034 * in case of stream optimized images from VMware where the descriptor is just
3035 * one sector big (the binary header is not written to disk for complete
3036 * stream optimized images in vmdkFlushImage()).
3037 */
3038 uint64_t u64DescSizeNew = RT_H2LE_U64(pExtent->cDescriptorSectors);
3039 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pFile->pStorage, RT_OFFSETOF(SparseExtentHeader, descriptorSize),
3040 &u64DescSizeNew, sizeof(u64DescSizeNew));
3041 if (RT_FAILURE(rc))
3042 {
3043 LogFlowFunc(("Increasing the descriptor size failed with %Rrc\n", rc));
3044 /* Restore the old size and carry on. */
3045 pExtent->cDescriptorSectors = cDescriptorSectorsOld;
3046 }
3047 }
3048 }
3049 /* Read the descriptor from the extent. */
3050 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3051 if (RT_LIKELY(pExtent->pDescData))
3052 {
3053 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
3054 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
3055 pExtent->pDescData,
3056 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3057 if (RT_SUCCESS(rc))
3058 {
3059 rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
3060 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3061 if ( RT_SUCCESS(rc)
3062 && ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3063 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)))
3064 {
3065 rc = vmdkReadMetaExtent(pImage, pExtent);
3066 if (RT_SUCCESS(rc))
3067 {
3068 /* Mark the extent as unclean if opened in read-write mode. */
3069 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3070 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
3071 {
3072 pExtent->fUncleanShutdown = true;
3073 pExtent->fMetaDirty = true;
3074 }
3075 }
3076 }
3077 else if (RT_SUCCESS(rc))
3078 rc = VERR_NOT_SUPPORTED;
3079 }
3080 else
3081 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname);
3082 }
3083 else
3084 rc = VERR_NO_MEMORY;
3085 }
3086 else if (RT_SUCCESS(rc))
3087 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in '%s'"), pImage->pszFilename);
3088 }
3089 else
3090 rc = VERR_NO_MEMORY;
3091 }
3092
3093 return rc;
3094}
3095
3096/**
3097 * Reads the descriptor from a pure text file.
3098 *
3099 * @returns VBox status code.
3100 * @param pImage VMDK image instance.
3101 * @param pFile The descriptor file handle.
3102 */
3103static int vmdkDescriptorReadAscii(PVMDKIMAGE pImage, PVMDKFILE pFile)
3104{
3105 /* Allocate at least 10K, and make sure that there is 5K free space
3106 * in case new entries need to be added to the descriptor. Never
3107 * allocate more than 128K, because that's no valid descriptor file
3108 * and will result in the correct "truncated read" error handling. */
3109 uint64_t cbFileSize;
3110 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pFile->pStorage, &cbFileSize);
3111 if ( RT_SUCCESS(rc)
3112 && cbFileSize >= 50)
3113 {
3114 uint64_t cbSize = cbFileSize;
3115 if (cbSize % VMDK_SECTOR2BYTE(10))
3116 cbSize += VMDK_SECTOR2BYTE(20) - cbSize % VMDK_SECTOR2BYTE(10);
3117 else
3118 cbSize += VMDK_SECTOR2BYTE(10);
3119 cbSize = RT_MIN(cbSize, _128K);
3120 pImage->cbDescAlloc = RT_MAX(VMDK_SECTOR2BYTE(20), cbSize);
3121 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
3122 if (RT_LIKELY(pImage->pDescData))
3123 {
3124 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, 0, pImage->pDescData,
3125 RT_MIN(pImage->cbDescAlloc, cbFileSize));
3126 if (RT_SUCCESS(rc))
3127 {
3128#if 0 /** @todo Revisit */
3129 cbRead += sizeof(u32Magic);
3130 if (cbRead == pImage->cbDescAlloc)
3131 {
3132 /* Likely the read is truncated. Better fail a bit too early
3133 * (normally the descriptor is much smaller than our buffer). */
3134 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename);
3135 goto out;
3136 }
3137#endif
3138 rc = vmdkParseDescriptor(pImage, pImage->pDescData,
3139 pImage->cbDescAlloc);
3140 if (RT_SUCCESS(rc))
3141 {
3142 for (unsigned i = 0; i < pImage->cExtents && RT_SUCCESS(rc); i++)
3143 {
3144 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3145 if (pExtent->pszBasename)
3146 {
3147 /* Hack to figure out whether the specified name in the
3148 * extent descriptor is absolute. Doesn't always work, but
3149 * should be good enough for now. */
3150 char *pszFullname;
3151 /** @todo implement proper path absolute check. */
3152 if (pExtent->pszBasename[0] == RTPATH_SLASH)
3153 {
3154 pszFullname = RTStrDup(pExtent->pszBasename);
3155 if (!pszFullname)
3156 {
3157 rc = VERR_NO_MEMORY;
3158 break;
3159 }
3160 }
3161 else
3162 {
3163 char *pszDirname = RTStrDup(pImage->pszFilename);
3164 if (!pszDirname)
3165 {
3166 rc = VERR_NO_MEMORY;
3167 break;
3168 }
3169 RTPathStripFilename(pszDirname);
3170 pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
3171 RTStrFree(pszDirname);
3172 if (!pszFullname)
3173 {
3174 rc = VERR_NO_STR_MEMORY;
3175 break;
3176 }
3177 }
3178 pExtent->pszFullname = pszFullname;
3179 }
3180 else
3181 pExtent->pszFullname = NULL;
3182
3183 unsigned uOpenFlags = pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0);
3184 switch (pExtent->enmType)
3185 {
3186 case VMDKETYPE_HOSTED_SPARSE:
3187 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3188 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3189 if (RT_FAILURE(rc))
3190 {
3191 /* Do NOT signal an appropriate error here, as the VD
3192 * layer has the choice of retrying the open if it
3193 * failed. */
3194 break;
3195 }
3196 rc = vmdkReadBinaryMetaExtent(pImage, pExtent,
3197 false /* fMagicAlreadyRead */);
3198 if (RT_FAILURE(rc))
3199 break;
3200 rc = vmdkReadMetaExtent(pImage, pExtent);
3201 if (RT_FAILURE(rc))
3202 break;
3203
3204 /* Mark extent as unclean if opened in read-write mode. */
3205 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3206 {
3207 pExtent->fUncleanShutdown = true;
3208 pExtent->fMetaDirty = true;
3209 }
3210 break;
3211 case VMDKETYPE_VMFS:
3212 case VMDKETYPE_FLAT:
3213 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3214 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3215 if (RT_FAILURE(rc))
3216 {
3217 /* Do NOT signal an appropriate error here, as the VD
3218 * layer has the choice of retrying the open if it
3219 * failed. */
3220 break;
3221 }
3222 break;
3223 case VMDKETYPE_ZERO:
3224 /* Nothing to do. */
3225 break;
3226 default:
3227 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
3228 }
3229 }
3230 }
3231 }
3232 else
3233 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename);
3234 }
3235 else
3236 rc = VERR_NO_MEMORY;
3237 }
3238 else if (RT_SUCCESS(rc))
3239 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor in '%s' is too short"), pImage->pszFilename);
3240
3241 return rc;
3242}
3243
3244/**
3245 * Read and process the descriptor based on the image type.
3246 *
3247 * @returns VBox status code.
3248 * @param pImage VMDK image instance.
3249 * @param pFile VMDK file handle.
3250 */
3251static int vmdkDescriptorRead(PVMDKIMAGE pImage, PVMDKFILE pFile)
3252{
3253 uint32_t u32Magic;
3254
3255 /* Read magic (if present). */
3256 int rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, 0,
3257 &u32Magic, sizeof(u32Magic));
3258 if (RT_SUCCESS(rc))
3259 {
3260 /* Handle the file according to its magic number. */
3261 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
3262 rc = vmdkDescriptorReadSparse(pImage, pFile);
3263 else
3264 rc = vmdkDescriptorReadAscii(pImage, pFile);
3265 }
3266 else
3267 {
3268 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename);
3269 rc = VERR_VD_VMDK_INVALID_HEADER;
3270 }
3271
3272 return rc;
3273}
3274
3275/**
3276 * Internal: Open an image, constructing all necessary data structures.
3277 */
3278static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
3279{
3280 pImage->uOpenFlags = uOpenFlags;
3281 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
3282 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
3283 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
3284
3285 /*
3286 * Open the image.
3287 * We don't have to check for asynchronous access because
3288 * we only support raw access and the opened file is a description
3289 * file were no data is stored.
3290 */
3291 PVMDKFILE pFile;
3292 int rc = vmdkFileOpen(pImage, &pFile, pImage->pszFilename,
3293 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3294 if (RT_SUCCESS(rc))
3295 {
3296 pImage->pFile = pFile;
3297
3298 rc = vmdkDescriptorRead(pImage, pFile);
3299 if (RT_SUCCESS(rc))
3300 {
3301 /* Determine PCHS geometry if not set. */
3302 if (pImage->PCHSGeometry.cCylinders == 0)
3303 {
3304 uint64_t cCylinders = VMDK_BYTE2SECTOR(pImage->cbSize)
3305 / pImage->PCHSGeometry.cHeads
3306 / pImage->PCHSGeometry.cSectors;
3307 pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383);
3308 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3309 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
3310 {
3311 rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry);
3312 AssertRC(rc);
3313 }
3314 }
3315
3316 /* Update the image metadata now in case has changed. */
3317 rc = vmdkFlushImage(pImage, NULL);
3318 if (RT_SUCCESS(rc))
3319 {
3320 /* Figure out a few per-image constants from the extents. */
3321 pImage->cbSize = 0;
3322 for (unsigned i = 0; i < pImage->cExtents; i++)
3323 {
3324 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3325 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
3326 {
3327 /* Here used to be a check whether the nominal size of an extent
3328 * is a multiple of the grain size. The spec says that this is
3329 * always the case, but unfortunately some files out there in the
3330 * wild violate the spec (e.g. ReactOS 0.3.1). */
3331 }
3332 else if ( pExtent->enmType == VMDKETYPE_FLAT
3333 || pExtent->enmType == VMDKETYPE_ZERO)
3334 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
3335
3336 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
3337 }
3338
3339 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3340 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3341 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
3342 rc = vmdkAllocateGrainTableCache(pImage);
3343 }
3344 }
3345 }
3346 /* else: Do NOT signal an appropriate error here, as the VD layer has the
3347 * choice of retrying the open if it failed. */
3348
3349 if (RT_SUCCESS(rc))
3350 {
3351 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
3352 pImage->RegionList.fFlags = 0;
3353 pImage->RegionList.cRegions = 1;
3354
3355 pRegion->offRegion = 0; /* Disk start. */
3356 pRegion->cbBlock = 512;
3357 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
3358 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
3359 pRegion->cbData = 512;
3360 pRegion->cbMetadata = 0;
3361 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
3362 }
3363 else
3364 vmdkFreeImage(pImage, false);
3365 return rc;
3366}
3367
3368/**
3369 * Internal: create VMDK images for raw disk/partition access.
3370 */
3371static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVDISKRAW pRaw,
3372 uint64_t cbSize)
3373{
3374 int rc = VINF_SUCCESS;
3375 PVMDKEXTENT pExtent;
3376
3377 if (pRaw->uFlags & VDISKRAW_DISK)
3378 {
3379 /* Full raw disk access. This requires setting up a descriptor
3380 * file and open the (flat) raw disk. */
3381 rc = vmdkCreateExtents(pImage, 1);
3382 if (RT_FAILURE(rc))
3383 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3384 pExtent = &pImage->pExtents[0];
3385 /* Create raw disk descriptor file. */
3386 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
3387 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3388 true /* fCreate */));
3389 if (RT_FAILURE(rc))
3390 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
3391
3392 /* Set up basename for extent description. Cannot use StrDup. */
3393 size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
3394 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
3395 if (!pszBasename)
3396 return VERR_NO_MEMORY;
3397 memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
3398 pExtent->pszBasename = pszBasename;
3399 /* For raw disks the full name is identical to the base name. */
3400 pExtent->pszFullname = RTStrDup(pszBasename);
3401 if (!pExtent->pszFullname)
3402 return VERR_NO_MEMORY;
3403 pExtent->enmType = VMDKETYPE_FLAT;
3404 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
3405 pExtent->uSectorOffset = 0;
3406 pExtent->enmAccess = (pRaw->uFlags & VDISKRAW_READONLY) ? VMDKACCESS_READONLY : VMDKACCESS_READWRITE;
3407 pExtent->fMetaDirty = false;
3408
3409 /* Open flat image, the raw disk. */
3410 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3411 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
3412 false /* fCreate */));
3413 if (RT_FAILURE(rc))
3414 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
3415 }
3416 else
3417 {
3418 /* Raw partition access. This requires setting up a descriptor
3419 * file, write the partition information to a flat extent and
3420 * open all the (flat) raw disk partitions. */
3421
3422 /* First pass over the partition data areas to determine how many
3423 * extents we need. One data area can require up to 2 extents, as
3424 * it might be necessary to skip over unpartitioned space. */
3425 unsigned cExtents = 0;
3426 uint64_t uStart = 0;
3427 for (unsigned i = 0; i < pRaw->cPartDescs; i++)
3428 {
3429 PVDISKRAWPARTDESC pPart = &pRaw->pPartDescs[i];
3430 if (uStart > pPart->uStart)
3431 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
3432 N_("VMDK: incorrect partition data area ordering set up by the caller in '%s'"), pImage->pszFilename);
3433
3434 if (uStart < pPart->uStart)
3435 cExtents++;
3436 uStart = pPart->uStart + pPart->cbData;
3437 cExtents++;
3438 }
3439 /* Another extent for filling up the rest of the image. */
3440 if (uStart != cbSize)
3441 cExtents++;
3442
3443 rc = vmdkCreateExtents(pImage, cExtents);
3444 if (RT_FAILURE(rc))
3445 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3446
3447 /* Create raw partition descriptor file. */
3448 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
3449 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3450 true /* fCreate */));
3451 if (RT_FAILURE(rc))
3452 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
3453
3454 /* Create base filename for the partition table extent. */
3455 /** @todo remove fixed buffer without creating memory leaks. */
3456 char pszPartition[1024];
3457 const char *pszBase = RTPathFilename(pImage->pszFilename);
3458 const char *pszSuff = RTPathSuffix(pszBase);
3459 if (pszSuff == NULL)
3460 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename);
3461 char *pszBaseBase = RTStrDup(pszBase);
3462 if (!pszBaseBase)
3463 return VERR_NO_MEMORY;
3464 RTPathStripSuffix(pszBaseBase);
3465 RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s",
3466 pszBaseBase, pszSuff);
3467 RTStrFree(pszBaseBase);
3468
3469 /* Second pass over the partitions, now define all extents. */
3470 uint64_t uPartOffset = 0;
3471 cExtents = 0;
3472 uStart = 0;
3473 for (unsigned i = 0; i < pRaw->cPartDescs; i++)
3474 {
3475 PVDISKRAWPARTDESC pPart = &pRaw->pPartDescs[i];
3476 pExtent = &pImage->pExtents[cExtents++];
3477
3478 if (uStart < pPart->uStart)
3479 {
3480 pExtent->pszBasename = NULL;
3481 pExtent->pszFullname = NULL;
3482 pExtent->enmType = VMDKETYPE_ZERO;
3483 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uStart - uStart);
3484 pExtent->uSectorOffset = 0;
3485 pExtent->enmAccess = VMDKACCESS_READWRITE;
3486 pExtent->fMetaDirty = false;
3487 /* go to next extent */
3488 pExtent = &pImage->pExtents[cExtents++];
3489 }
3490 uStart = pPart->uStart + pPart->cbData;
3491
3492 if (pPart->pvPartitionData)
3493 {
3494 /* Set up basename for extent description. Can't use StrDup. */
3495 size_t cbBasename = strlen(pszPartition) + 1;
3496 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
3497 if (!pszBasename)
3498 return VERR_NO_MEMORY;
3499 memcpy(pszBasename, pszPartition, cbBasename);
3500 pExtent->pszBasename = pszBasename;
3501
3502 /* Set up full name for partition extent. */
3503 char *pszDirname = RTStrDup(pImage->pszFilename);
3504 if (!pszDirname)
3505 return VERR_NO_STR_MEMORY;
3506 RTPathStripFilename(pszDirname);
3507 char *pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
3508 RTStrFree(pszDirname);
3509 if (!pszFullname)
3510 return VERR_NO_STR_MEMORY;
3511 pExtent->pszFullname = pszFullname;
3512 pExtent->enmType = VMDKETYPE_FLAT;
3513 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
3514 pExtent->uSectorOffset = uPartOffset;
3515 pExtent->enmAccess = VMDKACCESS_READWRITE;
3516 pExtent->fMetaDirty = false;
3517
3518 /* Create partition table flat image. */
3519 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3520 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
3521 true /* fCreate */));
3522 if (RT_FAILURE(rc))
3523 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
3524 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
3525 VMDK_SECTOR2BYTE(uPartOffset),
3526 pPart->pvPartitionData,
3527 pPart->cbData);
3528 if (RT_FAILURE(rc))
3529 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
3530 uPartOffset += VMDK_BYTE2SECTOR(pPart->cbData);
3531 }
3532 else
3533 {
3534 if (pPart->pszRawDevice)
3535 {
3536 /* Set up basename for extent descr. Can't use StrDup. */
3537 size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
3538 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
3539 if (!pszBasename)
3540 return VERR_NO_MEMORY;
3541 memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
3542 pExtent->pszBasename = pszBasename;
3543 /* For raw disks full name is identical to base name. */
3544 pExtent->pszFullname = RTStrDup(pszBasename);
3545 if (!pExtent->pszFullname)
3546 return VERR_NO_MEMORY;
3547 pExtent->enmType = VMDKETYPE_FLAT;
3548 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
3549 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uStartOffset);
3550 pExtent->enmAccess = (pPart->uFlags & VDISKRAW_READONLY) ? VMDKACCESS_READONLY : VMDKACCESS_READWRITE;
3551 pExtent->fMetaDirty = false;
3552
3553 /* Open flat image, the raw partition. */
3554 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3555 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
3556 false /* fCreate */));
3557 if (RT_FAILURE(rc))
3558 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
3559 }
3560 else
3561 {
3562 pExtent->pszBasename = NULL;
3563 pExtent->pszFullname = NULL;
3564 pExtent->enmType = VMDKETYPE_ZERO;
3565 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
3566 pExtent->uSectorOffset = 0;
3567 pExtent->enmAccess = VMDKACCESS_READWRITE;
3568 pExtent->fMetaDirty = false;
3569 }
3570 }
3571 }
3572 /* Another extent for filling up the rest of the image. */
3573 if (uStart != cbSize)
3574 {
3575 pExtent = &pImage->pExtents[cExtents++];
3576 pExtent->pszBasename = NULL;
3577 pExtent->pszFullname = NULL;
3578 pExtent->enmType = VMDKETYPE_ZERO;
3579 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
3580 pExtent->uSectorOffset = 0;
3581 pExtent->enmAccess = VMDKACCESS_READWRITE;
3582 pExtent->fMetaDirty = false;
3583 }
3584 }
3585
3586 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
3587 (pRaw->uFlags & VDISKRAW_DISK) ?
3588 "fullDevice" : "partitionedDevice");
3589 if (RT_FAILURE(rc))
3590 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
3591 return rc;
3592}
3593
3594/**
3595 * Internal: create a regular (i.e. file-backed) VMDK image.
3596 */
3597static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
3598 unsigned uImageFlags, PVDINTERFACEPROGRESS pIfProgress,
3599 unsigned uPercentStart, unsigned uPercentSpan)
3600{
3601 int rc = VINF_SUCCESS;
3602 unsigned cExtents = 1;
3603 uint64_t cbOffset = 0;
3604 uint64_t cbRemaining = cbSize;
3605
3606 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
3607 {
3608 cExtents = cbSize / VMDK_2G_SPLIT_SIZE;
3609 /* Do proper extent computation: need one smaller extent if the total
3610 * size isn't evenly divisible by the split size. */
3611 if (cbSize % VMDK_2G_SPLIT_SIZE)
3612 cExtents++;
3613 }
3614 rc = vmdkCreateExtents(pImage, cExtents);
3615 if (RT_FAILURE(rc))
3616 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3617
3618 /* Basename strings needed for constructing the extent names. */
3619 char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
3620 AssertPtr(pszBasenameSubstr);
3621 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
3622
3623 /* Create separate descriptor file if necessary. */
3624 if (cExtents != 1 || (uImageFlags & VD_IMAGE_FLAGS_FIXED))
3625 {
3626 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
3627 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3628 true /* fCreate */));
3629 if (RT_FAILURE(rc))
3630 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
3631 }
3632 else
3633 pImage->pFile = NULL;
3634
3635 /* Set up all extents. */
3636 for (unsigned i = 0; i < cExtents; i++)
3637 {
3638 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3639 uint64_t cbExtent = cbRemaining;
3640
3641 /* Set up fullname/basename for extent description. Cannot use StrDup
3642 * for basename, as it is not guaranteed that the memory can be freed
3643 * with RTMemTmpFree, which must be used as in other code paths
3644 * StrDup is not usable. */
3645 if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
3646 {
3647 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
3648 if (!pszBasename)
3649 return VERR_NO_MEMORY;
3650 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
3651 pExtent->pszBasename = pszBasename;
3652 }
3653 else
3654 {
3655 char *pszBasenameSuff = RTPathSuffix(pszBasenameSubstr);
3656 char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
3657 RTPathStripSuffix(pszBasenameBase);
3658 char *pszTmp;
3659 size_t cbTmp;
3660 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3661 {
3662 if (cExtents == 1)
3663 RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase,
3664 pszBasenameSuff);
3665 else
3666 RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
3667 i+1, pszBasenameSuff);
3668 }
3669 else
3670 RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1,
3671 pszBasenameSuff);
3672 RTStrFree(pszBasenameBase);
3673 if (!pszTmp)
3674 return VERR_NO_STR_MEMORY;
3675 cbTmp = strlen(pszTmp) + 1;
3676 char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
3677 if (!pszBasename)
3678 {
3679 RTStrFree(pszTmp);
3680 return VERR_NO_MEMORY;
3681 }
3682 memcpy(pszBasename, pszTmp, cbTmp);
3683 RTStrFree(pszTmp);
3684 pExtent->pszBasename = pszBasename;
3685 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
3686 cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE);
3687 }
3688 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
3689 if (!pszBasedirectory)
3690 return VERR_NO_STR_MEMORY;
3691 RTPathStripFilename(pszBasedirectory);
3692 char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
3693 RTStrFree(pszBasedirectory);
3694 if (!pszFullname)
3695 return VERR_NO_STR_MEMORY;
3696 pExtent->pszFullname = pszFullname;
3697
3698 /* Create file for extent. */
3699 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3700 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3701 true /* fCreate */));
3702 if (RT_FAILURE(rc))
3703 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
3704 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3705 {
3706 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbExtent,
3707 0 /* fFlags */, pIfProgress,
3708 uPercentStart + cbOffset * uPercentSpan / cbSize,
3709 cbExtent * uPercentSpan / cbSize);
3710 if (RT_FAILURE(rc))
3711 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
3712 }
3713
3714 /* Place descriptor file information (where integrated). */
3715 if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
3716 {
3717 pExtent->uDescriptorSector = 1;
3718 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
3719 /* The descriptor is part of the (only) extent. */
3720 pExtent->pDescData = pImage->pDescData;
3721 pImage->pDescData = NULL;
3722 }
3723
3724 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
3725 {
3726 uint64_t cSectorsPerGDE, cSectorsPerGD;
3727 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
3728 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, _64K));
3729 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
3730 pExtent->cGTEntries = 512;
3731 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
3732 pExtent->cSectorsPerGDE = cSectorsPerGDE;
3733 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
3734 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
3735 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3736 {
3737 /* The spec says version is 1 for all VMDKs, but the vast
3738 * majority of streamOptimized VMDKs actually contain
3739 * version 3 - so go with the majority. Both are accepted. */
3740 pExtent->uVersion = 3;
3741 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
3742 }
3743 }
3744 else
3745 {
3746 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
3747 pExtent->enmType = VMDKETYPE_VMFS;
3748 else
3749 pExtent->enmType = VMDKETYPE_FLAT;
3750 }
3751
3752 pExtent->enmAccess = VMDKACCESS_READWRITE;
3753 pExtent->fUncleanShutdown = true;
3754 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent);
3755 pExtent->uSectorOffset = 0;
3756 pExtent->fMetaDirty = true;
3757
3758 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
3759 {
3760 /* fPreAlloc should never be false because VMware can't use such images. */
3761 rc = vmdkCreateGrainDirectory(pImage, pExtent,
3762 RT_MAX( pExtent->uDescriptorSector
3763 + pExtent->cDescriptorSectors,
3764 1),
3765 true /* fPreAlloc */);
3766 if (RT_FAILURE(rc))
3767 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
3768 }
3769
3770 cbOffset += cbExtent;
3771
3772 if (RT_SUCCESS(rc))
3773 vdIfProgress(pIfProgress, uPercentStart + cbOffset * uPercentSpan / cbSize);
3774
3775 cbRemaining -= cbExtent;
3776 }
3777
3778 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
3779 {
3780 /* VirtualBox doesn't care, but VMWare ESX freaks out if the wrong
3781 * controller type is set in an image. */
3782 rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, "ddb.adapterType", "lsilogic");
3783 if (RT_FAILURE(rc))
3784 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set controller type to lsilogic in '%s'"), pImage->pszFilename);
3785 }
3786
3787 const char *pszDescType = NULL;
3788 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3789 {
3790 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
3791 pszDescType = "vmfs";
3792 else
3793 pszDescType = (cExtents == 1)
3794 ? "monolithicFlat" : "twoGbMaxExtentFlat";
3795 }
3796 else
3797 {
3798 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3799 pszDescType = "streamOptimized";
3800 else
3801 {
3802 pszDescType = (cExtents == 1)
3803 ? "monolithicSparse" : "twoGbMaxExtentSparse";
3804 }
3805 }
3806 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
3807 pszDescType);
3808 if (RT_FAILURE(rc))
3809 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
3810 return rc;
3811}
3812
3813/**
3814 * Internal: Create a real stream optimized VMDK using only linear writes.
3815 */
3816static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize)
3817{
3818 int rc = vmdkCreateExtents(pImage, 1);
3819 if (RT_FAILURE(rc))
3820 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3821
3822 /* Basename strings needed for constructing the extent names. */
3823 const char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
3824 AssertPtr(pszBasenameSubstr);
3825 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
3826
3827 /* No separate descriptor file. */
3828 pImage->pFile = NULL;
3829
3830 /* Set up all extents. */
3831 PVMDKEXTENT pExtent = &pImage->pExtents[0];
3832
3833 /* Set up fullname/basename for extent description. Cannot use StrDup
3834 * for basename, as it is not guaranteed that the memory can be freed
3835 * with RTMemTmpFree, which must be used as in other code paths
3836 * StrDup is not usable. */
3837 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
3838 if (!pszBasename)
3839 return VERR_NO_MEMORY;
3840 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
3841 pExtent->pszBasename = pszBasename;
3842
3843 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
3844 RTPathStripFilename(pszBasedirectory);
3845 char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
3846 RTStrFree(pszBasedirectory);
3847 if (!pszFullname)
3848 return VERR_NO_STR_MEMORY;
3849 pExtent->pszFullname = pszFullname;
3850
3851 /* Create file for extent. Make it write only, no reading allowed. */
3852 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3853 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3854 true /* fCreate */)
3855 & ~RTFILE_O_READ);
3856 if (RT_FAILURE(rc))
3857 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
3858
3859 /* Place descriptor file information. */
3860 pExtent->uDescriptorSector = 1;
3861 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
3862 /* The descriptor is part of the (only) extent. */
3863 pExtent->pDescData = pImage->pDescData;
3864 pImage->pDescData = NULL;
3865
3866 uint64_t cSectorsPerGDE, cSectorsPerGD;
3867 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
3868 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));
3869 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
3870 pExtent->cGTEntries = 512;
3871 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
3872 pExtent->cSectorsPerGDE = cSectorsPerGDE;
3873 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
3874 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
3875
3876 /* The spec says version is 1 for all VMDKs, but the vast
3877 * majority of streamOptimized VMDKs actually contain
3878 * version 3 - so go with the majority. Both are accepted. */
3879 pExtent->uVersion = 3;
3880 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
3881 pExtent->fFooter = true;
3882
3883 pExtent->enmAccess = VMDKACCESS_READONLY;
3884 pExtent->fUncleanShutdown = false;
3885 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
3886 pExtent->uSectorOffset = 0;
3887 pExtent->fMetaDirty = true;
3888
3889 /* Create grain directory, without preallocating it straight away. It will
3890 * be constructed on the fly when writing out the data and written when
3891 * closing the image. The end effect is that the full grain directory is
3892 * allocated, which is a requirement of the VMDK specs. */
3893 rc = vmdkCreateGrainDirectory(pImage, pExtent, VMDK_GD_AT_END,
3894 false /* fPreAlloc */);
3895 if (RT_FAILURE(rc))
3896 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
3897
3898 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
3899 "streamOptimized");
3900 if (RT_FAILURE(rc))
3901 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
3902
3903 return rc;
3904}
3905
3906/**
3907 * Initializes the UUID fields in the DDB.
3908 *
3909 * @returns VBox status code.
3910 * @param pImage The VMDK image instance.
3911 */
3912static int vmdkCreateImageDdbUuidsInit(PVMDKIMAGE pImage)
3913{
3914 int rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
3915 if (RT_SUCCESS(rc))
3916 {
3917 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
3918 if (RT_SUCCESS(rc))
3919 {
3920 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_MODIFICATION_UUID,
3921 &pImage->ModificationUuid);
3922 if (RT_SUCCESS(rc))
3923 {
3924 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_MODIFICATION_UUID,
3925 &pImage->ParentModificationUuid);
3926 if (RT_FAILURE(rc))
3927 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3928 N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);
3929 }
3930 else
3931 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3932 N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);
3933 }
3934 else
3935 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3936 N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);
3937 }
3938 else
3939 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3940 N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);
3941
3942 return rc;
3943}
3944
3945/**
3946 * Internal: The actual code for creating any VMDK variant currently in
3947 * existence on hosted environments.
3948 */
3949static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
3950 unsigned uImageFlags, const char *pszComment,
3951 PCVDGEOMETRY pPCHSGeometry,
3952 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
3953 PVDINTERFACEPROGRESS pIfProgress,
3954 unsigned uPercentStart, unsigned uPercentSpan)
3955{
3956 pImage->uImageFlags = uImageFlags;
3957
3958 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
3959 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
3960 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
3961
3962 int rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,
3963 &pImage->Descriptor);
3964 if (RT_SUCCESS(rc))
3965 {
3966 if ( (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3967 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
3968 {
3969 /* Raw disk image (includes raw partition). */
3970 const PVDISKRAW pRaw = (const PVDISKRAW)pszComment;
3971 /* As the comment is misused, zap it so that no garbage comment
3972 * is set below. */
3973 pszComment = NULL;
3974 rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
3975 }
3976 else if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3977 {
3978 /* Stream optimized sparse image (monolithic). */
3979 rc = vmdkCreateStreamImage(pImage, cbSize);
3980 }
3981 else
3982 {
3983 /* Regular fixed or sparse image (monolithic or split). */
3984 rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags,
3985 pIfProgress, uPercentStart,
3986 uPercentSpan * 95 / 100);
3987 }
3988
3989 if (RT_SUCCESS(rc))
3990 {
3991 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan * 98 / 100);
3992
3993 pImage->cbSize = cbSize;
3994
3995 for (unsigned i = 0; i < pImage->cExtents; i++)
3996 {
3997 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3998
3999 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
4000 pExtent->cNominalSectors, pExtent->enmType,
4001 pExtent->pszBasename, pExtent->uSectorOffset);
4002 if (RT_FAILURE(rc))
4003 {
4004 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
4005 break;
4006 }
4007 }
4008
4009 if (RT_SUCCESS(rc))
4010 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
4011
4012 if ( RT_SUCCESS(rc)
4013 && pPCHSGeometry->cCylinders != 0
4014 && pPCHSGeometry->cHeads != 0
4015 && pPCHSGeometry->cSectors != 0)
4016 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
4017
4018 if ( RT_SUCCESS(rc)
4019 && pLCHSGeometry->cCylinders != 0
4020 && pLCHSGeometry->cHeads != 0
4021 && pLCHSGeometry->cSectors != 0)
4022 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
4023
4024 pImage->LCHSGeometry = *pLCHSGeometry;
4025 pImage->PCHSGeometry = *pPCHSGeometry;
4026
4027 pImage->ImageUuid = *pUuid;
4028 RTUuidClear(&pImage->ParentUuid);
4029 RTUuidClear(&pImage->ModificationUuid);
4030 RTUuidClear(&pImage->ParentModificationUuid);
4031
4032 if (RT_SUCCESS(rc))
4033 rc = vmdkCreateImageDdbUuidsInit(pImage);
4034
4035 if (RT_SUCCESS(rc))
4036 rc = vmdkAllocateGrainTableCache(pImage);
4037
4038 if (RT_SUCCESS(rc))
4039 {
4040 rc = vmdkSetImageComment(pImage, pszComment);
4041 if (RT_FAILURE(rc))
4042 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);
4043 }
4044
4045 if (RT_SUCCESS(rc))
4046 {
4047 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan * 99 / 100);
4048
4049 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
4050 {
4051 /* streamOptimized is a bit special, we cannot trigger the flush
4052 * until all data has been written. So we write the necessary
4053 * information explicitly. */
4054 pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64( pImage->Descriptor.aLines[pImage->Descriptor.cLines]
4055 - pImage->Descriptor.aLines[0], 512));
4056 rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0, NULL);
4057 if (RT_SUCCESS(rc))
4058 {
4059 rc = vmdkWriteDescriptor(pImage, NULL);
4060 if (RT_FAILURE(rc))
4061 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);
4062 }
4063 else
4064 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);
4065 }
4066 else
4067 rc = vmdkFlushImage(pImage, NULL);
4068 }
4069 }
4070 }
4071 else
4072 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);
4073
4074
4075 if (RT_SUCCESS(rc))
4076 {
4077 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
4078 pImage->RegionList.fFlags = 0;
4079 pImage->RegionList.cRegions = 1;
4080
4081 pRegion->offRegion = 0; /* Disk start. */
4082 pRegion->cbBlock = 512;
4083 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
4084 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
4085 pRegion->cbData = 512;
4086 pRegion->cbMetadata = 0;
4087 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
4088
4089 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
4090 }
4091 else
4092 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
4093 return rc;
4094}
4095
4096/**
4097 * Internal: Update image comment.
4098 */
4099static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
4100{
4101 char *pszCommentEncoded = NULL;
4102 if (pszComment)
4103 {
4104 pszCommentEncoded = vmdkEncodeString(pszComment);
4105 if (!pszCommentEncoded)
4106 return VERR_NO_MEMORY;
4107 }
4108
4109 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
4110 "ddb.comment", pszCommentEncoded);
4111 if (pszCommentEncoded)
4112 RTStrFree(pszCommentEncoded);
4113 if (RT_FAILURE(rc))
4114 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
4115 return VINF_SUCCESS;
4116}
4117
4118/**
4119 * Internal. Clear the grain table buffer for real stream optimized writing.
4120 */
4121static void vmdkStreamClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
4122{
4123 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
4124 for (uint32_t i = 0; i < cCacheLines; i++)
4125 memset(&pImage->pGTCache->aGTCache[i].aGTData[0], '\0',
4126 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
4127}
4128
4129/**
4130 * Internal. Flush the grain table buffer for real stream optimized writing.
4131 */
4132static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
4133 uint32_t uGDEntry)
4134{
4135 int rc = VINF_SUCCESS;
4136 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
4137
4138 /* VMware does not write out completely empty grain tables in the case
4139 * of streamOptimized images, which according to my interpretation of
4140 * the VMDK 1.1 spec is bending the rules. Since they do it and we can
4141 * handle it without problems do it the same way and save some bytes. */
4142 bool fAllZero = true;
4143 for (uint32_t i = 0; i < cCacheLines; i++)
4144 {
4145 /* Convert the grain table to little endian in place, as it will not
4146 * be used at all after this function has been called. */
4147 uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
4148 for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
4149 if (*pGTTmp)
4150 {
4151 fAllZero = false;
4152 break;
4153 }
4154 if (!fAllZero)
4155 break;
4156 }
4157 if (fAllZero)
4158 return VINF_SUCCESS;
4159
4160 uint64_t uFileOffset = pExtent->uAppendPosition;
4161 if (!uFileOffset)
4162 return VERR_INTERNAL_ERROR;
4163 /* Align to sector, as the previous write could have been any size. */
4164 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
4165
4166 /* Grain table marker. */
4167 uint8_t aMarker[512];
4168 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
4169 memset(pMarker, '\0', sizeof(aMarker));
4170 pMarker->uSector = RT_H2LE_U64(VMDK_BYTE2SECTOR((uint64_t)pExtent->cGTEntries * sizeof(uint32_t)));
4171 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT);
4172 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
4173 aMarker, sizeof(aMarker));
4174 AssertRC(rc);
4175 uFileOffset += 512;
4176
4177 if (!pExtent->pGD || pExtent->pGD[uGDEntry])
4178 return VERR_INTERNAL_ERROR;
4179
4180 pExtent->pGD[uGDEntry] = VMDK_BYTE2SECTOR(uFileOffset);
4181
4182 for (uint32_t i = 0; i < cCacheLines; i++)
4183 {
4184 /* Convert the grain table to little endian in place, as it will not
4185 * be used at all after this function has been called. */
4186 uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
4187 for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
4188 *pGTTmp = RT_H2LE_U32(*pGTTmp);
4189
4190 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
4191 &pImage->pGTCache->aGTCache[i].aGTData[0],
4192 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
4193 uFileOffset += VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t);
4194 if (RT_FAILURE(rc))
4195 break;
4196 }
4197 Assert(!(uFileOffset % 512));
4198 pExtent->uAppendPosition = RT_ALIGN_64(uFileOffset, 512);
4199 return rc;
4200}
4201
4202/**
4203 * Internal. Free all allocated space for representing an image, and optionally
4204 * delete the image from disk.
4205 */
4206static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
4207{
4208 int rc = VINF_SUCCESS;
4209
4210 /* Freeing a never allocated image (e.g. because the open failed) is
4211 * not signalled as an error. After all nothing bad happens. */
4212 if (pImage)
4213 {
4214 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4215 {
4216 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
4217 {
4218 /* Check if all extents are clean. */
4219 for (unsigned i = 0; i < pImage->cExtents; i++)
4220 {
4221 Assert(!pImage->pExtents[i].fUncleanShutdown);
4222 }
4223 }
4224 else
4225 {
4226 /* Mark all extents as clean. */
4227 for (unsigned i = 0; i < pImage->cExtents; i++)
4228 {
4229 if ( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
4230 && pImage->pExtents[i].fUncleanShutdown)
4231 {
4232 pImage->pExtents[i].fUncleanShutdown = false;
4233 pImage->pExtents[i].fMetaDirty = true;
4234 }
4235
4236 /* From now on it's not safe to append any more data. */
4237 pImage->pExtents[i].uAppendPosition = 0;
4238 }
4239 }
4240 }
4241
4242 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
4243 {
4244 /* No need to write any pending data if the file will be deleted
4245 * or if the new file wasn't successfully created. */
4246 if ( !fDelete && pImage->pExtents
4247 && pImage->pExtents[0].cGTEntries
4248 && pImage->pExtents[0].uAppendPosition)
4249 {
4250 PVMDKEXTENT pExtent = &pImage->pExtents[0];
4251 uint32_t uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
4252 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
4253 AssertRC(rc);
4254 vmdkStreamClearGT(pImage, pExtent);
4255 for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++)
4256 {
4257 rc = vmdkStreamFlushGT(pImage, pExtent, i);
4258 AssertRC(rc);
4259 }
4260
4261 uint64_t uFileOffset = pExtent->uAppendPosition;
4262 if (!uFileOffset)
4263 return VERR_INTERNAL_ERROR;
4264 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
4265
4266 /* From now on it's not safe to append any more data. */
4267 pExtent->uAppendPosition = 0;
4268
4269 /* Grain directory marker. */
4270 uint8_t aMarker[512];
4271 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
4272 memset(pMarker, '\0', sizeof(aMarker));
4273 pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64((uint64_t)pExtent->cGDEntries * sizeof(uint32_t)), 512));
4274 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);
4275 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
4276 aMarker, sizeof(aMarker));
4277 AssertRC(rc);
4278 uFileOffset += 512;
4279
4280 /* Write grain directory in little endian style. The array will
4281 * not be used after this, so convert in place. */
4282 uint32_t *pGDTmp = pExtent->pGD;
4283 for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++)
4284 *pGDTmp = RT_H2LE_U32(*pGDTmp);
4285 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
4286 uFileOffset, pExtent->pGD,
4287 pExtent->cGDEntries * sizeof(uint32_t));
4288 AssertRC(rc);
4289
4290 pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);
4291 pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset);
4292 uFileOffset = RT_ALIGN_64( uFileOffset
4293 + pExtent->cGDEntries * sizeof(uint32_t),
4294 512);
4295
4296 /* Footer marker. */
4297 memset(pMarker, '\0', sizeof(aMarker));
4298 pMarker->uSector = VMDK_BYTE2SECTOR(512);
4299 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);
4300 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
4301 uFileOffset, aMarker, sizeof(aMarker));
4302 AssertRC(rc);
4303
4304 uFileOffset += 512;
4305 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset, NULL);
4306 AssertRC(rc);
4307
4308 uFileOffset += 512;
4309 /* End-of-stream marker. */
4310 memset(pMarker, '\0', sizeof(aMarker));
4311 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
4312 uFileOffset, aMarker, sizeof(aMarker));
4313 AssertRC(rc);
4314 }
4315 }
4316 else if (!fDelete)
4317 vmdkFlushImage(pImage, NULL);
4318
4319 if (pImage->pExtents != NULL)
4320 {
4321 for (unsigned i = 0 ; i < pImage->cExtents; i++)
4322 {
4323 int rc2 = vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
4324 if (RT_SUCCESS(rc))
4325 rc = rc2; /* Propogate any error when closing the file. */
4326 }
4327 RTMemFree(pImage->pExtents);
4328 pImage->pExtents = NULL;
4329 }
4330 pImage->cExtents = 0;
4331 if (pImage->pFile != NULL)
4332 {
4333 int rc2 = vmdkFileClose(pImage, &pImage->pFile, fDelete);
4334 if (RT_SUCCESS(rc))
4335 rc = rc2; /* Propogate any error when closing the file. */
4336 }
4337 int rc2 = vmdkFileCheckAllClose(pImage);
4338 if (RT_SUCCESS(rc))
4339 rc = rc2; /* Propogate any error when closing the file. */
4340
4341 if (pImage->pGTCache)
4342 {
4343 RTMemFree(pImage->pGTCache);
4344 pImage->pGTCache = NULL;
4345 }
4346 if (pImage->pDescData)
4347 {
4348 RTMemFree(pImage->pDescData);
4349 pImage->pDescData = NULL;
4350 }
4351 }
4352
4353 LogFlowFunc(("returns %Rrc\n", rc));
4354 return rc;
4355}
4356
4357/**
4358 * Internal. Flush image data (and metadata) to disk.
4359 */
4360static int vmdkFlushImage(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
4361{
4362 PVMDKEXTENT pExtent;
4363 int rc = VINF_SUCCESS;
4364
4365 /* Update descriptor if changed. */
4366 if (pImage->Descriptor.fDirty)
4367 rc = vmdkWriteDescriptor(pImage, pIoCtx);
4368
4369 if (RT_SUCCESS(rc))
4370 {
4371 for (unsigned i = 0; i < pImage->cExtents; i++)
4372 {
4373 pExtent = &pImage->pExtents[i];
4374 if (pExtent->pFile != NULL && pExtent->fMetaDirty)
4375 {
4376 switch (pExtent->enmType)
4377 {
4378 case VMDKETYPE_HOSTED_SPARSE:
4379 if (!pExtent->fFooter)
4380 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0, pIoCtx);
4381 else
4382 {
4383 uint64_t uFileOffset = pExtent->uAppendPosition;
4384 /* Simply skip writing anything if the streamOptimized
4385 * image hasn't been just created. */
4386 if (!uFileOffset)
4387 break;
4388 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
4389 rc = vmdkWriteMetaSparseExtent(pImage, pExtent,
4390 uFileOffset, pIoCtx);
4391 }
4392 break;
4393 case VMDKETYPE_VMFS:
4394 case VMDKETYPE_FLAT:
4395 /* Nothing to do. */
4396 break;
4397 case VMDKETYPE_ZERO:
4398 default:
4399 AssertMsgFailed(("extent with type %d marked as dirty\n",
4400 pExtent->enmType));
4401 break;
4402 }
4403 }
4404
4405 if (RT_FAILURE(rc))
4406 break;
4407
4408 switch (pExtent->enmType)
4409 {
4410 case VMDKETYPE_HOSTED_SPARSE:
4411 case VMDKETYPE_VMFS:
4412 case VMDKETYPE_FLAT:
4413 /** @todo implement proper path absolute check. */
4414 if ( pExtent->pFile != NULL
4415 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4416 && !(pExtent->pszBasename[0] == RTPATH_SLASH))
4417 rc = vdIfIoIntFileFlush(pImage->pIfIo, pExtent->pFile->pStorage, pIoCtx,
4418 NULL, NULL);
4419 break;
4420 case VMDKETYPE_ZERO:
4421 /* No need to do anything for this extent. */
4422 break;
4423 default:
4424 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
4425 break;
4426 }
4427 }
4428 }
4429
4430 return rc;
4431}
4432
4433/**
4434 * Internal. Find extent corresponding to the sector number in the disk.
4435 */
4436static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector,
4437 PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
4438{
4439 PVMDKEXTENT pExtent = NULL;
4440 int rc = VINF_SUCCESS;
4441
4442 for (unsigned i = 0; i < pImage->cExtents; i++)
4443 {
4444 if (offSector < pImage->pExtents[i].cNominalSectors)
4445 {
4446 pExtent = &pImage->pExtents[i];
4447 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
4448 break;
4449 }
4450 offSector -= pImage->pExtents[i].cNominalSectors;
4451 }
4452
4453 if (pExtent)
4454 *ppExtent = pExtent;
4455 else
4456 rc = VERR_IO_SECTOR_NOT_FOUND;
4457
4458 return rc;
4459}
4460
4461/**
4462 * Internal. Hash function for placing the grain table hash entries.
4463 */
4464static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
4465 unsigned uExtent)
4466{
4467 /** @todo this hash function is quite simple, maybe use a better one which
4468 * scrambles the bits better. */
4469 return (uSector + uExtent) % pCache->cEntries;
4470}
4471
4472/**
4473 * Internal. Get sector number in the extent file from the relative sector
4474 * number in the extent.
4475 */
4476static int vmdkGetSector(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
4477 PVMDKEXTENT pExtent, uint64_t uSector,
4478 uint64_t *puExtentSector)
4479{
4480 PVMDKGTCACHE pCache = pImage->pGTCache;
4481 uint64_t uGDIndex, uGTSector, uGTBlock;
4482 uint32_t uGTHash, uGTBlockIndex;
4483 PVMDKGTCACHEENTRY pGTCacheEntry;
4484 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
4485 int rc;
4486
4487 /* For newly created and readonly/sequentially opened streamOptimized
4488 * images this must be a no-op, as the grain directory is not there. */
4489 if ( ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
4490 && pExtent->uAppendPosition)
4491 || ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
4492 && pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY
4493 && pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
4494 {
4495 *puExtentSector = 0;
4496 return VINF_SUCCESS;
4497 }
4498
4499 uGDIndex = uSector / pExtent->cSectorsPerGDE;
4500 if (uGDIndex >= pExtent->cGDEntries)
4501 return VERR_OUT_OF_RANGE;
4502 uGTSector = pExtent->pGD[uGDIndex];
4503 if (!uGTSector)
4504 {
4505 /* There is no grain table referenced by this grain directory
4506 * entry. So there is absolutely no data in this area. */
4507 *puExtentSector = 0;
4508 return VINF_SUCCESS;
4509 }
4510
4511 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
4512 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
4513 pGTCacheEntry = &pCache->aGTCache[uGTHash];
4514 if ( pGTCacheEntry->uExtent != pExtent->uExtent
4515 || pGTCacheEntry->uGTBlock != uGTBlock)
4516 {
4517 /* Cache miss, fetch data from disk. */
4518 PVDMETAXFER pMetaXfer;
4519 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4520 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
4521 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
4522 if (RT_FAILURE(rc))
4523 return rc;
4524 /* We can release the metadata transfer immediately. */
4525 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
4526 pGTCacheEntry->uExtent = pExtent->uExtent;
4527 pGTCacheEntry->uGTBlock = uGTBlock;
4528 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
4529 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
4530 }
4531 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
4532 uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
4533 if (uGrainSector)
4534 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
4535 else
4536 *puExtentSector = 0;
4537 return VINF_SUCCESS;
4538}
4539
4540/**
4541 * Internal. Writes the grain and also if necessary the grain tables.
4542 * Uses the grain table cache as a true grain table.
4543 */
4544static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
4545 uint64_t uSector, PVDIOCTX pIoCtx,
4546 uint64_t cbWrite)
4547{
4548 uint32_t uGrain;
4549 uint32_t uGDEntry, uLastGDEntry;
4550 uint32_t cbGrain = 0;
4551 uint32_t uCacheLine, uCacheEntry;
4552 const void *pData;
4553 int rc;
4554
4555 /* Very strict requirements: always write at least one full grain, with
4556 * proper alignment. Everything else would require reading of already
4557 * written data, which we don't support for obvious reasons. The only
4558 * exception is the last grain, and only if the image size specifies
4559 * that only some portion holds data. In any case the write must be
4560 * within the image limits, no "overshoot" allowed. */
4561 if ( cbWrite == 0
4562 || ( cbWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
4563 && pExtent->cNominalSectors - uSector >= pExtent->cSectorsPerGrain)
4564 || uSector % pExtent->cSectorsPerGrain
4565 || uSector + VMDK_BYTE2SECTOR(cbWrite) > pExtent->cNominalSectors)
4566 return VERR_INVALID_PARAMETER;
4567
4568 /* Clip write range to at most the rest of the grain. */
4569 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain));
4570
4571 /* Do not allow to go back. */
4572 uGrain = uSector / pExtent->cSectorsPerGrain;
4573 uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
4574 uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE;
4575 uGDEntry = uGrain / pExtent->cGTEntries;
4576 uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
4577 if (uGrain < pExtent->uLastGrainAccess)
4578 return VERR_VD_VMDK_INVALID_WRITE;
4579
4580 /* Zero byte write optimization. Since we don't tell VBoxHDD that we need
4581 * to allocate something, we also need to detect the situation ourself. */
4582 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
4583 && vdIfIoIntIoCtxIsZero(pImage->pIfIo, pIoCtx, cbWrite, true /* fAdvance */))
4584 return VINF_SUCCESS;
4585
4586 if (uGDEntry != uLastGDEntry)
4587 {
4588 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
4589 if (RT_FAILURE(rc))
4590 return rc;
4591 vmdkStreamClearGT(pImage, pExtent);
4592 for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++)
4593 {
4594 rc = vmdkStreamFlushGT(pImage, pExtent, i);
4595 if (RT_FAILURE(rc))
4596 return rc;
4597 }
4598 }
4599
4600 uint64_t uFileOffset;
4601 uFileOffset = pExtent->uAppendPosition;
4602 if (!uFileOffset)
4603 return VERR_INTERNAL_ERROR;
4604 /* Align to sector, as the previous write could have been any size. */
4605 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
4606
4607 /* Paranoia check: extent type, grain table buffer presence and
4608 * grain table buffer space. Also grain table entry must be clear. */
4609 if ( pExtent->enmType != VMDKETYPE_HOSTED_SPARSE
4610 || !pImage->pGTCache
4611 || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE
4612 || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry])
4613 return VERR_INTERNAL_ERROR;
4614
4615 /* Update grain table entry. */
4616 pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset);
4617
4618 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
4619 {
4620 vdIfIoIntIoCtxCopyFrom(pImage->pIfIo, pIoCtx, pExtent->pvGrain, cbWrite);
4621 memset((char *)pExtent->pvGrain + cbWrite, '\0',
4622 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite);
4623 pData = pExtent->pvGrain;
4624 }
4625 else
4626 {
4627 RTSGSEG Segment;
4628 unsigned cSegments = 1;
4629 size_t cbSeg = 0;
4630
4631 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
4632 &cSegments, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
4633 Assert(cbSeg == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
4634 pData = Segment.pvSeg;
4635 }
4636 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
4637 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
4638 uSector, &cbGrain);
4639 if (RT_FAILURE(rc))
4640 {
4641 pExtent->uGrainSectorAbs = 0;
4642 AssertRC(rc);
4643 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
4644 }
4645 pExtent->uLastGrainAccess = uGrain;
4646 pExtent->uAppendPosition += cbGrain;
4647
4648 return rc;
4649}
4650
4651/**
4652 * Internal: Updates the grain table during grain allocation.
4653 */
4654static int vmdkAllocGrainGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
4655 PVMDKGRAINALLOCASYNC pGrainAlloc)
4656{
4657 int rc = VINF_SUCCESS;
4658 PVMDKGTCACHE pCache = pImage->pGTCache;
4659 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
4660 uint32_t uGTHash, uGTBlockIndex;
4661 uint64_t uGTSector, uRGTSector, uGTBlock;
4662 uint64_t uSector = pGrainAlloc->uSector;
4663 PVMDKGTCACHEENTRY pGTCacheEntry;
4664
4665 LogFlowFunc(("pImage=%#p pExtent=%#p pCache=%#p pIoCtx=%#p pGrainAlloc=%#p\n",
4666 pImage, pExtent, pCache, pIoCtx, pGrainAlloc));
4667
4668 uGTSector = pGrainAlloc->uGTSector;
4669 uRGTSector = pGrainAlloc->uRGTSector;
4670 LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
4671
4672 /* Update the grain table (and the cache). */
4673 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
4674 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
4675 pGTCacheEntry = &pCache->aGTCache[uGTHash];
4676 if ( pGTCacheEntry->uExtent != pExtent->uExtent
4677 || pGTCacheEntry->uGTBlock != uGTBlock)
4678 {
4679 /* Cache miss, fetch data from disk. */
4680 LogFlow(("Cache miss, fetch data from disk\n"));
4681 PVDMETAXFER pMetaXfer = NULL;
4682 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4683 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
4684 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
4685 &pMetaXfer, vmdkAllocGrainComplete, pGrainAlloc);
4686 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4687 {
4688 pGrainAlloc->cIoXfersPending++;
4689 pGrainAlloc->fGTUpdateNeeded = true;
4690 /* Leave early, we will be called again after the read completed. */
4691 LogFlowFunc(("Metadata read in progress, leaving\n"));
4692 return rc;
4693 }
4694 else if (RT_FAILURE(rc))
4695 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
4696 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
4697 pGTCacheEntry->uExtent = pExtent->uExtent;
4698 pGTCacheEntry->uGTBlock = uGTBlock;
4699 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
4700 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
4701 }
4702 else
4703 {
4704 /* Cache hit. Convert grain table block back to disk format, otherwise
4705 * the code below will write garbage for all but the updated entry. */
4706 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
4707 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
4708 }
4709 pGrainAlloc->fGTUpdateNeeded = false;
4710 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
4711 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset));
4712 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset);
4713 /* Update grain table on disk. */
4714 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4715 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
4716 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
4717 vmdkAllocGrainComplete, pGrainAlloc);
4718 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4719 pGrainAlloc->cIoXfersPending++;
4720 else if (RT_FAILURE(rc))
4721 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
4722 if (pExtent->pRGD)
4723 {
4724 /* Update backup grain table on disk. */
4725 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4726 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
4727 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
4728 vmdkAllocGrainComplete, pGrainAlloc);
4729 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4730 pGrainAlloc->cIoXfersPending++;
4731 else if (RT_FAILURE(rc))
4732 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
4733 }
4734
4735 LogFlowFunc(("leaving rc=%Rrc\n", rc));
4736 return rc;
4737}
4738
4739/**
4740 * Internal - complete the grain allocation by updating disk grain table if required.
4741 */
4742static int vmdkAllocGrainComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
4743{
4744 RT_NOREF1(rcReq);
4745 int rc = VINF_SUCCESS;
4746 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4747 PVMDKGRAINALLOCASYNC pGrainAlloc = (PVMDKGRAINALLOCASYNC)pvUser;
4748
4749 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc\n",
4750 pBackendData, pIoCtx, pvUser, rcReq));
4751
4752 pGrainAlloc->cIoXfersPending--;
4753 if (!pGrainAlloc->cIoXfersPending && pGrainAlloc->fGTUpdateNeeded)
4754 rc = vmdkAllocGrainGTUpdate(pImage, pGrainAlloc->pExtent, pIoCtx, pGrainAlloc);
4755
4756 if (!pGrainAlloc->cIoXfersPending)
4757 {
4758 /* Grain allocation completed. */
4759 RTMemFree(pGrainAlloc);
4760 }
4761
4762 LogFlowFunc(("Leaving rc=%Rrc\n", rc));
4763 return rc;
4764}
4765
4766/**
4767 * Internal. Allocates a new grain table (if necessary).
4768 */
4769static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
4770 uint64_t uSector, uint64_t cbWrite)
4771{
4772 PVMDKGTCACHE pCache = pImage->pGTCache; NOREF(pCache);
4773 uint64_t uGDIndex, uGTSector, uRGTSector;
4774 uint64_t uFileOffset;
4775 PVMDKGRAINALLOCASYNC pGrainAlloc = NULL;
4776 int rc;
4777
4778 LogFlowFunc(("pCache=%#p pExtent=%#p pIoCtx=%#p uSector=%llu cbWrite=%llu\n",
4779 pCache, pExtent, pIoCtx, uSector, cbWrite));
4780
4781 pGrainAlloc = (PVMDKGRAINALLOCASYNC)RTMemAllocZ(sizeof(VMDKGRAINALLOCASYNC));
4782 if (!pGrainAlloc)
4783 return VERR_NO_MEMORY;
4784
4785 pGrainAlloc->pExtent = pExtent;
4786 pGrainAlloc->uSector = uSector;
4787
4788 uGDIndex = uSector / pExtent->cSectorsPerGDE;
4789 if (uGDIndex >= pExtent->cGDEntries)
4790 {
4791 RTMemFree(pGrainAlloc);
4792 return VERR_OUT_OF_RANGE;
4793 }
4794 uGTSector = pExtent->pGD[uGDIndex];
4795 if (pExtent->pRGD)
4796 uRGTSector = pExtent->pRGD[uGDIndex];
4797 else
4798 uRGTSector = 0; /**< avoid compiler warning */
4799 if (!uGTSector)
4800 {
4801 LogFlow(("Allocating new grain table\n"));
4802
4803 /* There is no grain table referenced by this grain directory
4804 * entry. So there is absolutely no data in this area. Allocate
4805 * a new grain table and put the reference to it in the GDs. */
4806 uFileOffset = pExtent->uAppendPosition;
4807 if (!uFileOffset)
4808 {
4809 RTMemFree(pGrainAlloc);
4810 return VERR_INTERNAL_ERROR;
4811 }
4812 Assert(!(uFileOffset % 512));
4813
4814 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
4815 uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
4816
4817 /* Normally the grain table is preallocated for hosted sparse extents
4818 * that support more than 32 bit sector numbers. So this shouldn't
4819 * ever happen on a valid extent. */
4820 if (uGTSector > UINT32_MAX)
4821 {
4822 RTMemFree(pGrainAlloc);
4823 return VERR_VD_VMDK_INVALID_HEADER;
4824 }
4825
4826 /* Write grain table by writing the required number of grain table
4827 * cache chunks. Allocate memory dynamically here or we flood the
4828 * metadata cache with very small entries. */
4829 size_t cbGTDataTmp = pExtent->cGTEntries * sizeof(uint32_t);
4830 uint32_t *paGTDataTmp = (uint32_t *)RTMemTmpAllocZ(cbGTDataTmp);
4831
4832 if (!paGTDataTmp)
4833 {
4834 RTMemFree(pGrainAlloc);
4835 return VERR_NO_MEMORY;
4836 }
4837
4838 memset(paGTDataTmp, '\0', cbGTDataTmp);
4839 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4840 VMDK_SECTOR2BYTE(uGTSector),
4841 paGTDataTmp, cbGTDataTmp, pIoCtx,
4842 vmdkAllocGrainComplete, pGrainAlloc);
4843 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4844 pGrainAlloc->cIoXfersPending++;
4845 else if (RT_FAILURE(rc))
4846 {
4847 RTMemTmpFree(paGTDataTmp);
4848 RTMemFree(pGrainAlloc);
4849 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
4850 }
4851 pExtent->uAppendPosition = RT_ALIGN_64( pExtent->uAppendPosition
4852 + cbGTDataTmp, 512);
4853
4854 if (pExtent->pRGD)
4855 {
4856 AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
4857 uFileOffset = pExtent->uAppendPosition;
4858 if (!uFileOffset)
4859 return VERR_INTERNAL_ERROR;
4860 Assert(!(uFileOffset % 512));
4861 uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
4862
4863 /* Normally the redundant grain table is preallocated for hosted
4864 * sparse extents that support more than 32 bit sector numbers. So
4865 * this shouldn't ever happen on a valid extent. */
4866 if (uRGTSector > UINT32_MAX)
4867 {
4868 RTMemTmpFree(paGTDataTmp);
4869 return VERR_VD_VMDK_INVALID_HEADER;
4870 }
4871
4872 /* Write grain table by writing the required number of grain table
4873 * cache chunks. Allocate memory dynamically here or we flood the
4874 * metadata cache with very small entries. */
4875 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4876 VMDK_SECTOR2BYTE(uRGTSector),
4877 paGTDataTmp, cbGTDataTmp, pIoCtx,
4878 vmdkAllocGrainComplete, pGrainAlloc);
4879 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4880 pGrainAlloc->cIoXfersPending++;
4881 else if (RT_FAILURE(rc))
4882 {
4883 RTMemTmpFree(paGTDataTmp);
4884 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
4885 }
4886
4887 pExtent->uAppendPosition = pExtent->uAppendPosition + cbGTDataTmp;
4888 }
4889
4890 RTMemTmpFree(paGTDataTmp);
4891
4892 /* Update the grain directory on disk (doing it before writing the
4893 * grain table will result in a garbled extent if the operation is
4894 * aborted for some reason. Otherwise the worst that can happen is
4895 * some unused sectors in the extent. */
4896 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
4897 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4898 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
4899 &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
4900 vmdkAllocGrainComplete, pGrainAlloc);
4901 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4902 pGrainAlloc->cIoXfersPending++;
4903 else if (RT_FAILURE(rc))
4904 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
4905 if (pExtent->pRGD)
4906 {
4907 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
4908 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
4909 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
4910 &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
4911 vmdkAllocGrainComplete, pGrainAlloc);
4912 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4913 pGrainAlloc->cIoXfersPending++;
4914 else if (RT_FAILURE(rc))
4915 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
4916 }
4917
4918 /* As the final step update the in-memory copy of the GDs. */
4919 pExtent->pGD[uGDIndex] = uGTSector;
4920 if (pExtent->pRGD)
4921 pExtent->pRGD[uGDIndex] = uRGTSector;
4922 }
4923
4924 LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
4925 pGrainAlloc->uGTSector = uGTSector;
4926 pGrainAlloc->uRGTSector = uRGTSector;
4927
4928 uFileOffset = pExtent->uAppendPosition;
4929 if (!uFileOffset)
4930 return VERR_INTERNAL_ERROR;
4931 Assert(!(uFileOffset % 512));
4932
4933 pGrainAlloc->uGrainOffset = uFileOffset;
4934
4935 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
4936 {
4937 AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
4938 ("Accesses to stream optimized images must be synchronous\n"),
4939 VERR_INVALID_STATE);
4940
4941 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
4942 return vdIfError(pImage->pIfError, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
4943
4944 /* Invalidate cache, just in case some code incorrectly allows mixing
4945 * of reads and writes. Normally shouldn't be needed. */
4946 pExtent->uGrainSectorAbs = 0;
4947
4948 /* Write compressed data block and the markers. */
4949 uint32_t cbGrain = 0;
4950 size_t cbSeg = 0;
4951 RTSGSEG Segment;
4952 unsigned cSegments = 1;
4953
4954 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
4955 &cSegments, cbWrite);
4956 Assert(cbSeg == cbWrite);
4957
4958 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
4959 Segment.pvSeg, cbWrite, uSector, &cbGrain);
4960 if (RT_FAILURE(rc))
4961 {
4962 AssertRC(rc);
4963 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
4964 }
4965 pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
4966 pExtent->uAppendPosition += cbGrain;
4967 }
4968 else
4969 {
4970 /* Write the data. Always a full grain, or we're in big trouble. */
4971 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
4972 uFileOffset, pIoCtx, cbWrite,
4973 vmdkAllocGrainComplete, pGrainAlloc);
4974 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4975 pGrainAlloc->cIoXfersPending++;
4976 else if (RT_FAILURE(rc))
4977 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
4978
4979 pExtent->uAppendPosition += cbWrite;
4980 }
4981
4982 rc = vmdkAllocGrainGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
4983
4984 if (!pGrainAlloc->cIoXfersPending)
4985 {
4986 /* Grain allocation completed. */
4987 RTMemFree(pGrainAlloc);
4988 }
4989
4990 LogFlowFunc(("leaving rc=%Rrc\n", rc));
4991
4992 return rc;
4993}
4994
4995/**
4996 * Internal. Reads the contents by sequentially going over the compressed
4997 * grains (hoping that they are in sequence).
4998 */
4999static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
5000 uint64_t uSector, PVDIOCTX pIoCtx,
5001 uint64_t cbRead)
5002{
5003 int rc;
5004
5005 LogFlowFunc(("pImage=%#p pExtent=%#p uSector=%llu pIoCtx=%#p cbRead=%llu\n",
5006 pImage, pExtent, uSector, pIoCtx, cbRead));
5007
5008 AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
5009 ("Async I/O not supported for sequential stream optimized images\n"),
5010 VERR_INVALID_STATE);
5011
5012 /* Do not allow to go back. */
5013 uint32_t uGrain = uSector / pExtent->cSectorsPerGrain;
5014 if (uGrain < pExtent->uLastGrainAccess)
5015 return VERR_VD_VMDK_INVALID_STATE;
5016 pExtent->uLastGrainAccess = uGrain;
5017
5018 /* After a previous error do not attempt to recover, as it would need
5019 * seeking (in the general case backwards which is forbidden). */
5020 if (!pExtent->uGrainSectorAbs)
5021 return VERR_VD_VMDK_INVALID_STATE;
5022
5023 /* Check if we need to read something from the image or if what we have
5024 * in the buffer is good to fulfill the request. */
5025 if (!pExtent->cbGrainStreamRead || uGrain > pExtent->uGrain)
5026 {
5027 uint32_t uGrainSectorAbs = pExtent->uGrainSectorAbs
5028 + VMDK_BYTE2SECTOR(pExtent->cbGrainStreamRead);
5029
5030 /* Get the marker from the next data block - and skip everything which
5031 * is not a compressed grain. If it's a compressed grain which is for
5032 * the requested sector (or after), read it. */
5033 VMDKMARKER Marker;
5034 do
5035 {
5036 RT_ZERO(Marker);
5037 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
5038 VMDK_SECTOR2BYTE(uGrainSectorAbs),
5039 &Marker, RT_OFFSETOF(VMDKMARKER, uType));
5040 if (RT_FAILURE(rc))
5041 return rc;
5042 Marker.uSector = RT_LE2H_U64(Marker.uSector);
5043 Marker.cbSize = RT_LE2H_U32(Marker.cbSize);
5044
5045 if (Marker.cbSize == 0)
5046 {
5047 /* A marker for something else than a compressed grain. */
5048 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
5049 VMDK_SECTOR2BYTE(uGrainSectorAbs)
5050 + RT_OFFSETOF(VMDKMARKER, uType),
5051 &Marker.uType, sizeof(Marker.uType));
5052 if (RT_FAILURE(rc))
5053 return rc;
5054 Marker.uType = RT_LE2H_U32(Marker.uType);
5055 switch (Marker.uType)
5056 {
5057 case VMDK_MARKER_EOS:
5058 uGrainSectorAbs++;
5059 /* Read (or mostly skip) to the end of file. Uses the
5060 * Marker (LBA sector) as it is unused anyway. This
5061 * makes sure that really everything is read in the
5062 * success case. If this read fails it means the image
5063 * is truncated, but this is harmless so ignore. */
5064 vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
5065 VMDK_SECTOR2BYTE(uGrainSectorAbs)
5066 + 511,
5067 &Marker.uSector, 1);
5068 break;
5069 case VMDK_MARKER_GT:
5070 uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
5071 break;
5072 case VMDK_MARKER_GD:
5073 uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(RT_ALIGN(pExtent->cGDEntries * sizeof(uint32_t), 512));
5074 break;
5075 case VMDK_MARKER_FOOTER:
5076 uGrainSectorAbs += 2;
5077 break;
5078 case VMDK_MARKER_UNSPECIFIED:
5079 /* Skip over the contents of the unspecified marker
5080 * type 4 which exists in some vSphere created files. */
5081 /** @todo figure out what the payload means. */
5082 uGrainSectorAbs += 1;
5083 break;
5084 default:
5085 AssertMsgFailed(("VMDK: corrupted marker, type=%#x\n", Marker.uType));
5086 pExtent->uGrainSectorAbs = 0;
5087 return VERR_VD_VMDK_INVALID_STATE;
5088 }
5089 pExtent->cbGrainStreamRead = 0;
5090 }
5091 else
5092 {
5093 /* A compressed grain marker. If it is at/after what we're
5094 * interested in read and decompress data. */
5095 if (uSector > Marker.uSector + pExtent->cSectorsPerGrain)
5096 {
5097 uGrainSectorAbs += VMDK_BYTE2SECTOR(RT_ALIGN(Marker.cbSize + RT_OFFSETOF(VMDKMARKER, uType), 512));
5098 continue;
5099 }
5100 uint64_t uLBA = 0;
5101 uint32_t cbGrainStreamRead = 0;
5102 rc = vmdkFileInflateSync(pImage, pExtent,
5103 VMDK_SECTOR2BYTE(uGrainSectorAbs),
5104 pExtent->pvGrain,
5105 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
5106 &Marker, &uLBA, &cbGrainStreamRead);
5107 if (RT_FAILURE(rc))
5108 {
5109 pExtent->uGrainSectorAbs = 0;
5110 return rc;
5111 }
5112 if ( pExtent->uGrain
5113 && uLBA / pExtent->cSectorsPerGrain <= pExtent->uGrain)
5114 {
5115 pExtent->uGrainSectorAbs = 0;
5116 return VERR_VD_VMDK_INVALID_STATE;
5117 }
5118 pExtent->uGrain = uLBA / pExtent->cSectorsPerGrain;
5119 pExtent->cbGrainStreamRead = cbGrainStreamRead;
5120 break;
5121 }
5122 } while (Marker.uType != VMDK_MARKER_EOS);
5123
5124 pExtent->uGrainSectorAbs = uGrainSectorAbs;
5125
5126 if (!pExtent->cbGrainStreamRead && Marker.uType == VMDK_MARKER_EOS)
5127 {
5128 pExtent->uGrain = UINT32_MAX;
5129 /* Must set a non-zero value for pExtent->cbGrainStreamRead or
5130 * the next read would try to get more data, and we're at EOF. */
5131 pExtent->cbGrainStreamRead = 1;
5132 }
5133 }
5134
5135 if (pExtent->uGrain > uSector / pExtent->cSectorsPerGrain)
5136 {
5137 /* The next data block we have is not for this area, so just return
5138 * that there is no data. */
5139 LogFlowFunc(("returns VERR_VD_BLOCK_FREE\n"));
5140 return VERR_VD_BLOCK_FREE;
5141 }
5142
5143 uint32_t uSectorInGrain = uSector % pExtent->cSectorsPerGrain;
5144 vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
5145 (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
5146 cbRead);
5147 LogFlowFunc(("returns VINF_SUCCESS\n"));
5148 return VINF_SUCCESS;
5149}
5150
5151/**
5152 * Replaces a fragment of a string with the specified string.
5153 *
5154 * @returns Pointer to the allocated UTF-8 string.
5155 * @param pszWhere UTF-8 string to search in.
5156 * @param pszWhat UTF-8 string to search for.
5157 * @param pszByWhat UTF-8 string to replace the found string with.
5158 */
5159static char *vmdkStrReplace(const char *pszWhere, const char *pszWhat,
5160 const char *pszByWhat)
5161{
5162 AssertPtr(pszWhere);
5163 AssertPtr(pszWhat);
5164 AssertPtr(pszByWhat);
5165 const char *pszFoundStr = strstr(pszWhere, pszWhat);
5166 if (!pszFoundStr)
5167 return NULL;
5168 size_t cFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
5169 char *pszNewStr = (char *)RTMemAlloc(cFinal);
5170 if (pszNewStr)
5171 {
5172 char *pszTmp = pszNewStr;
5173 memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
5174 pszTmp += pszFoundStr - pszWhere;
5175 memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
5176 pszTmp += strlen(pszByWhat);
5177 strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
5178 }
5179 return pszNewStr;
5180}
5181
5182
5183/** @copydoc VDIMAGEBACKEND::pfnProbe */
5184static DECLCALLBACK(int) vmdkProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
5185 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
5186{
5187 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
5188 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
5189
5190 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
5191
5192 int rc = VINF_SUCCESS;
5193 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
5194 if (RT_LIKELY(pImage))
5195 {
5196 pImage->pszFilename = pszFilename;
5197 pImage->pFile = NULL;
5198 pImage->pExtents = NULL;
5199 pImage->pFiles = NULL;
5200 pImage->pGTCache = NULL;
5201 pImage->pDescData = NULL;
5202 pImage->pVDIfsDisk = pVDIfsDisk;
5203 pImage->pVDIfsImage = pVDIfsImage;
5204 /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
5205 * much as possible in vmdkOpenImage. */
5206 rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
5207 vmdkFreeImage(pImage, false);
5208 RTMemFree(pImage);
5209
5210 if (RT_SUCCESS(rc))
5211 *penmType = VDTYPE_HDD;
5212 }
5213 else
5214 rc = VERR_NO_MEMORY;
5215
5216 LogFlowFunc(("returns %Rrc\n", rc));
5217 return rc;
5218}
5219
5220/** @copydoc VDIMAGEBACKEND::pfnOpen */
5221static DECLCALLBACK(int) vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
5222 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
5223 VDTYPE enmType, void **ppBackendData)
5224{
5225 RT_NOREF1(enmType); /**< @todo r=klaus make use of the type info. */
5226
5227 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
5228 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
5229 int rc;
5230
5231 /* Check open flags. All valid flags are supported. */
5232 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
5233 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
5234
5235 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
5236 if (RT_LIKELY(pImage))
5237 {
5238 pImage->pszFilename = pszFilename;
5239 pImage->pFile = NULL;
5240 pImage->pExtents = NULL;
5241 pImage->pFiles = NULL;
5242 pImage->pGTCache = NULL;
5243 pImage->pDescData = NULL;
5244 pImage->pVDIfsDisk = pVDIfsDisk;
5245 pImage->pVDIfsImage = pVDIfsImage;
5246
5247 rc = vmdkOpenImage(pImage, uOpenFlags);
5248 if (RT_SUCCESS(rc))
5249 *ppBackendData = pImage;
5250 else
5251 RTMemFree(pImage);
5252 }
5253 else
5254 rc = VERR_NO_MEMORY;
5255
5256 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
5257 return rc;
5258}
5259
5260/** @copydoc VDIMAGEBACKEND::pfnCreate */
5261static DECLCALLBACK(int) vmdkCreate(const char *pszFilename, uint64_t cbSize,
5262 unsigned uImageFlags, const char *pszComment,
5263 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
5264 PCRTUUID pUuid, unsigned uOpenFlags,
5265 unsigned uPercentStart, unsigned uPercentSpan,
5266 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
5267 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
5268 void **ppBackendData)
5269{
5270 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",
5271 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
5272 int rc;
5273
5274 /* Check the VD container type and image flags. */
5275 if ( enmType != VDTYPE_HDD
5276 || (uImageFlags & ~VD_VMDK_IMAGE_FLAGS_MASK) != 0)
5277 return VERR_VD_INVALID_TYPE;
5278
5279 /* Check size. Maximum 256TB-64K for sparse images, otherwise unlimited. */
5280 if ( !cbSize
5281 || (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K))
5282 return VERR_VD_INVALID_SIZE;
5283
5284 /* Check image flags for invalid combinations. */
5285 if ( (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5286 && (uImageFlags & ~(VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_IMAGE_FLAGS_DIFF)))
5287 return VERR_INVALID_PARAMETER;
5288
5289 /* Check open flags. All valid flags are supported. */
5290 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
5291 AssertReturn( VALID_PTR(pszFilename)
5292 && *pszFilename
5293 && VALID_PTR(pPCHSGeometry)
5294 && VALID_PTR(pLCHSGeometry)
5295 && !( uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX
5296 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED)),
5297 VERR_INVALID_PARAMETER);
5298
5299 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
5300 if (RT_LIKELY(pImage))
5301 {
5302 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
5303
5304 pImage->pszFilename = pszFilename;
5305 pImage->pFile = NULL;
5306 pImage->pExtents = NULL;
5307 pImage->pFiles = NULL;
5308 pImage->pGTCache = NULL;
5309 pImage->pDescData = NULL;
5310 pImage->pVDIfsDisk = pVDIfsDisk;
5311 pImage->pVDIfsImage = pVDIfsImage;
5312 /* Descriptors for split images can be pretty large, especially if the
5313 * filename is long. So prepare for the worst, and allocate quite some
5314 * memory for the descriptor in this case. */
5315 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
5316 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(200);
5317 else
5318 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
5319 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
5320 if (RT_LIKELY(pImage->pDescData))
5321 {
5322 rc = vmdkCreateImage(pImage, cbSize, uImageFlags, pszComment,
5323 pPCHSGeometry, pLCHSGeometry, pUuid,
5324 pIfProgress, uPercentStart, uPercentSpan);
5325 if (RT_SUCCESS(rc))
5326 {
5327 /* So far the image is opened in read/write mode. Make sure the
5328 * image is opened in read-only mode if the caller requested that. */
5329 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5330 {
5331 vmdkFreeImage(pImage, false);
5332 rc = vmdkOpenImage(pImage, uOpenFlags);
5333 }
5334
5335 if (RT_SUCCESS(rc))
5336 *ppBackendData = pImage;
5337 }
5338
5339 if (RT_FAILURE(rc))
5340 RTMemFree(pImage->pDescData);
5341 }
5342 else
5343 rc = VERR_NO_MEMORY;
5344
5345 if (RT_FAILURE(rc))
5346 RTMemFree(pImage);
5347 }
5348 else
5349 rc = VERR_NO_MEMORY;
5350
5351 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
5352 return rc;
5353}
5354
5355/**
5356 * Prepares the state for renaming a VMDK image, setting up the state and allocating
5357 * memory.
5358 *
5359 * @returns VBox status code.
5360 * @param pImage VMDK image instance.
5361 * @param pRenameState The state to initialize.
5362 * @param pszFilename The new filename.
5363 */
5364static int vmdkRenameStatePrepare(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState, const char *pszFilename)
5365{
5366 int rc = VINF_SUCCESS;
5367
5368 memset(&pRenameState->DescriptorCopy, 0, sizeof(pRenameState->DescriptorCopy));
5369
5370 /*
5371 * Allocate an array to store both old and new names of renamed files
5372 * in case we have to roll back the changes. Arrays are initialized
5373 * with zeros. We actually save stuff when and if we change it.
5374 */
5375 pRenameState->cExtents = pImage->cExtents;
5376 pRenameState->apszOldName = (char **)RTMemTmpAllocZ((pRenameState->cExtents + 1) * sizeof(char*));
5377 pRenameState->apszNewName = (char **)RTMemTmpAllocZ((pRenameState->cExtents + 1) * sizeof(char*));
5378 pRenameState->apszNewLines = (char **)RTMemTmpAllocZ(pRenameState->cExtents * sizeof(char*));
5379 if ( pRenameState->apszOldName
5380 && pRenameState->apszNewName
5381 && pRenameState->apszNewLines)
5382 {
5383 /* Save the descriptor size and position. */
5384 if (pImage->pDescData)
5385 {
5386 /* Separate descriptor file. */
5387 pRenameState->fEmbeddedDesc = false;
5388 }
5389 else
5390 {
5391 /* Embedded descriptor file. */
5392 pRenameState->ExtentCopy = pImage->pExtents[0];
5393 pRenameState->fEmbeddedDesc = true;
5394 }
5395
5396 /* Save the descriptor content. */
5397 pRenameState->DescriptorCopy.cLines = pImage->Descriptor.cLines;
5398 for (unsigned i = 0; i < pRenameState->DescriptorCopy.cLines; i++)
5399 {
5400 pRenameState->DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
5401 if (!pRenameState->DescriptorCopy.aLines[i])
5402 {
5403 rc = VERR_NO_MEMORY;
5404 break;
5405 }
5406 }
5407
5408 if (RT_SUCCESS(rc))
5409 {
5410 /* Prepare both old and new base names used for string replacement. */
5411 pRenameState->pszNewBaseName = RTStrDup(RTPathFilename(pszFilename));
5412 RTPathStripSuffix(pRenameState->pszNewBaseName);
5413 pRenameState->pszOldBaseName = RTStrDup(RTPathFilename(pImage->pszFilename));
5414 RTPathStripSuffix(pRenameState->pszOldBaseName);
5415 /* Prepare both old and new full names used for string replacement. */
5416 pRenameState->pszNewFullName = RTStrDup(pszFilename);
5417 RTPathStripSuffix(pRenameState->pszNewFullName);
5418 pRenameState->pszOldFullName = RTStrDup(pImage->pszFilename);
5419 RTPathStripSuffix(pRenameState->pszOldFullName);
5420
5421 /* Save the old name for easy access to the old descriptor file. */
5422 pRenameState->pszOldDescName = RTStrDup(pImage->pszFilename);
5423 /* Save old image name. */
5424 pRenameState->pszOldImageName = pImage->pszFilename;
5425 }
5426 }
5427 else
5428 rc = VERR_NO_MEMORY;
5429
5430 return rc;
5431}
5432
5433/**
5434 * Destroys the given rename state, freeing all allocated memory.
5435 *
5436 * @returns nothing.
5437 * @param pRenameState The rename state to destroy.
5438 */
5439static void vmdkRenameStateDestroy(PVMDKRENAMESTATE pRenameState)
5440{
5441 for (unsigned i = 0; i < pRenameState->DescriptorCopy.cLines; i++)
5442 if (pRenameState->DescriptorCopy.aLines[i])
5443 RTStrFree(pRenameState->DescriptorCopy.aLines[i]);
5444 if (pRenameState->apszOldName)
5445 {
5446 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
5447 if (pRenameState->apszOldName[i])
5448 RTStrFree(pRenameState->apszOldName[i]);
5449 RTMemTmpFree(pRenameState->apszOldName);
5450 }
5451 if (pRenameState->apszNewName)
5452 {
5453 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
5454 if (pRenameState->apszNewName[i])
5455 RTStrFree(pRenameState->apszNewName[i]);
5456 RTMemTmpFree(pRenameState->apszNewName);
5457 }
5458 if (pRenameState->apszNewLines)
5459 {
5460 for (unsigned i = 0; i < pRenameState->cExtents; i++)
5461 if (pRenameState->apszNewLines[i])
5462 RTStrFree(pRenameState->apszNewLines[i]);
5463 RTMemTmpFree(pRenameState->apszNewLines);
5464 }
5465 if (pRenameState->pszOldDescName)
5466 RTStrFree(pRenameState->pszOldDescName);
5467 if (pRenameState->pszOldBaseName)
5468 RTStrFree(pRenameState->pszOldBaseName);
5469 if (pRenameState->pszNewBaseName)
5470 RTStrFree(pRenameState->pszNewBaseName);
5471 if (pRenameState->pszOldFullName)
5472 RTStrFree(pRenameState->pszOldFullName);
5473 if (pRenameState->pszNewFullName)
5474 RTStrFree(pRenameState->pszNewFullName);
5475}
5476
5477/**
5478 * Rolls back the rename operation to the original state.
5479 *
5480 * @returns VBox status code.
5481 * @param pImage VMDK image instance.
5482 * @param pRenameState The rename state.
5483 */
5484static int vmdkRenameRollback(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState)
5485{
5486 int rc = VINF_SUCCESS;
5487
5488 if (!pRenameState->fImageFreed)
5489 {
5490 /*
5491 * Some extents may have been closed, close the rest. We will
5492 * re-open the whole thing later.
5493 */
5494 vmdkFreeImage(pImage, false);
5495 }
5496
5497 /* Rename files back. */
5498 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
5499 {
5500 if (pRenameState->apszOldName[i])
5501 {
5502 rc = vdIfIoIntFileMove(pImage->pIfIo, pRenameState->apszNewName[i], pRenameState->apszOldName[i], 0);
5503 AssertRC(rc);
5504 }
5505 }
5506 /* Restore the old descriptor. */
5507 PVMDKFILE pFile;
5508 rc = vmdkFileOpen(pImage, &pFile, pRenameState->pszOldDescName,
5509 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_NORMAL,
5510 false /* fCreate */));
5511 AssertRC(rc);
5512 if (pRenameState->fEmbeddedDesc)
5513 {
5514 pRenameState->ExtentCopy.pFile = pFile;
5515 pImage->pExtents = &pRenameState->ExtentCopy;
5516 }
5517 else
5518 {
5519 /* Shouldn't be null for separate descriptor.
5520 * There will be no access to the actual content.
5521 */
5522 pImage->pDescData = pRenameState->pszOldDescName;
5523 pImage->pFile = pFile;
5524 }
5525 pImage->Descriptor = pRenameState->DescriptorCopy;
5526 vmdkWriteDescriptor(pImage, NULL);
5527 vmdkFileClose(pImage, &pFile, false);
5528 /* Get rid of the stuff we implanted. */
5529 pImage->pExtents = NULL;
5530 pImage->pFile = NULL;
5531 pImage->pDescData = NULL;
5532 /* Re-open the image back. */
5533 pImage->pszFilename = pRenameState->pszOldImageName;
5534 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
5535
5536 return rc;
5537}
5538
5539/**
5540 * Rename worker doing the real work.
5541 *
5542 * @returns VBox status code.
5543 * @param pImage VMDK image instance.
5544 * @param pRenameState The rename state.
5545 * @param pszFilename The new filename.
5546 */
5547static int vmdkRenameWorker(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState, const char *pszFilename)
5548{
5549 int rc = VINF_SUCCESS;
5550 unsigned i, line;
5551
5552 /* Update the descriptor with modified extent names. */
5553 for (i = 0, line = pImage->Descriptor.uFirstExtent;
5554 i < pRenameState->cExtents;
5555 i++, line = pImage->Descriptor.aNextLines[line])
5556 {
5557 /* Update the descriptor. */
5558 pRenameState->apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
5559 pRenameState->pszOldBaseName,
5560 pRenameState->pszNewBaseName);
5561 if (!pRenameState->apszNewLines[i])
5562 {
5563 rc = VERR_NO_MEMORY;
5564 break;
5565 }
5566 pImage->Descriptor.aLines[line] = pRenameState->apszNewLines[i];
5567 }
5568
5569 if (RT_SUCCESS(rc))
5570 {
5571 /* Make sure the descriptor gets written back. */
5572 pImage->Descriptor.fDirty = true;
5573 /* Flush the descriptor now, in case it is embedded. */
5574 vmdkFlushImage(pImage, NULL);
5575
5576 /* Close and rename/move extents. */
5577 for (i = 0; i < pRenameState->cExtents; i++)
5578 {
5579 PVMDKEXTENT pExtent = &pImage->pExtents[i];
5580 /* Compose new name for the extent. */
5581 pRenameState->apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
5582 pRenameState->pszOldFullName,
5583 pRenameState->pszNewFullName);
5584 if (!pRenameState->apszNewName[i])
5585 {
5586 rc = VERR_NO_MEMORY;
5587 break;
5588 }
5589 /* Close the extent file. */
5590 rc = vmdkFileClose(pImage, &pExtent->pFile, false);
5591 if (RT_FAILURE(rc))
5592 break;;
5593
5594 /* Rename the extent file. */
5595 rc = vdIfIoIntFileMove(pImage->pIfIo, pExtent->pszFullname, pRenameState->apszNewName[i], 0);
5596 if (RT_FAILURE(rc))
5597 break;
5598 /* Remember the old name. */
5599 pRenameState->apszOldName[i] = RTStrDup(pExtent->pszFullname);
5600 }
5601
5602 if (RT_SUCCESS(rc))
5603 {
5604 /* Release all old stuff. */
5605 rc = vmdkFreeImage(pImage, false);
5606 if (RT_SUCCESS(rc))
5607 {
5608 pRenameState->fImageFreed = true;
5609
5610 /* Last elements of new/old name arrays are intended for
5611 * storing descriptor's names.
5612 */
5613 pRenameState->apszNewName[pRenameState->cExtents] = RTStrDup(pszFilename);
5614 /* Rename the descriptor file if it's separate. */
5615 if (!pRenameState->fEmbeddedDesc)
5616 {
5617 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pRenameState->apszNewName[pRenameState->cExtents], 0);
5618 if (RT_SUCCESS(rc))
5619 {
5620 /* Save old name only if we may need to change it back. */
5621 pRenameState->apszOldName[pRenameState->cExtents] = RTStrDup(pszFilename);
5622 }
5623 }
5624
5625 /* Update pImage with the new information. */
5626 pImage->pszFilename = pszFilename;
5627
5628 /* Open the new image. */
5629 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
5630 }
5631 }
5632 }
5633
5634 return rc;
5635}
5636
5637/** @copydoc VDIMAGEBACKEND::pfnRename */
5638static DECLCALLBACK(int) vmdkRename(void *pBackendData, const char *pszFilename)
5639{
5640 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
5641
5642 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5643 VMDKRENAMESTATE RenameState;
5644
5645 memset(&RenameState, 0, sizeof(RenameState));
5646
5647 /* Check arguments. */
5648 AssertReturn(( pImage
5649 && VALID_PTR(pszFilename)
5650 && *pszFilename
5651 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)), VERR_INVALID_PARAMETER);
5652
5653 int rc = vmdkRenameStatePrepare(pImage, &RenameState, pszFilename);
5654 if (RT_SUCCESS(rc))
5655 {
5656 /* --- Up to this point we have not done any damage yet. --- */
5657
5658 rc = vmdkRenameWorker(pImage, &RenameState, pszFilename);
5659 /* Roll back all changes in case of failure. */
5660 if (RT_FAILURE(rc))
5661 {
5662 int rrc = vmdkRenameRollback(pImage, &RenameState);
5663 AssertRC(rrc);
5664 }
5665 }
5666
5667 vmdkRenameStateDestroy(&RenameState);
5668 LogFlowFunc(("returns %Rrc\n", rc));
5669 return rc;
5670}
5671
5672/** @copydoc VDIMAGEBACKEND::pfnClose */
5673static DECLCALLBACK(int) vmdkClose(void *pBackendData, bool fDelete)
5674{
5675 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
5676 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5677
5678 int rc = vmdkFreeImage(pImage, fDelete);
5679 RTMemFree(pImage);
5680
5681 LogFlowFunc(("returns %Rrc\n", rc));
5682 return rc;
5683}
5684
5685/** @copydoc VDIMAGEBACKEND::pfnRead */
5686static DECLCALLBACK(int) vmdkRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
5687 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
5688{
5689 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
5690 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
5691 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5692
5693 AssertPtr(pImage);
5694 Assert(uOffset % 512 == 0);
5695 Assert(cbToRead % 512 == 0);
5696 AssertReturn((VALID_PTR(pIoCtx) && cbToRead), VERR_INVALID_PARAMETER);
5697 AssertReturn(uOffset + cbToRead <= pImage->cbSize, VERR_INVALID_PARAMETER);
5698
5699 /* Find the extent and check access permissions as defined in the extent descriptor. */
5700 PVMDKEXTENT pExtent;
5701 uint64_t uSectorExtentRel;
5702 int rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5703 &pExtent, &uSectorExtentRel);
5704 if ( RT_SUCCESS(rc)
5705 && pExtent->enmAccess != VMDKACCESS_NOACCESS)
5706 {
5707 /* Clip read range to remain in this extent. */
5708 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5709
5710 /* Handle the read according to the current extent type. */
5711 switch (pExtent->enmType)
5712 {
5713 case VMDKETYPE_HOSTED_SPARSE:
5714 {
5715 uint64_t uSectorExtentAbs;
5716
5717 rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
5718 if (RT_FAILURE(rc))
5719 break;
5720 /* Clip read range to at most the rest of the grain. */
5721 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
5722 Assert(!(cbToRead % 512));
5723 if (uSectorExtentAbs == 0)
5724 {
5725 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5726 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5727 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
5728 rc = VERR_VD_BLOCK_FREE;
5729 else
5730 rc = vmdkStreamReadSequential(pImage, pExtent,
5731 uSectorExtentRel,
5732 pIoCtx, cbToRead);
5733 }
5734 else
5735 {
5736 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5737 {
5738 AssertMsg(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
5739 ("Async I/O is not supported for stream optimized VMDK's\n"));
5740
5741 uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
5742 uSectorExtentAbs -= uSectorInGrain;
5743 if (pExtent->uGrainSectorAbs != uSectorExtentAbs)
5744 {
5745 uint64_t uLBA = 0; /* gcc maybe uninitialized */
5746 rc = vmdkFileInflateSync(pImage, pExtent,
5747 VMDK_SECTOR2BYTE(uSectorExtentAbs),
5748 pExtent->pvGrain,
5749 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
5750 NULL, &uLBA, NULL);
5751 if (RT_FAILURE(rc))
5752 {
5753 pExtent->uGrainSectorAbs = 0;
5754 break;
5755 }
5756 pExtent->uGrainSectorAbs = uSectorExtentAbs;
5757 pExtent->uGrain = uSectorExtentRel / pExtent->cSectorsPerGrain;
5758 Assert(uLBA == uSectorExtentRel);
5759 }
5760 vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
5761 (uint8_t *)pExtent->pvGrain
5762 + VMDK_SECTOR2BYTE(uSectorInGrain),
5763 cbToRead);
5764 }
5765 else
5766 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
5767 VMDK_SECTOR2BYTE(uSectorExtentAbs),
5768 pIoCtx, cbToRead);
5769 }
5770 break;
5771 }
5772 case VMDKETYPE_VMFS:
5773 case VMDKETYPE_FLAT:
5774 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
5775 VMDK_SECTOR2BYTE(uSectorExtentRel),
5776 pIoCtx, cbToRead);
5777 break;
5778 case VMDKETYPE_ZERO:
5779 {
5780 size_t cbSet;
5781
5782 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
5783 Assert(cbSet == cbToRead);
5784 break;
5785 }
5786 }
5787 if (pcbActuallyRead)
5788 *pcbActuallyRead = cbToRead;
5789 }
5790 else if (RT_SUCCESS(rc))
5791 rc = VERR_VD_VMDK_INVALID_STATE;
5792
5793 LogFlowFunc(("returns %Rrc\n", rc));
5794 return rc;
5795}
5796
5797/** @copydoc VDIMAGEBACKEND::pfnWrite */
5798static DECLCALLBACK(int) vmdkWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
5799 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
5800 size_t *pcbPostRead, unsigned fWrite)
5801{
5802 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
5803 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
5804 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5805 int rc;
5806
5807 AssertPtr(pImage);
5808 Assert(uOffset % 512 == 0);
5809 Assert(cbToWrite % 512 == 0);
5810 AssertReturn((VALID_PTR(pIoCtx) && cbToWrite), VERR_INVALID_PARAMETER);
5811
5812 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5813 {
5814 PVMDKEXTENT pExtent;
5815 uint64_t uSectorExtentRel;
5816 uint64_t uSectorExtentAbs;
5817
5818 /* No size check here, will do that later when the extent is located.
5819 * There are sparse images out there which according to the spec are
5820 * invalid, because the total size is not a multiple of the grain size.
5821 * Also for sparse images which are stitched together in odd ways (not at
5822 * grain boundaries, and with the nominal size not being a multiple of the
5823 * grain size), this would prevent writing to the last grain. */
5824
5825 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5826 &pExtent, &uSectorExtentRel);
5827 if (RT_SUCCESS(rc))
5828 {
5829 if ( pExtent->enmAccess != VMDKACCESS_READWRITE
5830 && ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5831 && !pImage->pExtents[0].uAppendPosition
5832 && pExtent->enmAccess != VMDKACCESS_READONLY))
5833 rc = VERR_VD_VMDK_INVALID_STATE;
5834 else
5835 {
5836 /* Handle the write according to the current extent type. */
5837 switch (pExtent->enmType)
5838 {
5839 case VMDKETYPE_HOSTED_SPARSE:
5840 rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
5841 if (RT_SUCCESS(rc))
5842 {
5843 if ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
5844 && uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
5845 rc = VERR_VD_VMDK_INVALID_WRITE;
5846 else
5847 {
5848 /* Clip write range to at most the rest of the grain. */
5849 cbToWrite = RT_MIN(cbToWrite,
5850 VMDK_SECTOR2BYTE( pExtent->cSectorsPerGrain
5851 - uSectorExtentRel % pExtent->cSectorsPerGrain));
5852 if (uSectorExtentAbs == 0)
5853 {
5854 if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5855 {
5856 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
5857 {
5858 /* Full block write to a previously unallocated block.
5859 * Check if the caller wants to avoid the automatic alloc. */
5860 if (!(fWrite & VD_WRITE_NO_ALLOC))
5861 {
5862 /* Allocate GT and find out where to store the grain. */
5863 rc = vmdkAllocGrain(pImage, pExtent, pIoCtx,
5864 uSectorExtentRel, cbToWrite);
5865 }
5866 else
5867 rc = VERR_VD_BLOCK_FREE;
5868 *pcbPreRead = 0;
5869 *pcbPostRead = 0;
5870 }
5871 else
5872 {
5873 /* Clip write range to remain in this extent. */
5874 cbToWrite = RT_MIN(cbToWrite,
5875 VMDK_SECTOR2BYTE( pExtent->uSectorOffset
5876 + pExtent->cNominalSectors - uSectorExtentRel));
5877 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
5878 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
5879 rc = VERR_VD_BLOCK_FREE;
5880 }
5881 }
5882 else
5883 rc = vmdkStreamAllocGrain(pImage, pExtent, uSectorExtentRel,
5884 pIoCtx, cbToWrite);
5885 }
5886 else
5887 {
5888 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5889 {
5890 /* A partial write to a streamOptimized image is simply
5891 * invalid. It requires rewriting already compressed data
5892 * which is somewhere between expensive and impossible. */
5893 rc = VERR_VD_VMDK_INVALID_STATE;
5894 pExtent->uGrainSectorAbs = 0;
5895 AssertRC(rc);
5896 }
5897 else
5898 {
5899 Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
5900 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
5901 VMDK_SECTOR2BYTE(uSectorExtentAbs),
5902 pIoCtx, cbToWrite, NULL, NULL);
5903 }
5904 }
5905 }
5906 }
5907 break;
5908 case VMDKETYPE_VMFS:
5909 case VMDKETYPE_FLAT:
5910 /* Clip write range to remain in this extent. */
5911 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5912 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
5913 VMDK_SECTOR2BYTE(uSectorExtentRel),
5914 pIoCtx, cbToWrite, NULL, NULL);
5915 break;
5916 case VMDKETYPE_ZERO:
5917 /* Clip write range to remain in this extent. */
5918 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5919 break;
5920 }
5921 }
5922
5923 if (pcbWriteProcess)
5924 *pcbWriteProcess = cbToWrite;
5925 }
5926 }
5927 else
5928 rc = VERR_VD_IMAGE_READ_ONLY;
5929
5930 LogFlowFunc(("returns %Rrc\n", rc));
5931 return rc;
5932}
5933
5934/** @copydoc VDIMAGEBACKEND::pfnFlush */
5935static DECLCALLBACK(int) vmdkFlush(void *pBackendData, PVDIOCTX pIoCtx)
5936{
5937 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5938
5939 return vmdkFlushImage(pImage, pIoCtx);
5940}
5941
5942/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
5943static DECLCALLBACK(unsigned) vmdkGetVersion(void *pBackendData)
5944{
5945 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5946 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5947
5948 AssertPtrReturn(pImage, 0);
5949
5950 return VMDK_IMAGE_VERSION;
5951}
5952
5953/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
5954static DECLCALLBACK(uint64_t) vmdkGetFileSize(void *pBackendData)
5955{
5956 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
5957 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5958 uint64_t cb = 0;
5959
5960 AssertPtrReturn(pImage, 0);
5961
5962 if (pImage->pFile != NULL)
5963 {
5964 uint64_t cbFile;
5965 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pFile->pStorage, &cbFile);
5966 if (RT_SUCCESS(rc))
5967 cb += cbFile;
5968 }
5969 for (unsigned i = 0; i < pImage->cExtents; i++)
5970 {
5971 if (pImage->pExtents[i].pFile != NULL)
5972 {
5973 uint64_t cbFile;
5974 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pExtents[i].pFile->pStorage, &cbFile);
5975 if (RT_SUCCESS(rc))
5976 cb += cbFile;
5977 }
5978 }
5979
5980 LogFlowFunc(("returns %lld\n", cb));
5981 return cb;
5982}
5983
5984/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
5985static DECLCALLBACK(int) vmdkGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
5986{
5987 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
5988 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5989 int rc = VINF_SUCCESS;
5990
5991 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
5992
5993 if (pImage->PCHSGeometry.cCylinders)
5994 *pPCHSGeometry = pImage->PCHSGeometry;
5995 else
5996 rc = VERR_VD_GEOMETRY_NOT_SET;
5997
5998 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
5999 return rc;
6000}
6001
6002/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
6003static DECLCALLBACK(int) vmdkSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
6004{
6005 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
6006 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
6007 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6008 int rc = VINF_SUCCESS;
6009
6010 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6011
6012 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6013 {
6014 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6015 {
6016 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
6017 if (RT_SUCCESS(rc))
6018 pImage->PCHSGeometry = *pPCHSGeometry;
6019 }
6020 else
6021 rc = VERR_NOT_SUPPORTED;
6022 }
6023 else
6024 rc = VERR_VD_IMAGE_READ_ONLY;
6025
6026 LogFlowFunc(("returns %Rrc\n", rc));
6027 return rc;
6028}
6029
6030/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
6031static DECLCALLBACK(int) vmdkGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
6032{
6033 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
6034 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6035 int rc = VINF_SUCCESS;
6036
6037 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6038
6039 if (pImage->LCHSGeometry.cCylinders)
6040 *pLCHSGeometry = pImage->LCHSGeometry;
6041 else
6042 rc = VERR_VD_GEOMETRY_NOT_SET;
6043
6044 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
6045 return rc;
6046}
6047
6048/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
6049static DECLCALLBACK(int) vmdkSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
6050{
6051 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
6052 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
6053 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6054 int rc = VINF_SUCCESS;
6055
6056 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6057
6058 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6059 {
6060 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6061 {
6062 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
6063 if (RT_SUCCESS(rc))
6064 pImage->LCHSGeometry = *pLCHSGeometry;
6065 }
6066 else
6067 rc = VERR_NOT_SUPPORTED;
6068 }
6069 else
6070 rc = VERR_VD_IMAGE_READ_ONLY;
6071
6072 LogFlowFunc(("returns %Rrc\n", rc));
6073 return rc;
6074}
6075
6076/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
6077static DECLCALLBACK(int) vmdkQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
6078{
6079 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
6080 PVMDKIMAGE pThis = (PVMDKIMAGE)pBackendData;
6081
6082 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
6083
6084 *ppRegionList = &pThis->RegionList;
6085 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
6086 return VINF_SUCCESS;
6087}
6088
6089/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
6090static DECLCALLBACK(void) vmdkRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
6091{
6092 RT_NOREF1(pRegionList);
6093 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
6094 PVMDKIMAGE pThis = (PVMDKIMAGE)pBackendData;
6095 AssertPtr(pThis); RT_NOREF(pThis);
6096
6097 /* Nothing to do here. */
6098}
6099
6100/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
6101static DECLCALLBACK(unsigned) vmdkGetImageFlags(void *pBackendData)
6102{
6103 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
6104 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6105
6106 AssertPtrReturn(pImage, 0);
6107
6108 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
6109 return pImage->uImageFlags;
6110}
6111
6112/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
6113static DECLCALLBACK(unsigned) vmdkGetOpenFlags(void *pBackendData)
6114{
6115 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
6116 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6117
6118 AssertPtrReturn(pImage, 0);
6119
6120 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
6121 return pImage->uOpenFlags;
6122}
6123
6124/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
6125static DECLCALLBACK(int) vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
6126{
6127 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
6128 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6129 int rc;
6130
6131 /* Image must be opened and the new flags must be valid. */
6132 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
6133 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
6134 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
6135 rc = VERR_INVALID_PARAMETER;
6136 else
6137 {
6138 /* StreamOptimized images need special treatment: reopen is prohibited. */
6139 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
6140 {
6141 if (pImage->uOpenFlags == uOpenFlags)
6142 rc = VINF_SUCCESS;
6143 else
6144 rc = VERR_INVALID_PARAMETER;
6145 }
6146 else
6147 {
6148 /* Implement this operation via reopening the image. */
6149 vmdkFreeImage(pImage, false);
6150 rc = vmdkOpenImage(pImage, uOpenFlags);
6151 }
6152 }
6153
6154 LogFlowFunc(("returns %Rrc\n", rc));
6155 return rc;
6156}
6157
6158/** @copydoc VDIMAGEBACKEND::pfnGetComment */
6159static DECLCALLBACK(int) vmdkGetComment(void *pBackendData, char *pszComment, size_t cbComment)
6160{
6161 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
6162 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6163
6164 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6165
6166 char *pszCommentEncoded = NULL;
6167 int rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
6168 "ddb.comment", &pszCommentEncoded);
6169 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
6170 {
6171 pszCommentEncoded = NULL;
6172 rc = VINF_SUCCESS;
6173 }
6174
6175 if (RT_SUCCESS(rc))
6176 {
6177 if (pszComment && pszCommentEncoded)
6178 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
6179 else if (pszComment)
6180 *pszComment = '\0';
6181
6182 if (pszCommentEncoded)
6183 RTMemTmpFree(pszCommentEncoded);
6184 }
6185
6186 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
6187 return rc;
6188}
6189
6190/** @copydoc VDIMAGEBACKEND::pfnSetComment */
6191static DECLCALLBACK(int) vmdkSetComment(void *pBackendData, const char *pszComment)
6192{
6193 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
6194 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6195 int rc;
6196
6197 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6198
6199 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6200 {
6201 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6202 rc = vmdkSetImageComment(pImage, pszComment);
6203 else
6204 rc = VERR_NOT_SUPPORTED;
6205 }
6206 else
6207 rc = VERR_VD_IMAGE_READ_ONLY;
6208
6209 LogFlowFunc(("returns %Rrc\n", rc));
6210 return rc;
6211}
6212
6213/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
6214static DECLCALLBACK(int) vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
6215{
6216 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
6217 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6218
6219 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6220
6221 *pUuid = pImage->ImageUuid;
6222
6223 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
6224 return VINF_SUCCESS;
6225}
6226
6227/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
6228static DECLCALLBACK(int) vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
6229{
6230 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
6231 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6232 int rc = VINF_SUCCESS;
6233
6234 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6235
6236 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6237 {
6238 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6239 {
6240 pImage->ImageUuid = *pUuid;
6241 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
6242 VMDK_DDB_IMAGE_UUID, pUuid);
6243 if (RT_FAILURE(rc))
6244 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
6245 N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
6246 }
6247 else
6248 rc = VERR_NOT_SUPPORTED;
6249 }
6250 else
6251 rc = VERR_VD_IMAGE_READ_ONLY;
6252
6253 LogFlowFunc(("returns %Rrc\n", rc));
6254 return rc;
6255}
6256
6257/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
6258static DECLCALLBACK(int) vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
6259{
6260 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
6261 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6262
6263 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6264
6265 *pUuid = pImage->ModificationUuid;
6266
6267 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
6268 return VINF_SUCCESS;
6269}
6270
6271/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
6272static DECLCALLBACK(int) vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
6273{
6274 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
6275 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6276 int rc = VINF_SUCCESS;
6277
6278 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6279
6280 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6281 {
6282 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6283 {
6284 /* Only touch the modification uuid if it changed. */
6285 if (RTUuidCompare(&pImage->ModificationUuid, pUuid))
6286 {
6287 pImage->ModificationUuid = *pUuid;
6288 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
6289 VMDK_DDB_MODIFICATION_UUID, pUuid);
6290 if (RT_FAILURE(rc))
6291 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
6292 }
6293 }
6294 else
6295 rc = VERR_NOT_SUPPORTED;
6296 }
6297 else
6298 rc = VERR_VD_IMAGE_READ_ONLY;
6299
6300 LogFlowFunc(("returns %Rrc\n", rc));
6301 return rc;
6302}
6303
6304/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
6305static DECLCALLBACK(int) vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
6306{
6307 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
6308 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6309
6310 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6311
6312 *pUuid = pImage->ParentUuid;
6313
6314 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
6315 return VINF_SUCCESS;
6316}
6317
6318/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
6319static DECLCALLBACK(int) vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
6320{
6321 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
6322 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6323 int rc = VINF_SUCCESS;
6324
6325 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6326
6327 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6328 {
6329 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6330 {
6331 pImage->ParentUuid = *pUuid;
6332 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
6333 VMDK_DDB_PARENT_UUID, pUuid);
6334 if (RT_FAILURE(rc))
6335 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
6336 N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
6337 }
6338 else
6339 rc = VERR_NOT_SUPPORTED;
6340 }
6341 else
6342 rc = VERR_VD_IMAGE_READ_ONLY;
6343
6344 LogFlowFunc(("returns %Rrc\n", rc));
6345 return rc;
6346}
6347
6348/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
6349static DECLCALLBACK(int) vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
6350{
6351 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
6352 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6353
6354 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6355
6356 *pUuid = pImage->ParentModificationUuid;
6357
6358 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
6359 return VINF_SUCCESS;
6360}
6361
6362/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
6363static DECLCALLBACK(int) vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
6364{
6365 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
6366 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6367 int rc = VINF_SUCCESS;
6368
6369 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
6370
6371 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6372 {
6373 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6374 {
6375 pImage->ParentModificationUuid = *pUuid;
6376 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
6377 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
6378 if (RT_FAILURE(rc))
6379 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
6380 }
6381 else
6382 rc = VERR_NOT_SUPPORTED;
6383 }
6384 else
6385 rc = VERR_VD_IMAGE_READ_ONLY;
6386
6387 LogFlowFunc(("returns %Rrc\n", rc));
6388 return rc;
6389}
6390
6391/** @copydoc VDIMAGEBACKEND::pfnDump */
6392static DECLCALLBACK(void) vmdkDump(void *pBackendData)
6393{
6394 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6395
6396 AssertPtrReturnVoid(pImage);
6397 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
6398 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
6399 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
6400 VMDK_BYTE2SECTOR(pImage->cbSize));
6401 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
6402 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
6403 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
6404 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
6405}
6406
6407
6408
6409const VDIMAGEBACKEND g_VmdkBackend =
6410{
6411 /* u32Version */
6412 VD_IMGBACKEND_VERSION,
6413 /* pszBackendName */
6414 "VMDK",
6415 /* uBackendCaps */
6416 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
6417 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC
6418 | VD_CAP_VFS | VD_CAP_PREFERRED,
6419 /* paFileExtensions */
6420 s_aVmdkFileExtensions,
6421 /* paConfigInfo */
6422 NULL,
6423 /* pfnProbe */
6424 vmdkProbe,
6425 /* pfnOpen */
6426 vmdkOpen,
6427 /* pfnCreate */
6428 vmdkCreate,
6429 /* pfnRename */
6430 vmdkRename,
6431 /* pfnClose */
6432 vmdkClose,
6433 /* pfnRead */
6434 vmdkRead,
6435 /* pfnWrite */
6436 vmdkWrite,
6437 /* pfnFlush */
6438 vmdkFlush,
6439 /* pfnDiscard */
6440 NULL,
6441 /* pfnGetVersion */
6442 vmdkGetVersion,
6443 /* pfnGetFileSize */
6444 vmdkGetFileSize,
6445 /* pfnGetPCHSGeometry */
6446 vmdkGetPCHSGeometry,
6447 /* pfnSetPCHSGeometry */
6448 vmdkSetPCHSGeometry,
6449 /* pfnGetLCHSGeometry */
6450 vmdkGetLCHSGeometry,
6451 /* pfnSetLCHSGeometry */
6452 vmdkSetLCHSGeometry,
6453 /* pfnQueryRegions */
6454 vmdkQueryRegions,
6455 /* pfnRegionListRelease */
6456 vmdkRegionListRelease,
6457 /* pfnGetImageFlags */
6458 vmdkGetImageFlags,
6459 /* pfnGetOpenFlags */
6460 vmdkGetOpenFlags,
6461 /* pfnSetOpenFlags */
6462 vmdkSetOpenFlags,
6463 /* pfnGetComment */
6464 vmdkGetComment,
6465 /* pfnSetComment */
6466 vmdkSetComment,
6467 /* pfnGetUuid */
6468 vmdkGetUuid,
6469 /* pfnSetUuid */
6470 vmdkSetUuid,
6471 /* pfnGetModificationUuid */
6472 vmdkGetModificationUuid,
6473 /* pfnSetModificationUuid */
6474 vmdkSetModificationUuid,
6475 /* pfnGetParentUuid */
6476 vmdkGetParentUuid,
6477 /* pfnSetParentUuid */
6478 vmdkSetParentUuid,
6479 /* pfnGetParentModificationUuid */
6480 vmdkGetParentModificationUuid,
6481 /* pfnSetParentModificationUuid */
6482 vmdkSetParentModificationUuid,
6483 /* pfnDump */
6484 vmdkDump,
6485 /* pfnGetTimestamp */
6486 NULL,
6487 /* pfnGetParentTimestamp */
6488 NULL,
6489 /* pfnSetParentTimestamp */
6490 NULL,
6491 /* pfnGetParentFilename */
6492 NULL,
6493 /* pfnSetParentFilename */
6494 NULL,
6495 /* pfnComposeLocation */
6496 genericFileComposeLocation,
6497 /* pfnComposeName */
6498 genericFileComposeName,
6499 /* pfnCompact */
6500 NULL,
6501 /* pfnResize */
6502 NULL,
6503 /* pfnRepair */
6504 NULL,
6505 /* pfnTraverseMetadata */
6506 NULL,
6507 /* u32VersionEnd */
6508 VD_IMGBACKEND_VERSION
6509};
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