VirtualBox

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

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

Storage/VMDK: implement VMDK 1.1 streamOptimized format. read only at the moment.

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