VirtualBox

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

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

Storage/VBoxHDD: eliminate the obsolete "-new" part of the name.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 201.1 KB
Line 
1/* $Id: VmdkHDDCore.cpp 16873 2009-02-17 19:07:37Z 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-Internal.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 /* Determine PCHS geometry if not set. */
2879 if (pImage->PCHSGeometry.cCylinders == 0)
2880 {
2881 uint64_t cCylinders = VMDK_BYTE2SECTOR(pImage->cbSize)
2882 / pImage->PCHSGeometry.cHeads
2883 / pImage->PCHSGeometry.cSectors;
2884 pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383);
2885 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2886 {
2887 rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry);
2888 AssertRC(rc);
2889 }
2890 }
2891
2892 /* Update the image metadata now in case has changed. */
2893 rc = vmdkFlushImage(pImage);
2894 if (RT_FAILURE(rc))
2895 goto out;
2896
2897 /* Figure out a few per-image constants from the extents. */
2898 pImage->cbSize = 0;
2899 for (unsigned i = 0; i < pImage->cExtents; i++)
2900 {
2901 pExtent = &pImage->pExtents[i];
2902 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
2903#ifdef VBOX_WITH_VMDK_ESX
2904 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
2905#endif /* VBOX_WITH_VMDK_ESX */
2906 )
2907 {
2908 /* Here used to be a check whether the nominal size of an extent
2909 * is a multiple of the grain size. The spec says that this is
2910 * always the case, but unfortunately some files out there in the
2911 * wild violate the spec (e.g. ReactOS 0.3.1). */
2912 }
2913 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
2914 }
2915
2916 pImage->enmImageType = VD_IMAGE_TYPE_NORMAL;
2917 for (unsigned i = 0; i < pImage->cExtents; i++)
2918 {
2919 pExtent = &pImage->pExtents[i];
2920 if ( pImage->pExtents[i].enmType == VMDKETYPE_FLAT
2921 || pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
2922 {
2923 pImage->enmImageType = VD_IMAGE_TYPE_FIXED;
2924 break;
2925 }
2926 }
2927
2928 rc = vmdkAllocateGrainTableCache(pImage);
2929 if (RT_FAILURE(rc))
2930 goto out;
2931
2932out:
2933 if (RT_FAILURE(rc))
2934 vmdkFreeImage(pImage, false);
2935 return rc;
2936}
2937
2938/**
2939 * Internal: create VMDK images for raw disk/partition access.
2940 */
2941static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVBOXHDDRAW pRaw,
2942 uint64_t cbSize)
2943{
2944 int rc = VINF_SUCCESS;
2945 PVMDKEXTENT pExtent;
2946
2947 if (pRaw->fRawDisk)
2948 {
2949 /* Full raw disk access. This requires setting up a descriptor
2950 * file and open the (flat) raw disk. */
2951 rc = vmdkCreateExtents(pImage, 1);
2952 if (RT_FAILURE(rc))
2953 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
2954 pExtent = &pImage->pExtents[0];
2955 /* Create raw disk descriptor file. */
2956 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
2957 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED,
2958 false);
2959 if (RT_FAILURE(rc))
2960 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
2961
2962 /* Set up basename for extent description. Cannot use StrDup. */
2963 size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
2964 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
2965 if (!pszBasename)
2966 return VERR_NO_MEMORY;
2967 memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
2968 pExtent->pszBasename = pszBasename;
2969 /* For raw disks the full name is identical to the base name. */
2970 pExtent->pszFullname = RTStrDup(pszBasename);
2971 if (!pExtent->pszFullname)
2972 return VERR_NO_MEMORY;
2973 pExtent->enmType = VMDKETYPE_FLAT;
2974 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
2975 pExtent->uSectorOffset = 0;
2976 pExtent->enmAccess = VMDKACCESS_READWRITE;
2977 pExtent->fMetaDirty = false;
2978
2979 /* Open flat image, the raw disk. */
2980 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
2981 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, false);
2982 if (RT_FAILURE(rc))
2983 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
2984 }
2985 else
2986 {
2987 /* Raw partition access. This requires setting up a descriptor
2988 * file, write the partition information to a flat extent and
2989 * open all the (flat) raw disk partitions. */
2990
2991 /* First pass over the partitions to determine how many
2992 * extents we need. One partition can require up to 4 extents.
2993 * One to skip over unpartitioned space, one for the
2994 * partitioning data, one to skip over unpartitioned space
2995 * and one for the partition data. */
2996 unsigned cExtents = 0;
2997 uint64_t uStart = 0;
2998 for (unsigned i = 0; i < pRaw->cPartitions; i++)
2999 {
3000 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i];
3001 if (pPart->cbPartitionData)
3002 {
3003 if (uStart > pPart->uPartitionDataStart)
3004 return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partitioning information in '%s'"), pImage->pszFilename);
3005 else if (uStart != pPart->uPartitionDataStart)
3006 cExtents++;
3007 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData;
3008 cExtents++;
3009 }
3010 if (pPart->cbPartition)
3011 {
3012 if (uStart > pPart->uPartitionStart)
3013 return vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partition data in '%s'"), pImage->pszFilename);
3014 else if (uStart != pPart->uPartitionStart)
3015 cExtents++;
3016 uStart = pPart->uPartitionStart + pPart->cbPartition;
3017 cExtents++;
3018 }
3019 }
3020 /* Another extent for filling up the rest of the image. */
3021 if (uStart != cbSize)
3022 cExtents++;
3023
3024 rc = vmdkCreateExtents(pImage, cExtents);
3025 if (RT_FAILURE(rc))
3026 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3027
3028 /* Create raw partition descriptor file. */
3029 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
3030 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED,
3031 false);
3032 if (RT_FAILURE(rc))
3033 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
3034
3035 /* Create base filename for the partition table extent. */
3036 /** @todo remove fixed buffer without creating memory leaks. */
3037 char pszPartition[1024];
3038 const char *pszBase = RTPathFilename(pImage->pszFilename);
3039 const char *pszExt = RTPathExt(pszBase);
3040 if (pszExt == NULL)
3041 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename);
3042 char *pszBaseBase = RTStrDup(pszBase);
3043 if (!pszBaseBase)
3044 return VERR_NO_MEMORY;
3045 RTPathStripExt(pszBaseBase);
3046 RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s",
3047 pszBaseBase, pszExt);
3048 RTStrFree(pszBaseBase);
3049
3050 /* Second pass over the partitions, now define all extents. */
3051 uint64_t uPartOffset = 0;
3052 cExtents = 0;
3053 uStart = 0;
3054 for (unsigned i = 0; i < pRaw->cPartitions; i++)
3055 {
3056 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i];
3057 if (pPart->cbPartitionData)
3058 {
3059 if (uStart != pPart->uPartitionDataStart)
3060 {
3061 pExtent = &pImage->pExtents[cExtents++];
3062 pExtent->pszBasename = NULL;
3063 pExtent->pszFullname = NULL;
3064 pExtent->enmType = VMDKETYPE_ZERO;
3065 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionDataStart - uStart);
3066 pExtent->uSectorOffset = 0;
3067 pExtent->enmAccess = VMDKACCESS_READWRITE;
3068 pExtent->fMetaDirty = false;
3069 }
3070 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData;
3071 pExtent = &pImage->pExtents[cExtents++];
3072 /* Set up basename for extent description. Can't use StrDup. */
3073 size_t cbBasename = strlen(pszPartition) + 1;
3074 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
3075 if (!pszBasename)
3076 return VERR_NO_MEMORY;
3077 memcpy(pszBasename, pszPartition, cbBasename);
3078 pExtent->pszBasename = pszBasename;
3079
3080 /* Set up full name for partition extent. */
3081 size_t cbDirname;
3082 char *pszDirname = RTStrDup(pImage->pszFilename);
3083 if (!pszDirname)
3084 return VERR_NO_MEMORY;
3085 RTPathStripFilename(pszDirname);
3086 cbDirname = strlen(pszDirname);
3087 char *pszFullname;
3088 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname,
3089 RTPATH_SLASH, pExtent->pszBasename);
3090 RTStrFree(pszDirname);
3091 if (RT_FAILURE(rc))
3092 return rc;
3093 pExtent->pszFullname = pszFullname;
3094 pExtent->enmType = VMDKETYPE_FLAT;
3095 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartitionData);
3096 pExtent->uSectorOffset = uPartOffset;
3097 pExtent->enmAccess = VMDKACCESS_READWRITE;
3098 pExtent->fMetaDirty = false;
3099
3100 /* Create partition table flat image. */
3101 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3102 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED,
3103 false);
3104 if (RT_FAILURE(rc))
3105 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
3106 rc = vmdkFileWriteAt(pExtent->pFile,
3107 VMDK_SECTOR2BYTE(uPartOffset),
3108 pPart->pvPartitionData,
3109 pPart->cbPartitionData, NULL);
3110 if (RT_FAILURE(rc))
3111 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
3112 uPartOffset += VMDK_BYTE2SECTOR(pPart->cbPartitionData);
3113 }
3114 if (pPart->cbPartition)
3115 {
3116 if (uStart != pPart->uPartitionStart)
3117 {
3118 pExtent = &pImage->pExtents[cExtents++];
3119 pExtent->pszBasename = NULL;
3120 pExtent->pszFullname = NULL;
3121 pExtent->enmType = VMDKETYPE_ZERO;
3122 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionStart - uStart);
3123 pExtent->uSectorOffset = 0;
3124 pExtent->enmAccess = VMDKACCESS_READWRITE;
3125 pExtent->fMetaDirty = false;
3126 }
3127 uStart = pPart->uPartitionStart + pPart->cbPartition;
3128 pExtent = &pImage->pExtents[cExtents++];
3129 if (pPart->pszRawDevice)
3130 {
3131 /* Set up basename for extent descr. Can't use StrDup. */
3132 size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
3133 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
3134 if (!pszBasename)
3135 return VERR_NO_MEMORY;
3136 memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
3137 pExtent->pszBasename = pszBasename;
3138 /* For raw disks full name is identical to base name. */
3139 pExtent->pszFullname = RTStrDup(pszBasename);
3140 if (!pExtent->pszFullname)
3141 return VERR_NO_MEMORY;
3142 pExtent->enmType = VMDKETYPE_FLAT;
3143 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition);
3144 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uPartitionStartOffset);
3145 pExtent->enmAccess = VMDKACCESS_READWRITE;
3146 pExtent->fMetaDirty = false;
3147
3148 /* Open flat image, the raw partition. */
3149 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3150 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE,
3151 false);
3152 if (RT_FAILURE(rc))
3153 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
3154 }
3155 else
3156 {
3157 pExtent->pszBasename = NULL;
3158 pExtent->pszFullname = NULL;
3159 pExtent->enmType = VMDKETYPE_ZERO;
3160 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition);
3161 pExtent->uSectorOffset = 0;
3162 pExtent->enmAccess = VMDKACCESS_READWRITE;
3163 pExtent->fMetaDirty = false;
3164 }
3165 }
3166 }
3167 /* Another extent for filling up the rest of the image. */
3168 if (uStart != cbSize)
3169 {
3170 pExtent = &pImage->pExtents[cExtents++];
3171 pExtent->pszBasename = NULL;
3172 pExtent->pszFullname = NULL;
3173 pExtent->enmType = VMDKETYPE_ZERO;
3174 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
3175 pExtent->uSectorOffset = 0;
3176 pExtent->enmAccess = VMDKACCESS_READWRITE;
3177 pExtent->fMetaDirty = false;
3178 }
3179 }
3180
3181 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
3182 pRaw->fRawDisk ?
3183 "fullDevice" : "partitionedDevice");
3184 if (RT_FAILURE(rc))
3185 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
3186 return rc;
3187}
3188
3189/**
3190 * Internal: create a regular (i.e. file-backed) VMDK image.
3191 */
3192static int vmdkCreateRegularImage(PVMDKIMAGE pImage, VDIMAGETYPE enmType,
3193 uint64_t cbSize, unsigned uImageFlags,
3194 PFNVMPROGRESS pfnProgress, void *pvUser,
3195 unsigned uPercentStart, unsigned uPercentSpan)
3196{
3197 int rc = VINF_SUCCESS;
3198 unsigned cExtents = 1;
3199 uint64_t cbOffset = 0;
3200 uint64_t cbRemaining = cbSize;
3201
3202 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
3203 {
3204 cExtents = cbSize / VMDK_2G_SPLIT_SIZE;
3205 /* Do proper extent computation: need one smaller extent if the total
3206 * size isn't evenly divisible by the split size. */
3207 if (cbSize % VMDK_2G_SPLIT_SIZE)
3208 cExtents++;
3209 }
3210 rc = vmdkCreateExtents(pImage, cExtents);
3211 if (RT_FAILURE(rc))
3212 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
3213
3214 /* Basename strings needed for constructing the extent names. */
3215 char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
3216 AssertPtr(pszBasenameSubstr);
3217 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
3218
3219 /* Create searate descriptor file if necessary. */
3220 if (cExtents != 1 || enmType == VD_IMAGE_TYPE_FIXED)
3221 {
3222 rc = vmdkFileOpen(pImage, &pImage->pFile, pImage->pszFilename,
3223 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED,
3224 false);
3225 if (RT_FAILURE(rc))
3226 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
3227 }
3228 else
3229 pImage->pFile = NULL;
3230
3231 /* Set up all extents. */
3232 for (unsigned i = 0; i < cExtents; i++)
3233 {
3234 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3235 uint64_t cbExtent = cbRemaining;
3236
3237 /* Set up fullname/basename for extent description. Cannot use StrDup
3238 * for basename, as it is not guaranteed that the memory can be freed
3239 * with RTMemTmpFree, which must be used as in other code paths
3240 * StrDup is not usable. */
3241 if (cExtents == 1 && enmType != VD_IMAGE_TYPE_FIXED)
3242 {
3243 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
3244 if (!pszBasename)
3245 return VERR_NO_MEMORY;
3246 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
3247 pExtent->pszBasename = pszBasename;
3248 }
3249 else
3250 {
3251 char *pszBasenameExt = RTPathExt(pszBasenameSubstr);
3252 char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
3253 RTPathStripExt(pszBasenameBase);
3254 char *pszTmp;
3255 size_t cbTmp;
3256 if (enmType == VD_IMAGE_TYPE_FIXED)
3257 {
3258 if (cExtents == 1)
3259 rc = RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase,
3260 pszBasenameExt);
3261 else
3262 rc = RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
3263 i+1, pszBasenameExt);
3264 }
3265 else
3266 rc = RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1,
3267 pszBasenameExt);
3268 RTStrFree(pszBasenameBase);
3269 if (RT_FAILURE(rc))
3270 return rc;
3271 cbTmp = strlen(pszTmp) + 1;
3272 char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
3273 if (!pszBasename)
3274 return VERR_NO_MEMORY;
3275 memcpy(pszBasename, pszTmp, cbTmp);
3276 RTStrFree(pszTmp);
3277 pExtent->pszBasename = pszBasename;
3278 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
3279 cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE);
3280 }
3281 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
3282 RTPathStripFilename(pszBasedirectory);
3283 char *pszFullname;
3284 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszBasedirectory,
3285 RTPATH_SLASH, pExtent->pszBasename);
3286 RTStrFree(pszBasedirectory);
3287 if (RT_FAILURE(rc))
3288 return rc;
3289 pExtent->pszFullname = pszFullname;
3290
3291 /* Create file for extent. */
3292 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszFullname,
3293 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE | RTFILE_O_NOT_CONTENT_INDEXED,
3294 false);
3295 if (RT_FAILURE(rc))
3296 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
3297 if (enmType == VD_IMAGE_TYPE_FIXED)
3298 {
3299 rc = vmdkFileSetSize(pExtent->pFile, cbExtent);
3300 if (RT_FAILURE(rc))
3301 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
3302
3303 /* Fill image with zeroes. We do this for every fixed-size image since on some systems
3304 * (for example Windows Vista), it takes ages to write a block near the end of a sparse
3305 * file and the guest could complain about an ATA timeout. */
3306
3307 /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
3308 * Currently supported file systems are ext4 and ocfs2. */
3309
3310 /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
3311 const size_t cbBuf = 128 * _1K;
3312 void *pvBuf = RTMemTmpAllocZ(cbBuf);
3313 if (!pvBuf)
3314 return VERR_NO_MEMORY;
3315
3316 uint64_t uOff = 0;
3317 /* Write data to all image blocks. */
3318 while (uOff < cbExtent)
3319 {
3320 unsigned cbChunk = (unsigned)RT_MIN(cbExtent, cbBuf);
3321
3322 rc = vmdkFileWriteAt(pExtent->pFile, uOff, pvBuf, cbChunk, NULL);
3323 if (RT_FAILURE(rc))
3324 {
3325 RTMemFree(pvBuf);
3326 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: writing block failed for '%s'"), pImage->pszFilename);
3327 }
3328
3329 uOff += cbChunk;
3330
3331 if (pfnProgress)
3332 {
3333 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
3334 uPercentStart + uOff * uPercentSpan / cbExtent,
3335 pvUser);
3336 if (RT_FAILURE(rc))
3337 {
3338 RTMemFree(pvBuf);
3339 return rc;
3340 }
3341 }
3342 }
3343 RTMemTmpFree(pvBuf);
3344 }
3345
3346 /* Place descriptor file information (where integrated). */
3347 if (cExtents == 1 && enmType != VD_IMAGE_TYPE_FIXED)
3348 {
3349 pExtent->uDescriptorSector = 1;
3350 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
3351 /* The descriptor is part of the (only) extent. */
3352 pExtent->pDescData = pImage->pDescData;
3353 pImage->pDescData = NULL;
3354 }
3355
3356 if (enmType == VD_IMAGE_TYPE_NORMAL)
3357 {
3358 uint64_t cSectorsPerGDE, cSectorsPerGD;
3359 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
3360 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, 65536));
3361 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(65536);
3362 pExtent->cGTEntries = 512;
3363 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
3364 pExtent->cSectorsPerGDE = cSectorsPerGDE;
3365 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
3366 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
3367 }
3368 else
3369 pExtent->enmType = VMDKETYPE_FLAT;
3370
3371 pExtent->enmAccess = VMDKACCESS_READWRITE;
3372 pExtent->fUncleanShutdown = true;
3373 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent);
3374 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(cbOffset);
3375 pExtent->fMetaDirty = true;
3376
3377 if (enmType == VD_IMAGE_TYPE_NORMAL)
3378 {
3379 rc = vmdkCreateGrainDirectory(pExtent,
3380 RT_MAX( pExtent->uDescriptorSector
3381 + pExtent->cDescriptorSectors,
3382 1),
3383 true);
3384 if (RT_FAILURE(rc))
3385 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
3386 }
3387
3388 if (RT_SUCCESS(rc) && pfnProgress)
3389 pfnProgress(NULL /* WARNING! pVM=NULL */,
3390 uPercentStart + i * uPercentSpan / cExtents,
3391 pvUser);
3392
3393 cbRemaining -= cbExtent;
3394 cbOffset += cbExtent;
3395 }
3396
3397 const char *pszDescType = NULL;
3398 if (enmType == VD_IMAGE_TYPE_FIXED)
3399 {
3400 pszDescType = (cExtents == 1)
3401 ? "monolithicFlat" : "twoGbMaxExtentFlat";
3402 }
3403 else if (enmType == VD_IMAGE_TYPE_NORMAL)
3404 {
3405 pszDescType = (cExtents == 1)
3406 ? "monolithicSparse" : "twoGbMaxExtentSparse";
3407 }
3408 else
3409 AssertMsgFailed(("invalid image type %d\n", enmType));
3410 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
3411 pszDescType);
3412 if (RT_FAILURE(rc))
3413 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
3414 return rc;
3415}
3416
3417/**
3418 * Internal: The actual code for creating any VMDK variant currently in
3419 * existence on hosted environments.
3420 */
3421static int vmdkCreateImage(PVMDKIMAGE pImage, VDIMAGETYPE enmType,
3422 uint64_t cbSize, unsigned uImageFlags,
3423 const char *pszComment,
3424 PCPDMMEDIAGEOMETRY pPCHSGeometry,
3425 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
3426 PFNVMPROGRESS pfnProgress, void *pvUser,
3427 unsigned uPercentStart, unsigned uPercentSpan)
3428{
3429 int rc;
3430
3431 pImage->uImageFlags = uImageFlags;
3432
3433 /* Try to get error interface. */
3434 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
3435 if (pImage->pInterfaceError)
3436 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
3437
3438 /* Try to get async I/O interface. */
3439 pImage->pInterfaceAsyncIO = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ASYNCIO);
3440 if (pImage->pInterfaceAsyncIO)
3441 pImage->pInterfaceAsyncIOCallbacks = VDGetInterfaceAsyncIO(pImage->pInterfaceAsyncIO);
3442
3443 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,
3444 &pImage->Descriptor);
3445 if (RT_FAILURE(rc))
3446 {
3447 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);
3448 goto out;
3449 }
3450
3451 if ( enmType == VD_IMAGE_TYPE_FIXED
3452 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
3453 {
3454 /* Raw disk image (includes raw partition). */
3455 const PVBOXHDDRAW pRaw = (const PVBOXHDDRAW)pszComment;
3456 /* As the comment is misused, zap it so that no garbage comment
3457 * is set below. */
3458 pszComment = NULL;
3459 rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
3460 }
3461 else if ( enmType == VD_IMAGE_TYPE_FIXED
3462 || enmType == VD_IMAGE_TYPE_NORMAL)
3463 {
3464 /* Regular fixed or sparse image (monolithic or split). */
3465 rc = vmdkCreateRegularImage(pImage, enmType, cbSize, uImageFlags,
3466 pfnProgress, pvUser, uPercentStart,
3467 uPercentSpan * 95 / 100);
3468 }
3469 else
3470 {
3471 /* Unknown/invalid image type. */
3472 rc = VERR_NOT_IMPLEMENTED;
3473 }
3474
3475 if (RT_FAILURE(rc))
3476 goto out;
3477
3478 if (RT_SUCCESS(rc) && pfnProgress)
3479 pfnProgress(NULL /* WARNING! pVM=NULL */,
3480 uPercentStart + uPercentSpan * 98 / 100, pvUser);
3481
3482 pImage->enmImageType = enmType;
3483 pImage->cbSize = cbSize;
3484
3485 for (unsigned i = 0; i < pImage->cExtents; i++)
3486 {
3487 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3488
3489 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
3490 pExtent->cNominalSectors, pExtent->enmType,
3491 pExtent->pszBasename, pExtent->uSectorOffset);
3492 if (RT_FAILURE(rc))
3493 {
3494 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
3495 goto out;
3496 }
3497 }
3498 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
3499
3500 if ( pPCHSGeometry->cCylinders != 0
3501 && pPCHSGeometry->cHeads != 0
3502 && pPCHSGeometry->cSectors != 0)
3503 {
3504 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
3505 if (RT_FAILURE(rc))
3506 goto out;
3507 }
3508 if ( pLCHSGeometry->cCylinders != 0
3509 && pLCHSGeometry->cHeads != 0
3510 && pLCHSGeometry->cSectors != 0)
3511 {
3512 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
3513 if (RT_FAILURE(rc))
3514 goto out;
3515 }
3516
3517 pImage->LCHSGeometry = *pLCHSGeometry;
3518 pImage->PCHSGeometry = *pPCHSGeometry;
3519
3520 pImage->ImageUuid = *pUuid;
3521 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3522 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
3523 if (RT_FAILURE(rc))
3524 {
3525 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);
3526 goto out;
3527 }
3528 RTUuidClear(&pImage->ParentUuid);
3529 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3530 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
3531 if (RT_FAILURE(rc))
3532 {
3533 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);
3534 goto out;
3535 }
3536 RTUuidClear(&pImage->ModificationUuid);
3537 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3538 VMDK_DDB_MODIFICATION_UUID,
3539 &pImage->ModificationUuid);
3540 if (RT_FAILURE(rc))
3541 {
3542 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);
3543 goto out;
3544 }
3545 RTUuidClear(&pImage->ParentModificationUuid);
3546 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3547 VMDK_DDB_PARENT_MODIFICATION_UUID,
3548 &pImage->ParentModificationUuid);
3549 if (RT_FAILURE(rc))
3550 {
3551 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);
3552 goto out;
3553 }
3554
3555 rc = vmdkAllocateGrainTableCache(pImage);
3556 if (RT_FAILURE(rc))
3557 goto out;
3558
3559 rc = vmdkSetImageComment(pImage, pszComment);
3560 if (RT_FAILURE(rc))
3561 {
3562 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);
3563 goto out;
3564 }
3565
3566 if (RT_SUCCESS(rc) && pfnProgress)
3567 pfnProgress(NULL /* WARNING! pVM=NULL */,
3568 uPercentStart + uPercentSpan * 99 / 100, pvUser);
3569
3570 rc = vmdkFlushImage(pImage);
3571
3572out:
3573 if (RT_SUCCESS(rc) && pfnProgress)
3574 pfnProgress(NULL /* WARNING! pVM=NULL */,
3575 uPercentStart + uPercentSpan, pvUser);
3576
3577 if (RT_FAILURE(rc))
3578 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
3579 return rc;
3580}
3581
3582/**
3583 * Internal: Update image comment.
3584 */
3585static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
3586{
3587 char *pszCommentEncoded;
3588 if (pszComment)
3589 {
3590 pszCommentEncoded = vmdkEncodeString(pszComment);
3591 if (!pszCommentEncoded)
3592 return VERR_NO_MEMORY;
3593 }
3594 else
3595 pszCommentEncoded = NULL;
3596 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
3597 "ddb.comment", pszCommentEncoded);
3598 if (pszComment)
3599 RTStrFree(pszCommentEncoded);
3600 if (RT_FAILURE(rc))
3601 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
3602 return VINF_SUCCESS;
3603}
3604
3605/**
3606 * Internal. Free all allocated space for representing an image, and optionally
3607 * delete the image from disk.
3608 */
3609static void vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
3610{
3611 AssertPtr(pImage);
3612
3613 if (pImage->enmImageType)
3614 {
3615 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3616 {
3617 /* Mark all extents as clean. */
3618 for (unsigned i = 0; i < pImage->cExtents; i++)
3619 {
3620 if (( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
3621#ifdef VBOX_WITH_VMDK_ESX
3622 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
3623#endif /* VBOX_WITH_VMDK_ESX */
3624 )
3625 && pImage->pExtents[i].fUncleanShutdown)
3626 {
3627 pImage->pExtents[i].fUncleanShutdown = false;
3628 pImage->pExtents[i].fMetaDirty = true;
3629 }
3630 }
3631 }
3632 (void)vmdkFlushImage(pImage);
3633 }
3634 if (pImage->pExtents != NULL)
3635 {
3636 for (unsigned i = 0 ; i < pImage->cExtents; i++)
3637 vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
3638 RTMemFree(pImage->pExtents);
3639 pImage->pExtents = NULL;
3640 }
3641 pImage->cExtents = 0;
3642 if (pImage->pFile != NULL)
3643 vmdkFileClose(pImage, &pImage->pFile, fDelete);
3644 vmdkFileCheckAllClose(pImage);
3645 if (pImage->pGTCache)
3646 {
3647 RTMemFree(pImage->pGTCache);
3648 pImage->pGTCache = NULL;
3649 }
3650 if (pImage->pDescData)
3651 {
3652 RTMemFree(pImage->pDescData);
3653 pImage->pDescData = NULL;
3654 }
3655}
3656
3657/**
3658 * Internal. Flush image data (and metadata) to disk.
3659 */
3660static int vmdkFlushImage(PVMDKIMAGE pImage)
3661{
3662 PVMDKEXTENT pExtent;
3663 int rc = VINF_SUCCESS;
3664
3665 /* Update descriptor if changed. */
3666 if (pImage->Descriptor.fDirty)
3667 {
3668 rc = vmdkWriteDescriptor(pImage);
3669 if (RT_FAILURE(rc))
3670 goto out;
3671 }
3672
3673 for (unsigned i = 0; i < pImage->cExtents; i++)
3674 {
3675 pExtent = &pImage->pExtents[i];
3676 if (pExtent->pFile != NULL && pExtent->fMetaDirty)
3677 {
3678 switch (pExtent->enmType)
3679 {
3680 case VMDKETYPE_HOSTED_SPARSE:
3681 rc = vmdkWriteMetaSparseExtent(pExtent);
3682 if (RT_FAILURE(rc))
3683 goto out;
3684 break;
3685#ifdef VBOX_WITH_VMDK_ESX
3686 case VMDKETYPE_ESX_SPARSE:
3687 /** @todo update the header. */
3688 break;
3689#endif /* VBOX_WITH_VMDK_ESX */
3690 case VMDKETYPE_FLAT:
3691 /* Nothing to do. */
3692 break;
3693 case VMDKETYPE_ZERO:
3694 default:
3695 AssertMsgFailed(("extent with type %d marked as dirty\n",
3696 pExtent->enmType));
3697 break;
3698 }
3699 }
3700 switch (pExtent->enmType)
3701 {
3702 case VMDKETYPE_HOSTED_SPARSE:
3703#ifdef VBOX_WITH_VMDK_ESX
3704 case VMDKETYPE_ESX_SPARSE:
3705#endif /* VBOX_WITH_VMDK_ESX */
3706 case VMDKETYPE_FLAT:
3707 /** @todo implement proper path absolute check. */
3708 if ( pExtent->pFile != NULL
3709 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3710 && !(pExtent->pszBasename[0] == RTPATH_SLASH))
3711 rc = vmdkFileFlush(pExtent->pFile);
3712 break;
3713 case VMDKETYPE_ZERO:
3714 /* No need to do anything for this extent. */
3715 break;
3716 default:
3717 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
3718 break;
3719 }
3720 }
3721
3722out:
3723 return rc;
3724}
3725
3726/**
3727 * Internal. Find extent corresponding to the sector number in the disk.
3728 */
3729static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector,
3730 PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
3731{
3732 PVMDKEXTENT pExtent = NULL;
3733 int rc = VINF_SUCCESS;
3734
3735 for (unsigned i = 0; i < pImage->cExtents; i++)
3736 {
3737 if (offSector < pImage->pExtents[i].cNominalSectors)
3738 {
3739 pExtent = &pImage->pExtents[i];
3740 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
3741 break;
3742 }
3743 offSector -= pImage->pExtents[i].cNominalSectors;
3744 }
3745
3746 if (pExtent)
3747 *ppExtent = pExtent;
3748 else
3749 rc = VERR_IO_SECTOR_NOT_FOUND;
3750
3751 return rc;
3752}
3753
3754/**
3755 * Internal. Hash function for placing the grain table hash entries.
3756 */
3757static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
3758 unsigned uExtent)
3759{
3760 /** @todo this hash function is quite simple, maybe use a better one which
3761 * scrambles the bits better. */
3762 return (uSector + uExtent) % pCache->cEntries;
3763}
3764
3765/**
3766 * Internal. Get sector number in the extent file from the relative sector
3767 * number in the extent.
3768 */
3769static int vmdkGetSector(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
3770 uint64_t uSector, uint64_t *puExtentSector)
3771{
3772 uint64_t uGDIndex, uGTSector, uGTBlock;
3773 uint32_t uGTHash, uGTBlockIndex;
3774 PVMDKGTCACHEENTRY pGTCacheEntry;
3775 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
3776 int rc;
3777
3778 uGDIndex = uSector / pExtent->cSectorsPerGDE;
3779 if (uGDIndex >= pExtent->cGDEntries)
3780 return VERR_OUT_OF_RANGE;
3781 uGTSector = pExtent->pGD[uGDIndex];
3782 if (!uGTSector)
3783 {
3784 /* There is no grain table referenced by this grain directory
3785 * entry. So there is absolutely no data in this area. */
3786 *puExtentSector = 0;
3787 return VINF_SUCCESS;
3788 }
3789
3790 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
3791 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
3792 pGTCacheEntry = &pCache->aGTCache[uGTHash];
3793 if ( pGTCacheEntry->uExtent != pExtent->uExtent
3794 || pGTCacheEntry->uGTBlock != uGTBlock)
3795 {
3796 /* Cache miss, fetch data from disk. */
3797 rc = vmdkFileReadAt(pExtent->pFile,
3798 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
3799 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3800 if (RT_FAILURE(rc))
3801 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname);
3802 pGTCacheEntry->uExtent = pExtent->uExtent;
3803 pGTCacheEntry->uGTBlock = uGTBlock;
3804 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
3805 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
3806 }
3807 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
3808 uint64_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
3809 if (uGrainSector)
3810 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
3811 else
3812 *puExtentSector = 0;
3813 return VINF_SUCCESS;
3814}
3815
3816/**
3817 * Internal. Allocates a new grain table (if necessary), writes the grain
3818 * and updates the grain table. The cache is also updated by this operation.
3819 * This is separate from vmdkGetSector, because that should be as fast as
3820 * possible. Most code from vmdkGetSector also appears here.
3821 */
3822static int vmdkAllocGrain(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
3823 uint64_t uSector, const void *pvBuf,
3824 uint64_t cbWrite)
3825{
3826 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
3827 uint64_t cbExtentSize;
3828 uint32_t uGTHash, uGTBlockIndex;
3829 PVMDKGTCACHEENTRY pGTCacheEntry;
3830 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
3831 int rc;
3832
3833 uGDIndex = uSector / pExtent->cSectorsPerGDE;
3834 if (uGDIndex >= pExtent->cGDEntries)
3835 return VERR_OUT_OF_RANGE;
3836 uGTSector = pExtent->pGD[uGDIndex];
3837 if (pExtent->pRGD)
3838 uRGTSector = pExtent->pRGD[uGDIndex];
3839 else
3840 uRGTSector = 0; /**< avoid compiler warning */
3841 if (!uGTSector)
3842 {
3843 /* There is no grain table referenced by this grain directory
3844 * entry. So there is absolutely no data in this area. Allocate
3845 * a new grain table and put the reference to it in the GDs. */
3846 rc = vmdkFileGetSize(pExtent->pFile, &cbExtentSize);
3847 if (RT_FAILURE(rc))
3848 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
3849 Assert(!(cbExtentSize % 512));
3850 cbExtentSize = RT_ALIGN_64(cbExtentSize, 512);
3851 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
3852 /* Normally the grain table is preallocated for hosted sparse extents
3853 * that support more than 32 bit sector numbers. So this shouldn't
3854 * ever happen on a valid extent. */
3855 if (uGTSector > UINT32_MAX)
3856 return VERR_VD_VMDK_INVALID_HEADER;
3857 /* Write grain table by writing the required number of grain table
3858 * cache chunks. Avoids dynamic memory allocation, but is a bit
3859 * slower. But as this is a pretty infrequently occurring case it
3860 * should be acceptable. */
3861 memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
3862 for (unsigned i = 0;
3863 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
3864 i++)
3865 {
3866 rc = vmdkFileWriteAt(pExtent->pFile,
3867 VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp),
3868 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3869 if (RT_FAILURE(rc))
3870 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
3871 }
3872 if (pExtent->pRGD)
3873 {
3874 AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
3875 rc = vmdkFileGetSize(pExtent->pFile, &cbExtentSize);
3876 if (RT_FAILURE(rc))
3877 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
3878 Assert(!(cbExtentSize % 512));
3879 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
3880 /* Normally the redundant grain table is preallocated for hosted
3881 * sparse extents that support more than 32 bit sector numbers. So
3882 * this shouldn't ever happen on a valid extent. */
3883 if (uRGTSector > UINT32_MAX)
3884 return VERR_VD_VMDK_INVALID_HEADER;
3885 /* Write backup grain table by writing the required number of grain
3886 * table cache chunks. Avoids dynamic memory allocation, but is a
3887 * bit slower. But as this is a pretty infrequently occurring case
3888 * it should be acceptable. */
3889 for (unsigned i = 0;
3890 i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
3891 i++)
3892 {
3893 rc = vmdkFileWriteAt(pExtent->pFile,
3894 VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp),
3895 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3896 if (RT_FAILURE(rc))
3897 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
3898 }
3899 }
3900
3901 /* Update the grain directory on disk (doing it before writing the
3902 * grain table will result in a garbled extent if the operation is
3903 * aborted for some reason. Otherwise the worst that can happen is
3904 * some unused sectors in the extent. */
3905 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
3906 rc = vmdkFileWriteAt(pExtent->pFile,
3907 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
3908 &uGTSectorLE, sizeof(uGTSectorLE), NULL);
3909 if (RT_FAILURE(rc))
3910 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
3911 if (pExtent->pRGD)
3912 {
3913 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
3914 rc = vmdkFileWriteAt(pExtent->pFile,
3915 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE),
3916 &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
3917 if (RT_FAILURE(rc))
3918 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
3919 }
3920
3921 /* As the final step update the in-memory copy of the GDs. */
3922 pExtent->pGD[uGDIndex] = uGTSector;
3923 if (pExtent->pRGD)
3924 pExtent->pRGD[uGDIndex] = uRGTSector;
3925 }
3926
3927 rc = vmdkFileGetSize(pExtent->pFile, &cbExtentSize);
3928 if (RT_FAILURE(rc))
3929 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
3930 Assert(!(cbExtentSize % 512));
3931
3932 /* Write the data. */
3933 rc = vmdkFileWriteAt(pExtent->pFile, cbExtentSize, pvBuf, cbWrite, NULL);
3934 if (RT_FAILURE(rc))
3935 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
3936
3937 /* Update the grain table (and the cache). */
3938 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
3939 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
3940 pGTCacheEntry = &pCache->aGTCache[uGTHash];
3941 if ( pGTCacheEntry->uExtent != pExtent->uExtent
3942 || pGTCacheEntry->uGTBlock != uGTBlock)
3943 {
3944 /* Cache miss, fetch data from disk. */
3945 rc = vmdkFileReadAt(pExtent->pFile,
3946 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
3947 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3948 if (RT_FAILURE(rc))
3949 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
3950 pGTCacheEntry->uExtent = pExtent->uExtent;
3951 pGTCacheEntry->uGTBlock = uGTBlock;
3952 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
3953 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
3954 }
3955 else
3956 {
3957 /* Cache hit. Convert grain table block back to disk format, otherwise
3958 * the code below will write garbage for all but the updated entry. */
3959 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
3960 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
3961 }
3962 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
3963 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(cbExtentSize));
3964 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(cbExtentSize);
3965 /* Update grain table on disk. */
3966 rc = vmdkFileWriteAt(pExtent->pFile,
3967 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
3968 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3969 if (RT_FAILURE(rc))
3970 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
3971 if (pExtent->pRGD)
3972 {
3973 /* Update backup grain table on disk. */
3974 rc = vmdkFileWriteAt(pExtent->pFile,
3975 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
3976 aGTDataTmp, sizeof(aGTDataTmp), NULL);
3977 if (RT_FAILURE(rc))
3978 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
3979 }
3980#ifdef VBOX_WITH_VMDK_ESX
3981 if (RT_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
3982 {
3983 pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
3984 pExtent->fMetaDirty = true;
3985 }
3986#endif /* VBOX_WITH_VMDK_ESX */
3987 return rc;
3988}
3989
3990
3991/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
3992static int vmdkCheckIfValid(const char *pszFilename)
3993{
3994 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
3995 int rc = VINF_SUCCESS;
3996 PVMDKIMAGE pImage;
3997
3998 if ( !pszFilename
3999 || !*pszFilename
4000 || strchr(pszFilename, '"'))
4001 {
4002 rc = VERR_INVALID_PARAMETER;
4003 goto out;
4004 }
4005
4006 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
4007 if (!pImage)
4008 {
4009 rc = VERR_NO_MEMORY;
4010 goto out;
4011 }
4012 pImage->pszFilename = pszFilename;
4013 pImage->pFile = NULL;
4014 pImage->pExtents = NULL;
4015 pImage->pFiles = NULL;
4016 pImage->pGTCache = NULL;
4017 pImage->pDescData = NULL;
4018 pImage->pVDIfsDisk = NULL;
4019 /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
4020 * much as possible in vmdkOpenImage. */
4021 rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
4022 vmdkFreeImage(pImage, false);
4023 RTMemFree(pImage);
4024
4025out:
4026 LogFlowFunc(("returns %Rrc\n", rc));
4027 return rc;
4028}
4029
4030/** @copydoc VBOXHDDBACKEND::pfnOpen */
4031static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
4032 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
4033 void **ppBackendData)
4034{
4035 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
4036 int rc;
4037 PVMDKIMAGE pImage;
4038
4039 /* Check open flags. All valid flags are supported. */
4040 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
4041 {
4042 rc = VERR_INVALID_PARAMETER;
4043 goto out;
4044 }
4045
4046 /* Check remaining arguments. */
4047 if ( !VALID_PTR(pszFilename)
4048 || !*pszFilename
4049 || strchr(pszFilename, '"'))
4050 {
4051 rc = VERR_INVALID_PARAMETER;
4052 goto out;
4053 }
4054
4055
4056 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
4057 if (!pImage)
4058 {
4059 rc = VERR_NO_MEMORY;
4060 goto out;
4061 }
4062 pImage->pszFilename = pszFilename;
4063 pImage->pFile = NULL;
4064 pImage->pExtents = NULL;
4065 pImage->pFiles = NULL;
4066 pImage->pGTCache = NULL;
4067 pImage->pDescData = NULL;
4068 pImage->pVDIfsDisk = pVDIfsDisk;
4069
4070 rc = vmdkOpenImage(pImage, uOpenFlags);
4071 if (RT_SUCCESS(rc))
4072 *ppBackendData = pImage;
4073
4074out:
4075 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4076 return rc;
4077}
4078
4079/** @copydoc VBOXHDDBACKEND::pfnCreate */
4080static int vmdkCreate(const char *pszFilename, VDIMAGETYPE enmType,
4081 uint64_t cbSize, unsigned uImageFlags,
4082 const char *pszComment,
4083 PCPDMMEDIAGEOMETRY pPCHSGeometry,
4084 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
4085 unsigned uOpenFlags, unsigned uPercentStart,
4086 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
4087 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation,
4088 void **ppBackendData)
4089{
4090 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));
4091 int rc;
4092 PVMDKIMAGE pImage;
4093
4094 PFNVMPROGRESS pfnProgress = NULL;
4095 void *pvUser = NULL;
4096 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
4097 VDINTERFACETYPE_PROGRESS);
4098 PVDINTERFACEPROGRESS pCbProgress = NULL;
4099 if (pIfProgress)
4100 {
4101 pCbProgress = VDGetInterfaceProgress(pIfProgress);
4102 pfnProgress = pCbProgress->pfnProgress;
4103 pvUser = pIfProgress->pvUser;
4104 }
4105
4106 /* Check open flags. All valid flags are supported. */
4107 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
4108 {
4109 rc = VERR_INVALID_PARAMETER;
4110 goto out;
4111 }
4112
4113 /* @todo A quick hack to support differencing images in VMDK. */
4114 if (enmType == VD_IMAGE_TYPE_DIFF)
4115 enmType = VD_IMAGE_TYPE_NORMAL;
4116
4117 /* Check remaining arguments. */
4118 if ( !VALID_PTR(pszFilename)
4119 || !*pszFilename
4120 || strchr(pszFilename, '"')
4121 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
4122 || !VALID_PTR(pPCHSGeometry)
4123 || !VALID_PTR(pLCHSGeometry))
4124 {
4125 rc = VERR_INVALID_PARAMETER;
4126 goto out;
4127 }
4128
4129 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
4130 if (!pImage)
4131 {
4132 rc = VERR_NO_MEMORY;
4133 goto out;
4134 }
4135 pImage->pszFilename = pszFilename;
4136 pImage->pFile = NULL;
4137 pImage->pExtents = NULL;
4138 pImage->pFiles = NULL;
4139 pImage->pGTCache = NULL;
4140 pImage->pDescData = NULL;
4141 pImage->pVDIfsDisk = NULL;
4142 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
4143 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
4144 if (!pImage->pDescData)
4145 {
4146 rc = VERR_NO_MEMORY;
4147 goto out;
4148 }
4149
4150 rc = vmdkCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment,
4151 pPCHSGeometry, pLCHSGeometry, pUuid,
4152 pfnProgress, pvUser, uPercentStart, uPercentSpan);
4153 if (RT_SUCCESS(rc))
4154 {
4155 /* So far the image is opened in read/write mode. Make sure the
4156 * image is opened in read-only mode if the caller requested that. */
4157 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
4158 {
4159 vmdkFreeImage(pImage, false);
4160 rc = vmdkOpenImage(pImage, uOpenFlags);
4161 if (RT_FAILURE(rc))
4162 goto out;
4163 }
4164 *ppBackendData = pImage;
4165 }
4166 else
4167 {
4168 RTMemFree(pImage->pDescData);
4169 RTMemFree(pImage);
4170 }
4171
4172out:
4173 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
4174 return rc;
4175}
4176
4177/**
4178 * Replaces a fragment of a string with the specified string.
4179 *
4180 * @returns Pointer to the allocated UTF-8 string.
4181 * @param pszWhere UTF-8 string to search in.
4182 * @param pszWhat UTF-8 string to search for.
4183 * @param pszByWhat UTF-8 string to replace the found string with.
4184 */
4185static char * vmdkStrReplace(const char *pszWhere, const char *pszWhat, const char *pszByWhat)
4186{
4187 AssertPtr(pszWhere);
4188 AssertPtr(pszWhat);
4189 AssertPtr(pszByWhat);
4190 const char *pszFoundStr = strstr(pszWhere, pszWhat);
4191 if (!pszFoundStr)
4192 return NULL;
4193 size_t cFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
4194 char *pszNewStr = (char *)RTMemAlloc(cFinal);
4195 if (pszNewStr)
4196 {
4197 char *pszTmp = pszNewStr;
4198 memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
4199 pszTmp += pszFoundStr - pszWhere;
4200 memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
4201 pszTmp += strlen(pszByWhat);
4202 strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
4203 }
4204 return pszNewStr;
4205}
4206
4207/** @copydoc VBOXHDDBACKEND::pfnRename */
4208static int vmdkRename(void *pBackendData, const char *pszFilename)
4209{
4210 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
4211
4212 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4213 int rc = VINF_SUCCESS;
4214 char **apszOldName = NULL;
4215 char **apszNewName = NULL;
4216 char **apszNewLines = NULL;
4217 char *pszOldDescName = NULL;
4218 bool fImageFreed = false;
4219 bool fEmbeddedDesc = false;
4220 unsigned cExtents = pImage->cExtents;
4221 char *pszNewBaseName = NULL;
4222 char *pszOldBaseName = NULL;
4223 char *pszNewFullName = NULL;
4224 char *pszOldFullName = NULL;
4225 const char *pszOldImageName;
4226 unsigned i, line;
4227 VMDKDESCRIPTOR DescriptorCopy;
4228 VMDKEXTENT ExtentCopy;
4229
4230 memset(&DescriptorCopy, 0, sizeof(DescriptorCopy));
4231
4232 /* Check arguments. */
4233 if ( !pImage
4234 || (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
4235 || !VALID_PTR(pszFilename)
4236 || !*pszFilename)
4237 {
4238 rc = VERR_INVALID_PARAMETER;
4239 goto out;
4240 }
4241
4242 /*
4243 * Allocate an array to store both old and new names of renamed files
4244 * in case we have to roll back the changes. Arrays are initialized
4245 * with zeros. We actually save stuff when and if we change it.
4246 */
4247 apszOldName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
4248 apszNewName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
4249 apszNewLines = (char **)RTMemTmpAllocZ((cExtents) * sizeof(char*));
4250 if (!apszOldName || !apszNewName || !apszNewLines)
4251 {
4252 rc = VERR_NO_MEMORY;
4253 goto out;
4254 }
4255
4256 /* Save the descriptor size and position. */
4257 if (pImage->pDescData)
4258 {
4259 /* Separate descriptor file. */
4260 fEmbeddedDesc = false;
4261 }
4262 else
4263 {
4264 /* Embedded descriptor file. */
4265 ExtentCopy = pImage->pExtents[0];
4266 fEmbeddedDesc = true;
4267 }
4268 /* Save the descriptor content. */
4269 DescriptorCopy.cLines = pImage->Descriptor.cLines;
4270 for (i = 0; i < DescriptorCopy.cLines; i++)
4271 {
4272 DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
4273 if (!DescriptorCopy.aLines[i])
4274 {
4275 rc = VERR_NO_MEMORY;
4276 goto out;
4277 }
4278 }
4279
4280 /* Prepare both old and new base names used for string replacement. */
4281 pszNewBaseName = RTStrDup(RTPathFilename(pszFilename));
4282 RTPathStripExt(pszNewBaseName);
4283 pszOldBaseName = RTStrDup(RTPathFilename(pImage->pszFilename));
4284 RTPathStripExt(pszOldBaseName);
4285 /* Prepare both old and new full names used for string replacement. */
4286 pszNewFullName = RTStrDup(pszFilename);
4287 RTPathStripExt(pszNewFullName);
4288 pszOldFullName = RTStrDup(pImage->pszFilename);
4289 RTPathStripExt(pszOldFullName);
4290
4291 /* --- Up to this point we have not done any damage yet. --- */
4292
4293 /* Save the old name for easy access to the old descriptor file. */
4294 pszOldDescName = RTStrDup(pImage->pszFilename);
4295 /* Save old image name. */
4296 pszOldImageName = pImage->pszFilename;
4297
4298 /* Update the descriptor with modified extent names. */
4299 for (i = 0, line = pImage->Descriptor.uFirstExtent;
4300 i < cExtents;
4301 i++, line = pImage->Descriptor.aNextLines[line])
4302 {
4303 /* Assume that vmdkStrReplace will fail. */
4304 rc = VERR_NO_MEMORY;
4305 /* Update the descriptor. */
4306 apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
4307 pszOldBaseName, pszNewBaseName);
4308 if (!apszNewLines[i])
4309 goto rollback;
4310 pImage->Descriptor.aLines[line] = apszNewLines[i];
4311 }
4312 /* Make sure the descriptor gets written back. */
4313 pImage->Descriptor.fDirty = true;
4314 /* Flush the descriptor now, in case it is embedded. */
4315 vmdkFlushImage(pImage);
4316
4317 /* Close and rename/move extents. */
4318 for (i = 0; i < cExtents; i++)
4319 {
4320 PVMDKEXTENT pExtent = &pImage->pExtents[i];
4321 /* Compose new name for the extent. */
4322 apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
4323 pszOldFullName, pszNewFullName);
4324 if (!apszNewName[i])
4325 goto rollback;
4326 /* Close the extent file. */
4327 vmdkFileClose(pImage, &pExtent->pFile, false);
4328 /* Rename the extent file. */
4329 rc = RTFileMove(pExtent->pszFullname, apszNewName[i], 0);
4330 if (RT_FAILURE(rc))
4331 goto rollback;
4332 /* Remember the old name. */
4333 apszOldName[i] = RTStrDup(pExtent->pszFullname);
4334 }
4335 /* Release all old stuff. */
4336 vmdkFreeImage(pImage, false);
4337
4338 fImageFreed = true;
4339
4340 /* Last elements of new/old name arrays are intended for
4341 * storing descriptor's names.
4342 */
4343 apszNewName[cExtents] = RTStrDup(pszFilename);
4344 /* Rename the descriptor file if it's separate. */
4345 if (!fEmbeddedDesc)
4346 {
4347 rc = RTFileMove(pImage->pszFilename, apszNewName[cExtents], 0);
4348 if (RT_FAILURE(rc))
4349 goto rollback;
4350 /* Save old name only if we may need to change it back. */
4351 apszOldName[cExtents] = RTStrDup(pszFilename);
4352 }
4353
4354 /* Update pImage with the new information. */
4355 pImage->pszFilename = pszFilename;
4356
4357 /* Open the new image. */
4358 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
4359 if (RT_SUCCESS(rc))
4360 goto out;
4361
4362rollback:
4363 /* Roll back all changes in case of failure. */
4364 if (RT_FAILURE(rc))
4365 {
4366 int rrc;
4367 if (!fImageFreed)
4368 {
4369 /*
4370 * Some extents may have been closed, close the rest. We will
4371 * re-open the whole thing later.
4372 */
4373 vmdkFreeImage(pImage, false);
4374 }
4375 /* Rename files back. */
4376 for (i = 0; i <= cExtents; i++)
4377 {
4378 if (apszOldName[i])
4379 {
4380 rrc = RTFileMove(apszNewName[i], apszOldName[i], 0);
4381 AssertRC(rrc);
4382 }
4383 }
4384 /* Restore the old descriptor. */
4385 PVMDKFILE pFile;
4386 rrc = vmdkFileOpen(pImage, &pFile, pszOldDescName,
4387 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, false);
4388 AssertRC(rrc);
4389 if (fEmbeddedDesc)
4390 {
4391 ExtentCopy.pFile = pFile;
4392 pImage->pExtents = &ExtentCopy;
4393 }
4394 else
4395 {
4396 /* Shouldn't be null for separate descriptor.
4397 * There will be no access to the actual content.
4398 */
4399 pImage->pDescData = pszOldDescName;
4400 pImage->pFile = pFile;
4401 }
4402 pImage->Descriptor = DescriptorCopy;
4403 vmdkWriteDescriptor(pImage);
4404 vmdkFileClose(pImage, &pFile, false);
4405 /* Get rid of the stuff we implanted. */
4406 pImage->pExtents = NULL;
4407 pImage->pFile = NULL;
4408 pImage->pDescData = NULL;
4409 /* Re-open the image back. */
4410 pImage->pszFilename = pszOldImageName;
4411 rrc = vmdkOpenImage(pImage, pImage->uOpenFlags);
4412 AssertRC(rrc);
4413 }
4414
4415out:
4416 for (i = 0; i < DescriptorCopy.cLines; i++)
4417 if (DescriptorCopy.aLines[i])
4418 RTStrFree(DescriptorCopy.aLines[i]);
4419 if (apszOldName)
4420 {
4421 for (i = 0; i <= cExtents; i++)
4422 if (apszOldName[i])
4423 RTStrFree(apszOldName[i]);
4424 RTMemTmpFree(apszOldName);
4425 }
4426 if (apszNewName)
4427 {
4428 for (i = 0; i <= cExtents; i++)
4429 if (apszNewName[i])
4430 RTStrFree(apszNewName[i]);
4431 RTMemTmpFree(apszNewName);
4432 }
4433 if (apszNewLines)
4434 {
4435 for (i = 0; i < cExtents; i++)
4436 if (apszNewLines[i])
4437 RTStrFree(apszNewLines[i]);
4438 RTMemTmpFree(apszNewLines);
4439 }
4440 if (pszOldDescName)
4441 RTStrFree(pszOldDescName);
4442 if (pszOldBaseName)
4443 RTStrFree(pszOldBaseName);
4444 if (pszNewBaseName)
4445 RTStrFree(pszNewBaseName);
4446 if (pszOldFullName)
4447 RTStrFree(pszOldFullName);
4448 if (pszNewFullName)
4449 RTStrFree(pszNewFullName);
4450 LogFlowFunc(("returns %Rrc\n", rc));
4451 return rc;
4452}
4453
4454/** @copydoc VBOXHDDBACKEND::pfnClose */
4455static int vmdkClose(void *pBackendData, bool fDelete)
4456{
4457 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
4458 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4459 int rc = VINF_SUCCESS;
4460
4461 /* Freeing a never allocated image (e.g. because the open failed) is
4462 * not signalled as an error. After all nothing bad happens. */
4463 if (pImage)
4464 {
4465 vmdkFreeImage(pImage, fDelete);
4466 RTMemFree(pImage);
4467 }
4468
4469 LogFlowFunc(("returns %Rrc\n", rc));
4470 return rc;
4471}
4472
4473/** @copydoc VBOXHDDBACKEND::pfnRead */
4474static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
4475 size_t cbToRead, size_t *pcbActuallyRead)
4476{
4477 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
4478 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4479 PVMDKEXTENT pExtent;
4480 uint64_t uSectorExtentRel;
4481 uint64_t uSectorExtentAbs;
4482 int rc;
4483
4484 AssertPtr(pImage);
4485 Assert(uOffset % 512 == 0);
4486 Assert(cbToRead % 512 == 0);
4487
4488 if ( uOffset + cbToRead > pImage->cbSize
4489 || cbToRead == 0)
4490 {
4491 rc = VERR_INVALID_PARAMETER;
4492 goto out;
4493 }
4494
4495 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
4496 &pExtent, &uSectorExtentRel);
4497 if (RT_FAILURE(rc))
4498 goto out;
4499
4500 /* Check access permissions as defined in the extent descriptor. */
4501 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
4502 {
4503 rc = VERR_VD_VMDK_INVALID_STATE;
4504 goto out;
4505 }
4506
4507 /* Clip read range to remain in this extent. */
4508 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4509
4510 /* Handle the read according to the current extent type. */
4511 switch (pExtent->enmType)
4512 {
4513 case VMDKETYPE_HOSTED_SPARSE:
4514#ifdef VBOX_WITH_VMDK_ESX
4515 case VMDKETYPE_ESX_SPARSE:
4516#endif /* VBOX_WITH_VMDK_ESX */
4517 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
4518 &uSectorExtentAbs);
4519 if (RT_FAILURE(rc))
4520 goto out;
4521 /* Clip read range to at most the rest of the grain. */
4522 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
4523 Assert(!(cbToRead % 512));
4524 if (uSectorExtentAbs == 0)
4525 rc = VERR_VD_BLOCK_FREE;
4526 else
4527 {
4528 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
4529 {
4530 uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
4531 uSectorExtentAbs -= uSectorInGrain;
4532 uint64_t uLBA;
4533 if (pExtent->uGrainSector != uSectorExtentAbs)
4534 {
4535 rc = vmdkFileInflateAt(pExtent->pFile, VMDK_SECTOR2BYTE(uSectorExtentAbs),
4536 pExtent->pvGrain, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain), VMDK_MARKER_IGNORE, &uLBA);
4537 if (RT_FAILURE(rc))
4538 {
4539 pExtent->uGrainSector = 0xffffffffffffffffULL;
4540 AssertRC(rc);
4541 goto out;
4542 }
4543 pExtent->uGrainSector = uSectorExtentAbs;
4544 Assert(uLBA == uSectorExtentRel);
4545 }
4546 memcpy(pvBuf, (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain), cbToRead);
4547 }
4548 else
4549 {
4550 rc = vmdkFileReadAt(pExtent->pFile,
4551 VMDK_SECTOR2BYTE(uSectorExtentAbs),
4552 pvBuf, cbToRead, NULL);
4553 }
4554 }
4555 break;
4556 case VMDKETYPE_FLAT:
4557 rc = vmdkFileReadAt(pExtent->pFile,
4558 VMDK_SECTOR2BYTE(uSectorExtentRel),
4559 pvBuf, cbToRead, NULL);
4560 break;
4561 case VMDKETYPE_ZERO:
4562 memset(pvBuf, '\0', cbToRead);
4563 break;
4564 }
4565 if (pcbActuallyRead)
4566 *pcbActuallyRead = cbToRead;
4567
4568out:
4569 LogFlowFunc(("returns %Rrc\n", rc));
4570 return rc;
4571}
4572
4573/** @copydoc VBOXHDDBACKEND::pfnWrite */
4574static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
4575 size_t cbToWrite, size_t *pcbWriteProcess,
4576 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
4577{
4578 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
4579 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4580 PVMDKEXTENT pExtent;
4581 uint64_t uSectorExtentRel;
4582 uint64_t uSectorExtentAbs;
4583 int rc;
4584
4585 AssertPtr(pImage);
4586 Assert(uOffset % 512 == 0);
4587 Assert(cbToWrite % 512 == 0);
4588
4589 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4590 {
4591 rc = VERR_VD_IMAGE_READ_ONLY;
4592 goto out;
4593 }
4594
4595 if (cbToWrite == 0)
4596 {
4597 rc = VERR_INVALID_PARAMETER;
4598 goto out;
4599 }
4600
4601 /* No size check here, will do that later when the extent is located.
4602 * There are sparse images out there which according to the spec are
4603 * invalid, because the total size is not a multiple of the grain size.
4604 * Also for sparse images which are stitched together in odd ways (not at
4605 * grain boundaries, and with the nominal size not being a multiple of the
4606 * grain size), this would prevent writing to the last grain. */
4607
4608 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
4609 &pExtent, &uSectorExtentRel);
4610 if (RT_FAILURE(rc))
4611 goto out;
4612
4613 /* Check access permissions as defined in the extent descriptor. */
4614 if (pExtent->enmAccess != VMDKACCESS_READWRITE)
4615 {
4616 rc = VERR_VD_VMDK_INVALID_STATE;
4617 goto out;
4618 }
4619
4620 /* Handle the write according to the current extent type. */
4621 switch (pExtent->enmType)
4622 {
4623 case VMDKETYPE_HOSTED_SPARSE:
4624#ifdef VBOX_WITH_VMDK_ESX
4625 case VMDKETYPE_ESX_SPARSE:
4626#endif /* VBOX_WITH_VMDK_ESX */
4627 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
4628 &uSectorExtentAbs);
4629 if (RT_FAILURE(rc))
4630 goto out;
4631 /* Clip write range to at most the rest of the grain. */
4632 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
4633 if (uSectorExtentAbs == 0)
4634 {
4635 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
4636 {
4637 /* Full block write to a previously unallocated block.
4638 * Check if the caller wants to avoid this. */
4639 if (!(fWrite & VD_WRITE_NO_ALLOC))
4640 {
4641 /* Allocate GT and find out where to store the grain. */
4642 rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
4643 uSectorExtentRel, pvBuf, cbToWrite);
4644 }
4645 else
4646 rc = VERR_VD_BLOCK_FREE;
4647 *pcbPreRead = 0;
4648 *pcbPostRead = 0;
4649 }
4650 else
4651 {
4652 /* Clip write range to remain in this extent. */
4653 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4654 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
4655 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
4656 rc = VERR_VD_BLOCK_FREE;
4657 }
4658 }
4659 else
4660 rc = vmdkFileWriteAt(pExtent->pFile,
4661 VMDK_SECTOR2BYTE(uSectorExtentAbs),
4662 pvBuf, cbToWrite, NULL);
4663 break;
4664 case VMDKETYPE_FLAT:
4665 /* Clip write range to remain in this extent. */
4666 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4667 rc = vmdkFileWriteAt(pExtent->pFile,
4668 VMDK_SECTOR2BYTE(uSectorExtentRel),
4669 pvBuf, cbToWrite, NULL);
4670 break;
4671 case VMDKETYPE_ZERO:
4672 /* Clip write range to remain in this extent. */
4673 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4674 break;
4675 }
4676 if (pcbWriteProcess)
4677 *pcbWriteProcess = cbToWrite;
4678
4679out:
4680 LogFlowFunc(("returns %Rrc\n", rc));
4681 return rc;
4682}
4683
4684/** @copydoc VBOXHDDBACKEND::pfnFlush */
4685static int vmdkFlush(void *pBackendData)
4686{
4687 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4688 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4689 int rc;
4690
4691 AssertPtr(pImage);
4692
4693 rc = vmdkFlushImage(pImage);
4694 LogFlowFunc(("returns %Rrc\n", rc));
4695 return rc;
4696}
4697
4698/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
4699static unsigned vmdkGetVersion(void *pBackendData)
4700{
4701 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4702 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4703
4704 AssertPtr(pImage);
4705
4706 if (pImage)
4707 return VMDK_IMAGE_VERSION;
4708 else
4709 return 0;
4710}
4711
4712/** @copydoc VBOXHDDBACKEND::pfnGetImageType */
4713static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
4714{
4715 LogFlowFunc(("pBackendData=%#p penmImageType=%#p\n", pBackendData, penmImageType));
4716 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4717 int rc = VINF_SUCCESS;
4718
4719 AssertPtr(pImage);
4720 AssertPtr(penmImageType);
4721
4722 if (pImage && pImage->cExtents != 0)
4723 *penmImageType = pImage->enmImageType;
4724 else
4725 rc = VERR_VD_NOT_OPENED;
4726
4727 LogFlowFunc(("returns %Rrc enmImageType=%u\n", rc, *penmImageType));
4728 return rc;
4729}
4730
4731/** @copydoc VBOXHDDBACKEND::pfnGetSize */
4732static uint64_t vmdkGetSize(void *pBackendData)
4733{
4734 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4735 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4736
4737 AssertPtr(pImage);
4738
4739 if (pImage)
4740 return pImage->cbSize;
4741 else
4742 return 0;
4743}
4744
4745/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
4746static uint64_t vmdkGetFileSize(void *pBackendData)
4747{
4748 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4749 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4750 uint64_t cb = 0;
4751
4752 AssertPtr(pImage);
4753
4754 if (pImage)
4755 {
4756 uint64_t cbFile;
4757 if (pImage->pFile != NULL)
4758 {
4759 int rc = vmdkFileGetSize(pImage->pFile, &cbFile);
4760 if (RT_SUCCESS(rc))
4761 cb += cbFile;
4762 }
4763 for (unsigned i = 0; i < pImage->cExtents; i++)
4764 {
4765 if (pImage->pExtents[i].pFile != NULL)
4766 {
4767 int rc = vmdkFileGetSize(pImage->pExtents[i].pFile, &cbFile);
4768 if (RT_SUCCESS(rc))
4769 cb += cbFile;
4770 }
4771 }
4772 }
4773
4774 LogFlowFunc(("returns %lld\n", cb));
4775 return cb;
4776}
4777
4778/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
4779static int vmdkGetPCHSGeometry(void *pBackendData,
4780 PPDMMEDIAGEOMETRY pPCHSGeometry)
4781{
4782 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
4783 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4784 int rc;
4785
4786 AssertPtr(pImage);
4787
4788 if (pImage)
4789 {
4790 if (pImage->PCHSGeometry.cCylinders)
4791 {
4792 *pPCHSGeometry = pImage->PCHSGeometry;
4793 rc = VINF_SUCCESS;
4794 }
4795 else
4796 rc = VERR_VD_GEOMETRY_NOT_SET;
4797 }
4798 else
4799 rc = VERR_VD_NOT_OPENED;
4800
4801 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4802 return rc;
4803}
4804
4805/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
4806static int vmdkSetPCHSGeometry(void *pBackendData,
4807 PCPDMMEDIAGEOMETRY pPCHSGeometry)
4808{
4809 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4810 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4811 int rc;
4812
4813 AssertPtr(pImage);
4814
4815 if (pImage)
4816 {
4817 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4818 {
4819 rc = VERR_VD_IMAGE_READ_ONLY;
4820 goto out;
4821 }
4822 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
4823 if (RT_FAILURE(rc))
4824 goto out;
4825
4826 pImage->PCHSGeometry = *pPCHSGeometry;
4827 rc = VINF_SUCCESS;
4828 }
4829 else
4830 rc = VERR_VD_NOT_OPENED;
4831
4832out:
4833 LogFlowFunc(("returns %Rrc\n", rc));
4834 return rc;
4835}
4836
4837/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
4838static int vmdkGetLCHSGeometry(void *pBackendData,
4839 PPDMMEDIAGEOMETRY pLCHSGeometry)
4840{
4841 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
4842 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4843 int rc;
4844
4845 AssertPtr(pImage);
4846
4847 if (pImage)
4848 {
4849 if (pImage->LCHSGeometry.cCylinders)
4850 {
4851 *pLCHSGeometry = pImage->LCHSGeometry;
4852 rc = VINF_SUCCESS;
4853 }
4854 else
4855 rc = VERR_VD_GEOMETRY_NOT_SET;
4856 }
4857 else
4858 rc = VERR_VD_NOT_OPENED;
4859
4860 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4861 return rc;
4862}
4863
4864/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
4865static int vmdkSetLCHSGeometry(void *pBackendData,
4866 PCPDMMEDIAGEOMETRY pLCHSGeometry)
4867{
4868 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4869 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4870 int rc;
4871
4872 AssertPtr(pImage);
4873
4874 if (pImage)
4875 {
4876 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4877 {
4878 rc = VERR_VD_IMAGE_READ_ONLY;
4879 goto out;
4880 }
4881 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
4882 if (RT_FAILURE(rc))
4883 goto out;
4884
4885 pImage->LCHSGeometry = *pLCHSGeometry;
4886 rc = VINF_SUCCESS;
4887 }
4888 else
4889 rc = VERR_VD_NOT_OPENED;
4890
4891out:
4892 LogFlowFunc(("returns %Rrc\n", rc));
4893 return rc;
4894}
4895
4896/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
4897static unsigned vmdkGetImageFlags(void *pBackendData)
4898{
4899 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4900 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4901 unsigned uImageFlags;
4902
4903 AssertPtr(pImage);
4904
4905 if (pImage)
4906 uImageFlags = pImage->uImageFlags;
4907 else
4908 uImageFlags = 0;
4909
4910 LogFlowFunc(("returns %#x\n", uImageFlags));
4911 return uImageFlags;
4912}
4913
4914/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
4915static unsigned vmdkGetOpenFlags(void *pBackendData)
4916{
4917 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4918 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4919 unsigned uOpenFlags;
4920
4921 AssertPtr(pImage);
4922
4923 if (pImage)
4924 uOpenFlags = pImage->uOpenFlags;
4925 else
4926 uOpenFlags = 0;
4927
4928 LogFlowFunc(("returns %#x\n", uOpenFlags));
4929 return uOpenFlags;
4930}
4931
4932/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
4933static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
4934{
4935 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
4936 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4937 int rc;
4938
4939 /* Image must be opened and the new flags must be valid. Just readonly and
4940 * info flags are supported. */
4941 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO)))
4942 {
4943 rc = VERR_INVALID_PARAMETER;
4944 goto out;
4945 }
4946
4947 /* Implement this operation via reopening the image. */
4948 vmdkFreeImage(pImage, false);
4949 rc = vmdkOpenImage(pImage, uOpenFlags);
4950
4951out:
4952 LogFlowFunc(("returns %Rrc\n", rc));
4953 return rc;
4954}
4955
4956/** @copydoc VBOXHDDBACKEND::pfnGetComment */
4957static int vmdkGetComment(void *pBackendData, char *pszComment,
4958 size_t cbComment)
4959{
4960 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
4961 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4962 int rc;
4963
4964 AssertPtr(pImage);
4965
4966 if (pImage)
4967 {
4968 const char *pszCommentEncoded = NULL;
4969 rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
4970 "ddb.comment", &pszCommentEncoded);
4971 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
4972 pszCommentEncoded = NULL;
4973 else if (RT_FAILURE(rc))
4974 goto out;
4975
4976 if (pszComment && pszCommentEncoded)
4977 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
4978 else
4979 {
4980 if (pszComment)
4981 *pszComment = '\0';
4982 rc = VINF_SUCCESS;
4983 }
4984 if (pszCommentEncoded)
4985 RTStrFree((char *)(void *)pszCommentEncoded);
4986 }
4987 else
4988 rc = VERR_VD_NOT_OPENED;
4989
4990out:
4991 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
4992 return rc;
4993}
4994
4995/** @copydoc VBOXHDDBACKEND::pfnSetComment */
4996static int vmdkSetComment(void *pBackendData, const char *pszComment)
4997{
4998 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
4999 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5000 int rc;
5001
5002 AssertPtr(pImage);
5003
5004 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
5005 {
5006 rc = VERR_VD_IMAGE_READ_ONLY;
5007 goto out;
5008 }
5009
5010 if (pImage)
5011 rc = vmdkSetImageComment(pImage, pszComment);
5012 else
5013 rc = VERR_VD_NOT_OPENED;
5014
5015out:
5016 LogFlowFunc(("returns %Rrc\n", rc));
5017 return rc;
5018}
5019
5020/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
5021static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
5022{
5023 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5024 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5025 int rc;
5026
5027 AssertPtr(pImage);
5028
5029 if (pImage)
5030 {
5031 *pUuid = pImage->ImageUuid;
5032 rc = VINF_SUCCESS;
5033 }
5034 else
5035 rc = VERR_VD_NOT_OPENED;
5036
5037 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5038 return rc;
5039}
5040
5041/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
5042static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
5043{
5044 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5045 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5046 int rc;
5047
5048 LogFlowFunc(("%RTuuid\n", pUuid));
5049 AssertPtr(pImage);
5050
5051 if (pImage)
5052 {
5053 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5054 {
5055 pImage->ImageUuid = *pUuid;
5056 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
5057 VMDK_DDB_IMAGE_UUID, pUuid);
5058 if (RT_FAILURE(rc))
5059 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
5060 rc = VINF_SUCCESS;
5061 }
5062 else
5063 rc = VERR_VD_IMAGE_READ_ONLY;
5064 }
5065 else
5066 rc = VERR_VD_NOT_OPENED;
5067
5068 LogFlowFunc(("returns %Rrc\n", rc));
5069 return rc;
5070}
5071
5072/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
5073static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
5074{
5075 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5076 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5077 int rc;
5078
5079 AssertPtr(pImage);
5080
5081 if (pImage)
5082 {
5083 *pUuid = pImage->ModificationUuid;
5084 rc = VINF_SUCCESS;
5085 }
5086 else
5087 rc = VERR_VD_NOT_OPENED;
5088
5089 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5090 return rc;
5091}
5092
5093/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
5094static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
5095{
5096 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5097 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5098 int rc;
5099
5100 AssertPtr(pImage);
5101
5102 if (pImage)
5103 {
5104 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5105 {
5106 pImage->ModificationUuid = *pUuid;
5107 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
5108 VMDK_DDB_MODIFICATION_UUID, pUuid);
5109 if (RT_FAILURE(rc))
5110 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
5111 rc = VINF_SUCCESS;
5112 }
5113 else
5114 rc = VERR_VD_IMAGE_READ_ONLY;
5115 }
5116 else
5117 rc = VERR_VD_NOT_OPENED;
5118
5119 LogFlowFunc(("returns %Rrc\n", rc));
5120 return rc;
5121}
5122
5123/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
5124static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
5125{
5126 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5127 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5128 int rc;
5129
5130 AssertPtr(pImage);
5131
5132 if (pImage)
5133 {
5134 *pUuid = pImage->ParentUuid;
5135 rc = VINF_SUCCESS;
5136 }
5137 else
5138 rc = VERR_VD_NOT_OPENED;
5139
5140 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5141 return rc;
5142}
5143
5144/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
5145static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
5146{
5147 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5148 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5149 int rc;
5150
5151 AssertPtr(pImage);
5152
5153 if (pImage)
5154 {
5155 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5156 {
5157 pImage->ParentUuid = *pUuid;
5158 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
5159 VMDK_DDB_PARENT_UUID, pUuid);
5160 if (RT_FAILURE(rc))
5161 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
5162 rc = VINF_SUCCESS;
5163 }
5164 else
5165 rc = VERR_VD_IMAGE_READ_ONLY;
5166 }
5167 else
5168 rc = VERR_VD_NOT_OPENED;
5169
5170 LogFlowFunc(("returns %Rrc\n", rc));
5171 return rc;
5172}
5173
5174/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
5175static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
5176{
5177 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
5178 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5179 int rc;
5180
5181 AssertPtr(pImage);
5182
5183 if (pImage)
5184 {
5185 *pUuid = pImage->ParentModificationUuid;
5186 rc = VINF_SUCCESS;
5187 }
5188 else
5189 rc = VERR_VD_NOT_OPENED;
5190
5191 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
5192 return rc;
5193}
5194
5195/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
5196static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
5197{
5198 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
5199 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5200 int rc;
5201
5202 AssertPtr(pImage);
5203
5204 if (pImage)
5205 {
5206 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
5207 {
5208 pImage->ParentModificationUuid = *pUuid;
5209 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
5210 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
5211 if (RT_FAILURE(rc))
5212 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
5213 rc = VINF_SUCCESS;
5214 }
5215 else
5216 rc = VERR_VD_IMAGE_READ_ONLY;
5217 }
5218 else
5219 rc = VERR_VD_NOT_OPENED;
5220
5221 LogFlowFunc(("returns %Rrc\n", rc));
5222 return rc;
5223}
5224
5225/** @copydoc VBOXHDDBACKEND::pfnDump */
5226static void vmdkDump(void *pBackendData)
5227{
5228 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
5229
5230 AssertPtr(pImage);
5231 if (pImage)
5232 {
5233 RTLogPrintf("Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
5234 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
5235 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
5236 VMDK_BYTE2SECTOR(pImage->cbSize));
5237 RTLogPrintf("Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
5238 RTLogPrintf("Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
5239 RTLogPrintf("Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
5240 RTLogPrintf("Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
5241 }
5242}
5243
5244
5245static int vmdkGetTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
5246{
5247 int rc = VERR_NOT_IMPLEMENTED;
5248 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5249 return rc;
5250}
5251
5252static int vmdkGetParentTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
5253{
5254 int rc = VERR_NOT_IMPLEMENTED;
5255 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5256 return rc;
5257}
5258
5259static int vmdkSetParentTimeStamp(void *pvBackendData, PCRTTIMESPEC pTimeStamp)
5260{
5261 int rc = VERR_NOT_IMPLEMENTED;
5262 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5263 return rc;
5264}
5265
5266static int vmdkGetParentFilename(void *pvBackendData, char **ppszParentFilename)
5267{
5268 int rc = VERR_NOT_IMPLEMENTED;
5269 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5270 return rc;
5271}
5272
5273static int vmdkSetParentFilename(void *pvBackendData, const char *pszParentFilename)
5274{
5275 int rc = VERR_NOT_IMPLEMENTED;
5276 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
5277 return rc;
5278}
5279
5280static bool vmdkIsAsyncIOSupported(void *pvBackendData)
5281{
5282 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5283 bool fAsyncIOSupported = false;
5284
5285 if (pImage)
5286 {
5287 /* We only support async I/O support if the image only consists of FLAT or ZERO extents. */
5288 fAsyncIOSupported = true;
5289 for (unsigned i = 0; i < pImage->cExtents; i++)
5290 {
5291 if ( (pImage->pExtents[i].enmType != VMDKETYPE_FLAT)
5292 && (pImage->pExtents[i].enmType != VMDKETYPE_ZERO))
5293 {
5294 fAsyncIOSupported = false;
5295 break; /* Stop search */
5296 }
5297 }
5298 }
5299
5300 return fAsyncIOSupported;
5301}
5302
5303static int vmdkAsyncRead(void *pvBackendData, uint64_t uOffset, size_t cbRead,
5304 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
5305{
5306 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5307 PVMDKEXTENT pExtent;
5308 int rc = VINF_SUCCESS;
5309 unsigned cTasksToSubmit = 0;
5310 PPDMDATASEG paSegCurrent = paSeg;
5311 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5312 unsigned uOffsetInCurrentSegment = 0;
5313
5314 AssertPtr(pImage);
5315 Assert(uOffset % 512 == 0);
5316 Assert(cbRead % 512 == 0);
5317
5318 if ( uOffset + cbRead > pImage->cbSize
5319 || cbRead == 0)
5320 {
5321 rc = VERR_INVALID_PARAMETER;
5322 goto out;
5323 }
5324
5325 while (cbRead && cSeg)
5326 {
5327 unsigned cbToRead;
5328 uint64_t uSectorExtentRel;
5329
5330 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5331 &pExtent, &uSectorExtentRel);
5332 if (RT_FAILURE(rc))
5333 goto out;
5334
5335 /* Check access permissions as defined in the extent descriptor. */
5336 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
5337 {
5338 rc = VERR_VD_VMDK_INVALID_STATE;
5339 goto out;
5340 }
5341
5342 /* Clip read range to remain in this extent. */
5343 cbToRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5344 /* Clip read range to remain into current data segment. */
5345 cbToRead = RT_MIN(cbToRead, cbLeftInCurrentSegment);
5346
5347 switch (pExtent->enmType)
5348 {
5349 case VMDKETYPE_FLAT:
5350 {
5351 /* Setup new task. */
5352 void *pTask;
5353 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareRead(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
5354 VMDK_SECTOR2BYTE(uSectorExtentRel),
5355 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
5356 cbToRead, &pTask);
5357 if (RT_FAILURE(rc))
5358 {
5359 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
5360 goto out;
5361 }
5362
5363 /* Check for enough room first. */
5364 if (cTasksToSubmit >= pImage->cTask)
5365 {
5366 /* We reached maximum, resize array. Try to realloc memory first. */
5367 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
5368
5369 if (!apTaskNew)
5370 {
5371 /* We failed. Allocate completely new. */
5372 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
5373 if (!apTaskNew)
5374 {
5375 /* Damn, we are out of memory. */
5376 rc = VERR_NO_MEMORY;
5377 goto out;
5378 }
5379
5380 /* Copy task handles over. */
5381 for (unsigned i = 0; i < cTasksToSubmit; i++)
5382 apTaskNew[i] = pImage->apTask[i];
5383
5384 /* Free old memory. */
5385 RTMemFree(pImage->apTask);
5386 }
5387
5388 pImage->cTask = cTasksToSubmit + 10;
5389 pImage->apTask = apTaskNew;
5390 }
5391
5392 pImage->apTask[cTasksToSubmit] = pTask;
5393 cTasksToSubmit++;
5394 break;
5395 }
5396 case VMDKETYPE_ZERO:
5397 memset((uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment, 0, cbToRead);
5398 break;
5399 default:
5400 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
5401 }
5402
5403 cbRead -= cbToRead;
5404 uOffset += cbToRead;
5405 cbLeftInCurrentSegment -= cbToRead;
5406 uOffsetInCurrentSegment += cbToRead;
5407 /* Go to next extent if there is no space left in current one. */
5408 if (!cbLeftInCurrentSegment)
5409 {
5410 uOffsetInCurrentSegment = 0;
5411 paSegCurrent++;
5412 cSeg--;
5413 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5414 }
5415 }
5416
5417 AssertMsg(cbRead == 0, ("No segment left but there is still data to read\n"));
5418
5419 if (cTasksToSubmit == 0)
5420 {
5421 /* The request was completely in a ZERO extent nothing to do. */
5422 rc = VINF_VD_ASYNC_IO_FINISHED;
5423 }
5424 else
5425 {
5426 /* Submit tasks. */
5427 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5428 pImage->apTask, cTasksToSubmit,
5429 NULL, pvUser,
5430 NULL /* Nothing required after read. */);
5431 AssertMsgRC(rc, ("Failed to enqueue tasks rc=%Rrc\n", rc));
5432 }
5433
5434out:
5435 LogFlowFunc(("returns %Rrc\n", rc));
5436 return rc;
5437}
5438
5439static int vmdkAsyncWrite(void *pvBackendData, uint64_t uOffset, size_t cbWrite,
5440 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
5441{
5442 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5443 PVMDKEXTENT pExtent;
5444 int rc = VINF_SUCCESS;
5445 unsigned cTasksToSubmit = 0;
5446 PPDMDATASEG paSegCurrent = paSeg;
5447 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5448 unsigned uOffsetInCurrentSegment = 0;
5449
5450 AssertPtr(pImage);
5451 Assert(uOffset % 512 == 0);
5452 Assert(cbWrite % 512 == 0);
5453
5454 if ( uOffset + cbWrite > pImage->cbSize
5455 || cbWrite == 0)
5456 {
5457 rc = VERR_INVALID_PARAMETER;
5458 goto out;
5459 }
5460
5461 while (cbWrite && cSeg)
5462 {
5463 unsigned cbToWrite;
5464 uint64_t uSectorExtentRel;
5465
5466 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5467 &pExtent, &uSectorExtentRel);
5468 if (RT_FAILURE(rc))
5469 goto out;
5470
5471 /* Check access permissions as defined in the extent descriptor. */
5472 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
5473 {
5474 rc = VERR_VD_VMDK_INVALID_STATE;
5475 goto out;
5476 }
5477
5478 /* Clip write range to remain in this extent. */
5479 cbToWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5480 /* Clip write range to remain into current data segment. */
5481 cbToWrite = RT_MIN(cbToWrite, cbLeftInCurrentSegment);
5482
5483 switch (pExtent->enmType)
5484 {
5485 case VMDKETYPE_FLAT:
5486 {
5487 /* Setup new task. */
5488 void *pTask;
5489 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareWrite(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
5490 VMDK_SECTOR2BYTE(uSectorExtentRel),
5491 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
5492 cbToWrite, &pTask);
5493 if (RT_FAILURE(rc))
5494 {
5495 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
5496 goto out;
5497 }
5498
5499 /* Check for enough room first. */
5500 if (cTasksToSubmit >= pImage->cTask)
5501 {
5502 /* We reached maximum, resize array. Try to realloc memory first. */
5503 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
5504
5505 if (!apTaskNew)
5506 {
5507 /* We failed. Allocate completely new. */
5508 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
5509 if (!apTaskNew)
5510 {
5511 /* Damn, we are out of memory. */
5512 rc = VERR_NO_MEMORY;
5513 goto out;
5514 }
5515
5516 /* Copy task handles over. */
5517 for (unsigned i = 0; i < cTasksToSubmit; i++)
5518 apTaskNew[i] = pImage->apTask[i];
5519
5520 /* Free old memory. */
5521 RTMemFree(pImage->apTask);
5522 }
5523
5524 pImage->cTask = cTasksToSubmit + 10;
5525 pImage->apTask = apTaskNew;
5526 }
5527
5528 pImage->apTask[cTasksToSubmit] = pTask;
5529 cTasksToSubmit++;
5530 break;
5531 }
5532 case VMDKETYPE_ZERO:
5533 /* Nothing left to do. */
5534 break;
5535 default:
5536 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
5537 }
5538
5539 cbWrite -= cbToWrite;
5540 uOffset += cbToWrite;
5541 cbLeftInCurrentSegment -= cbToWrite;
5542 uOffsetInCurrentSegment += cbToWrite;
5543 /* Go to next extent if there is no space left in current one. */
5544 if (!cbLeftInCurrentSegment)
5545 {
5546 uOffsetInCurrentSegment = 0;
5547 paSegCurrent++;
5548 cSeg--;
5549 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5550 }
5551 }
5552
5553 AssertMsg(cbWrite == 0, ("No segment left but there is still data to read\n"));
5554
5555 if (cTasksToSubmit == 0)
5556 {
5557 /* The request was completely in a ZERO extent nothing to do. */
5558 rc = VINF_VD_ASYNC_IO_FINISHED;
5559 }
5560 else
5561 {
5562 /* Submit tasks. */
5563 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5564 pImage->apTask, cTasksToSubmit,
5565 NULL, pvUser,
5566 NULL /* Nothing required after read. */);
5567 AssertMsgRC(rc, ("Failed to enqueue tasks rc=%Rrc\n", rc));
5568 }
5569
5570out:
5571 LogFlowFunc(("returns %Rrc\n", rc));
5572 return rc;
5573
5574}
5575
5576
5577VBOXHDDBACKEND g_VmdkBackend =
5578{
5579 /* pszBackendName */
5580 "VMDK",
5581 /* cbSize */
5582 sizeof(VBOXHDDBACKEND),
5583 /* uBackendCaps */
5584 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
5585 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE |VD_CAP_ASYNC,
5586 /* papszFileExtensions */
5587 s_apszVmdkFileExtensions,
5588 /* paConfigInfo */
5589 NULL,
5590 /* hPlugin */
5591 NIL_RTLDRMOD,
5592 /* pfnCheckIfValid */
5593 vmdkCheckIfValid,
5594 /* pfnOpen */
5595 vmdkOpen,
5596 /* pfnCreate */
5597 vmdkCreate,
5598 /* pfnRename */
5599 vmdkRename,
5600 /* pfnClose */
5601 vmdkClose,
5602 /* pfnRead */
5603 vmdkRead,
5604 /* pfnWrite */
5605 vmdkWrite,
5606 /* pfnFlush */
5607 vmdkFlush,
5608 /* pfnGetVersion */
5609 vmdkGetVersion,
5610 /* pfnGetImageType */
5611 vmdkGetImageType,
5612 /* pfnGetSize */
5613 vmdkGetSize,
5614 /* pfnGetFileSize */
5615 vmdkGetFileSize,
5616 /* pfnGetPCHSGeometry */
5617 vmdkGetPCHSGeometry,
5618 /* pfnSetPCHSGeometry */
5619 vmdkSetPCHSGeometry,
5620 /* pfnGetLCHSGeometry */
5621 vmdkGetLCHSGeometry,
5622 /* pfnSetLCHSGeometry */
5623 vmdkSetLCHSGeometry,
5624 /* pfnGetImageFlags */
5625 vmdkGetImageFlags,
5626 /* pfnGetOpenFlags */
5627 vmdkGetOpenFlags,
5628 /* pfnSetOpenFlags */
5629 vmdkSetOpenFlags,
5630 /* pfnGetComment */
5631 vmdkGetComment,
5632 /* pfnSetComment */
5633 vmdkSetComment,
5634 /* pfnGetUuid */
5635 vmdkGetUuid,
5636 /* pfnSetUuid */
5637 vmdkSetUuid,
5638 /* pfnGetModificationUuid */
5639 vmdkGetModificationUuid,
5640 /* pfnSetModificationUuid */
5641 vmdkSetModificationUuid,
5642 /* pfnGetParentUuid */
5643 vmdkGetParentUuid,
5644 /* pfnSetParentUuid */
5645 vmdkSetParentUuid,
5646 /* pfnGetParentModificationUuid */
5647 vmdkGetParentModificationUuid,
5648 /* pfnSetParentModificationUuid */
5649 vmdkSetParentModificationUuid,
5650 /* pfnDump */
5651 vmdkDump,
5652 /* pfnGetTimeStamp */
5653 vmdkGetTimeStamp,
5654 /* pfnGetParentTimeStamp */
5655 vmdkGetParentTimeStamp,
5656 /* pfnSetParentTimeStamp */
5657 vmdkSetParentTimeStamp,
5658 /* pfnGetParentFilename */
5659 vmdkGetParentFilename,
5660 /* pfnSetParentFilename */
5661 vmdkSetParentFilename,
5662 /* pfnIsAsyncIOSupported */
5663 vmdkIsAsyncIOSupported,
5664 /* pfnAsyncRead */
5665 vmdkAsyncRead,
5666 /* pfnAsyncWrite */
5667 vmdkAsyncWrite,
5668 /* pfnComposeLocation */
5669 genericFileComposeLocation,
5670 /* pfnComposeName */
5671 genericFileComposeName
5672};
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