VirtualBox

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

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

warnings

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