VirtualBox

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

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

Storage/VBoxHDD-new: introduced VD interfaces per image and per operation, completely unifying callback handling.

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