VirtualBox

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

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

Storage: big cleanup of the VD interfaces, especially hide the linked list better.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 186.5 KB
Line 
1/** $Id: VmdkHDDCore.cpp 11435 2008-08-14 18:23:31Z vboxsync $ */
2/** @file
3 * VMDK Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD_VMDK
26#include "VBoxHDD-newInternal.h"
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include <iprt/alloc.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/path.h>
35#include <iprt/string.h>
36#include <iprt/rand.h>
37
38
39/*******************************************************************************
40* Constants And Macros, Structures and Typedefs *
41*******************************************************************************/
42
43/** Maximum encoded string size (including NUL) we allow for VMDK images.
44 * Deliberately not set high to avoid running out of descriptor space. */
45#define VMDK_ENCODED_COMMENT_MAX 1024
46
47/** VMDK descriptor DDB entry for PCHS cylinders. */
48#define VMDK_DDB_GEO_PCHS_CYLINDERS "ddb.geometry.cylinders"
49
50/** VMDK descriptor DDB entry for PCHS heads. */
51#define VMDK_DDB_GEO_PCHS_HEADS "ddb.geometry.heads"
52
53/** VMDK descriptor DDB entry for PCHS sectors. */
54#define VMDK_DDB_GEO_PCHS_SECTORS "ddb.geometry.sectors"
55
56/** VMDK descriptor DDB entry for LCHS cylinders. */
57#define VMDK_DDB_GEO_LCHS_CYLINDERS "ddb.geometry.biosCylinders"
58
59/** VMDK descriptor DDB entry for LCHS heads. */
60#define VMDK_DDB_GEO_LCHS_HEADS "ddb.geometry.biosHeads"
61
62/** VMDK descriptor DDB entry for LCHS sectors. */
63#define VMDK_DDB_GEO_LCHS_SECTORS "ddb.geometry.biosSectors"
64
65/** VMDK descriptor DDB entry for image UUID. */
66#define VMDK_DDB_IMAGE_UUID "ddb.uuid.image"
67
68/** VMDK descriptor DDB entry for image modification UUID. */
69#define VMDK_DDB_MODIFICATION_UUID "ddb.uuid.modification"
70
71/** VMDK descriptor DDB entry for parent image UUID. */
72#define VMDK_DDB_PARENT_UUID "ddb.uuid.parent"
73
74/** VMDK descriptor DDB entry for parent image modification UUID. */
75#define VMDK_DDB_PARENT_MODIFICATION_UUID "ddb.uuid.parentmodification"
76
77/**
78 * Magic number for hosted images created by VMware Workstation 4, VMware
79 * Workstation 5, VMware Server or VMware Player.
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 list of VD interfaces. */
362 PVDINTERFACE pVDIfs;
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->pVDIfs, 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->pVDIfs, 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->pVDIfs, 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->pVDIfs, 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->pInterfaceError = NULL;
3703 pImage->pInterfaceErrorCallbacks = NULL;
3704 pImage->pInterfaceAsyncIO = NULL;
3705 pImage->pInterfaceAsyncIOCallbacks = NULL;
3706 pImage->pVDIfs = NULL;
3707 /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
3708 * much as possible in vmdkOpenImage. */
3709 rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
3710 vmdkFreeImage(pImage, false);
3711
3712out:
3713 LogFlowFunc(("returns %Rrc\n", rc));
3714 return rc;
3715}
3716
3717/** @copydoc VBOXHDDBACKEND::pfnOpen */
3718static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
3719 PVDINTERFACE pVDIfs, void **ppBackendData)
3720{
3721 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x ppBackendData=%#p\n", pszFilename, uOpenFlags, ppBackendData));
3722 int rc;
3723 PVMDKIMAGE pImage;
3724
3725 /* Check open flags. All valid flags are supported. */
3726 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
3727 {
3728 rc = VERR_INVALID_PARAMETER;
3729 goto out;
3730 }
3731
3732 /* Check remaining arguments. */
3733 if ( !VALID_PTR(pszFilename)
3734 || !*pszFilename
3735 || strchr(pszFilename, '"'))
3736 {
3737 rc = VERR_INVALID_PARAMETER;
3738 goto out;
3739 }
3740
3741
3742 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
3743 if (!pImage)
3744 {
3745 rc = VERR_NO_MEMORY;
3746 goto out;
3747 }
3748 pImage->pszFilename = pszFilename;
3749 pImage->pFile = NULL;
3750 pImage->pExtents = NULL;
3751 pImage->pFiles = NULL;
3752 pImage->pGTCache = NULL;
3753 pImage->pDescData = NULL;
3754 pImage->pInterfaceError = NULL;
3755 pImage->pInterfaceErrorCallbacks = NULL;
3756 pImage->pInterfaceAsyncIO = NULL;
3757 pImage->pInterfaceAsyncIOCallbacks = NULL;
3758 pImage->pVDIfs = NULL;
3759
3760 rc = vmdkOpenImage(pImage, uOpenFlags);
3761 if (RT_SUCCESS(rc))
3762 *ppBackendData = pImage;
3763
3764out:
3765 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
3766 return rc;
3767}
3768
3769/** @copydoc VBOXHDDBACKEND::pfnCreate */
3770static int vmdkCreate(const char *pszFilename, VDIMAGETYPE enmType,
3771 uint64_t cbSize, unsigned uImageFlags,
3772 const char *pszComment,
3773 PCPDMMEDIAGEOMETRY pPCHSGeometry,
3774 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
3775 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
3776 void *pvUser, unsigned uPercentStart,
3777 unsigned uPercentSpan, PVDINTERFACE pVDIfs,
3778 void **ppBackendData)
3779{
3780 LogFlowFunc(("pszFilename=\"%s\" enmType=%d cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x pfnProgress=%#p pvUser=%#p uPercentStart=%u uPercentSpan=%u pVDIfs=%#p ppBackendData=%#p", pszFilename, enmType, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, pfnProgress, pvUser, uPercentStart, uPercentSpan, pVDIfs, ppBackendData));
3781 int rc;
3782 PVMDKIMAGE pImage;
3783
3784 /* Check open flags. All valid flags are supported. */
3785 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
3786 {
3787 rc = VERR_INVALID_PARAMETER;
3788 goto out;
3789 }
3790
3791 /* @todo A quick hack to support differencing images in VMDK. */
3792 if (enmType == VD_IMAGE_TYPE_DIFF)
3793 enmType = VD_IMAGE_TYPE_NORMAL;
3794
3795 /* Check remaining arguments. */
3796 if ( !VALID_PTR(pszFilename)
3797 || !*pszFilename
3798 || strchr(pszFilename, '"')
3799 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
3800 || !VALID_PTR(pPCHSGeometry)
3801 || !VALID_PTR(pLCHSGeometry))
3802 {
3803 rc = VERR_INVALID_PARAMETER;
3804 goto out;
3805 }
3806
3807 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
3808 if (!pImage)
3809 {
3810 rc = VERR_NO_MEMORY;
3811 goto out;
3812 }
3813 pImage->pszFilename = pszFilename;
3814 pImage->pFile = NULL;
3815 pImage->pExtents = NULL;
3816 pImage->pFiles = NULL;
3817 pImage->pGTCache = NULL;
3818 pImage->pDescData = NULL;
3819 pImage->pInterfaceError = NULL;
3820 pImage->pInterfaceErrorCallbacks = NULL;
3821 pImage->pInterfaceAsyncIO = NULL;
3822 pImage->pInterfaceAsyncIOCallbacks = NULL;
3823 pImage->pVDIfs = NULL;
3824 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
3825 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
3826 if (!pImage->pDescData)
3827 {
3828 rc = VERR_NO_MEMORY;
3829 goto out;
3830 }
3831
3832 rc = vmdkCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment,
3833 pPCHSGeometry, pLCHSGeometry, pUuid,
3834 pfnProgress, pvUser, uPercentStart, uPercentSpan);
3835 if (RT_SUCCESS(rc))
3836 {
3837 /* So far the image is opened in read/write mode. Make sure the
3838 * image is opened in read-only mode if the caller requested that. */
3839 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
3840 {
3841 vmdkFreeImage(pImage, false);
3842 rc = vmdkOpenImage(pImage, uOpenFlags);
3843 if (RT_FAILURE(rc))
3844 goto out;
3845 }
3846 *ppBackendData = pImage;
3847 }
3848
3849out:
3850 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
3851 return rc;
3852}
3853
3854/**
3855 * Replaces a fragment of a string with the specified string.
3856 *
3857 * @returns Pointer to the allocated UTF-8 string.
3858 * @param pszWhere UTF-8 string to search in.
3859 * @param pszWhat UTF-8 string to search for.
3860 * @param pszByWhat UTF-8 string to replace the found string with.
3861 */
3862static char * vmdkStrReplace(const char *pszWhere, const char *pszWhat, const char *pszByWhat)
3863{
3864 Assert(VALID_PTR(pszWhere));
3865 Assert(VALID_PTR(pszWhat));
3866 Assert(VALID_PTR(pszByWhat));
3867 const char *pszFoundStr = strstr(pszWhere, pszWhat);
3868 if (!pszFoundStr)
3869 return NULL;
3870 size_t cFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
3871 char *pszNewStr = (char *)RTMemAlloc(cFinal);
3872 if (pszNewStr)
3873 {
3874 char *pszTmp = pszNewStr;
3875 memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
3876 pszTmp += pszFoundStr - pszWhere;
3877 memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
3878 pszTmp += strlen(pszByWhat);
3879 strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
3880 }
3881 return pszNewStr;
3882}
3883
3884/** @copydoc VBOXHDDBACKEND::pfnRename */
3885static int vmdkRename(void *pBackendData, const char *pszFilename)
3886{
3887 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
3888
3889 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3890 int rc = VINF_SUCCESS;
3891 char **apszOldName = NULL;
3892 char **apszNewName = NULL;
3893 char **apszNewLines = NULL;
3894 char *pszOldDescName = NULL;
3895 bool fImageFreed = false;
3896 bool fEmbeddedDesc = false;
3897 unsigned cExtents = pImage->cExtents;
3898 char *pszNewBasename;
3899 char *pszOldBasename;
3900 unsigned i, line;
3901 VMDKDESCRIPTOR DescriptorCopy;
3902 VMDKEXTENT ExtentCopy;
3903
3904 memset(&DescriptorCopy, 0, sizeof(DescriptorCopy));
3905
3906 /* Check arguments. */
3907 if ( !pImage
3908 || (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
3909 || !VALID_PTR(pszFilename)
3910 || !*pszFilename)
3911 {
3912 rc = VERR_INVALID_PARAMETER;
3913 goto out;
3914 }
3915
3916 /*
3917 * Allocate an array to store both old and new names of renamed files
3918 * in case we have to roll back the changes. Arrays are initialized
3919 * with zeros. We actually save stuff when and if we change it.
3920 */
3921 apszOldName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
3922 apszNewName = (char **)RTMemTmpAllocZ((cExtents + 1) * sizeof(char*));
3923 apszNewLines = (char **)RTMemTmpAllocZ((cExtents) * sizeof(char*));
3924 if (!apszOldName || !apszNewName || !apszNewLines)
3925 {
3926 rc = VERR_NO_MEMORY;
3927 goto out;
3928 }
3929
3930 /* Save the descriptor size and position. */
3931 if (pImage->pDescData)
3932 {
3933 /* Separate descriptor file. */
3934 fEmbeddedDesc = false;
3935 }
3936 else
3937 {
3938 /* Embedded descriptor file. */
3939 ExtentCopy = pImage->pExtents[0];
3940 fEmbeddedDesc = true;
3941 }
3942 /* Save the descriptor content. */
3943 DescriptorCopy.cLines = pImage->Descriptor.cLines;
3944 for (i = 0; i < DescriptorCopy.cLines; i++)
3945 {
3946 DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
3947 if (!DescriptorCopy.aLines[i])
3948 {
3949 rc = VERR_NO_MEMORY;
3950 goto out;
3951 }
3952 }
3953
3954 /* Prepare both old and new base names used for string replacement. */
3955 pszNewBasename = RTStrDup(RTPathFilename(pszFilename));
3956 RTPathStripExt(pszNewBasename);
3957 pszOldBasename = RTStrDup(RTPathFilename(pImage->pszFilename));
3958 RTPathStripExt(pszOldBasename);
3959
3960 /* --- Up to this point we have not done any damage yet. --- */
3961
3962 /* Save the old name for easy access to the old descriptor file. */
3963 pszOldDescName = RTStrDup(pImage->pszFilename);
3964
3965 /* Rename the extents. */
3966 for (i = 0, line = pImage->Descriptor.uFirstExtent;
3967 i < cExtents;
3968 i++, line = pImage->Descriptor.aNextLines[line])
3969 {
3970 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3971 /* Assume that vmdkStrReplace will fail. */
3972 rc = VERR_NO_MEMORY;
3973 /* Update the descriptor. */
3974 apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
3975 pszOldBasename, pszNewBasename);
3976 if (!apszNewLines[i])
3977 goto rollback;
3978 pImage->Descriptor.aLines[line] = apszNewLines[i];
3979 /* Compose new name for the extent. */
3980 apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
3981 pszOldBasename, pszNewBasename);
3982 if (!apszNewName[i])
3983 goto rollback;
3984 /* Close the extent file. */
3985 vmdkFileClose(pImage, &pExtent->pFile, false);
3986 /* Rename the extent file. */
3987 rc = RTFileMove(pExtent->pszFullname, apszNewName[i], 0);
3988 if (RT_FAILURE(rc))
3989 goto rollback;
3990 /* Remember the old name. */
3991 apszOldName[i] = RTStrDup(pExtent->pszFullname);
3992 }
3993
3994 /* Make sure the descriptor gets written back. */
3995 pImage->Descriptor.fDirty = true;
3996 /* Release all old stuff and write back the descriptor. */
3997 vmdkFreeImage(pImage, false);
3998
3999 fImageFreed = true;
4000
4001 /* Last elements of new/old name arrays are intended for
4002 * storing descriptor's names.
4003 */
4004 apszNewName[cExtents] = RTPathFilename(pszFilename);
4005 /* Rename the descriptor file if it's separate. */
4006 if (!fEmbeddedDesc)
4007 {
4008 rc = RTFileMove(pImage->pszFilename, apszNewName[cExtents], 0);
4009 if (RT_FAILURE(rc))
4010 goto rollback;
4011 /* Save old name only if we may need to change it back. */
4012 apszOldName[cExtents] = RTStrDup(pszFilename);
4013 }
4014
4015 /* Update pImage with the new information. */
4016 pImage->pszFilename = pszFilename;
4017
4018 /* Open the new image. */
4019 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
4020 if (RT_SUCCESS(rc))
4021 goto out;
4022
4023rollback:
4024 /* Roll back all changes in case of failure. */
4025 if (RT_FAILURE(rc))
4026 {
4027 int rrc;
4028 if (!fImageFreed)
4029 {
4030 /*
4031 * Some extents may have been closed, close the rest. We will
4032 * re-open the whole thing later.
4033 */
4034 vmdkFreeImage(pImage, false);
4035 }
4036 /* Rename files back and free the memory. */
4037 for (i = 0; i < cExtents + 1; i++)
4038 {
4039 if (apszOldName[i])
4040 {
4041 rrc = RTFileMove(apszNewName[i], apszOldName[i], 0);
4042 AssertRC(rrc);
4043 RTStrFree(apszOldName[i]);
4044 }
4045 if (apszNewName[i])
4046 RTStrFree(apszNewName[i]);
4047 if (apszNewLines[i])
4048 RTStrFree(apszNewLines[i]);
4049 }
4050 /* Restore the old descriptor. */
4051 PVMDKFILE pFile;
4052 rrc = vmdkFileOpen(pImage, &pFile, pszOldDescName,
4053 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, false);
4054 AssertRC(rrc);
4055 if (fEmbeddedDesc)
4056 {
4057 ExtentCopy.pFile = pFile;
4058 pImage->pExtents = &ExtentCopy;
4059 }
4060 else
4061 pImage->pFile = pFile;
4062 pImage->Descriptor = DescriptorCopy;
4063 vmdkWriteDescriptor(pImage);
4064 vmdkFileClose(pImage, &pFile, false);
4065 RTStrFree(pszOldDescName);
4066 /* Re-open the image back. */
4067 rrc = vmdkOpenImage(pImage, pImage->uOpenFlags);
4068 AssertRC(rrc);
4069 }
4070
4071out:
4072 for (i = 0; i < DescriptorCopy.cLines; i++)
4073 if (DescriptorCopy.aLines[i])
4074 RTStrFree(DescriptorCopy.aLines[i]);
4075 if (apszOldName)
4076 RTMemTmpFree(apszOldName);
4077 if (apszNewName)
4078 RTMemTmpFree(apszNewName);
4079 if (apszNewLines)
4080 RTMemTmpFree(apszNewLines);
4081 LogFlowFunc(("returns %Rrc\n", rc));
4082 return rc;
4083}
4084
4085/** @copydoc VBOXHDDBACKEND::pfnClose */
4086static int vmdkClose(void *pBackendData, bool fDelete)
4087{
4088 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
4089 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4090 int rc = VINF_SUCCESS;
4091
4092 /* Freeing a never allocated image (e.g. because the open failed) is
4093 * not signalled as an error. After all nothing bad happens. */
4094 if (pImage)
4095 vmdkFreeImage(pImage, fDelete);
4096
4097 LogFlowFunc(("returns %Rrc\n", rc));
4098 return rc;
4099}
4100
4101/** @copydoc VBOXHDDBACKEND::pfnRead */
4102static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
4103 size_t cbToRead, size_t *pcbActuallyRead)
4104{
4105 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
4106 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4107 PVMDKEXTENT pExtent;
4108 uint64_t uSectorExtentRel;
4109 uint64_t uSectorExtentAbs;
4110 int rc;
4111
4112 Assert(pImage);
4113 Assert(uOffset % 512 == 0);
4114 Assert(cbToRead % 512 == 0);
4115
4116 if ( uOffset + cbToRead > pImage->cbSize
4117 || cbToRead == 0)
4118 {
4119 rc = VERR_INVALID_PARAMETER;
4120 goto out;
4121 }
4122
4123 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
4124 &pExtent, &uSectorExtentRel);
4125 if (RT_FAILURE(rc))
4126 goto out;
4127
4128 /* Check access permissions as defined in the extent descriptor. */
4129 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
4130 {
4131 rc = VERR_VDI_INVALID_STATE;
4132 goto out;
4133 }
4134
4135 /* Clip read range to remain in this extent. */
4136 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4137
4138 /* Handle the read according to the current extent type. */
4139 switch (pExtent->enmType)
4140 {
4141 case VMDKETYPE_HOSTED_SPARSE:
4142#ifdef VBOX_WITH_VMDK_ESX
4143 case VMDKETYPE_ESX_SPARSE:
4144#endif /* VBOX_WITH_VMDK_ESX */
4145 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
4146 &uSectorExtentAbs);
4147 if (RT_FAILURE(rc))
4148 goto out;
4149 /* Clip read range to at most the rest of the grain. */
4150 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
4151 Assert(!(cbToRead % 512));
4152 if (uSectorExtentAbs == 0)
4153 rc = VERR_VDI_BLOCK_FREE;
4154 else
4155 rc = vmdkFileReadAt(pExtent->pFile,
4156 VMDK_SECTOR2BYTE(uSectorExtentAbs),
4157 pvBuf, cbToRead, NULL);
4158 break;
4159 case VMDKETYPE_FLAT:
4160 rc = vmdkFileReadAt(pExtent->pFile,
4161 VMDK_SECTOR2BYTE(uSectorExtentRel),
4162 pvBuf, cbToRead, NULL);
4163 break;
4164 case VMDKETYPE_ZERO:
4165 memset(pvBuf, '\0', cbToRead);
4166 break;
4167 }
4168 *pcbActuallyRead = cbToRead;
4169
4170out:
4171 LogFlowFunc(("returns %Rrc\n", rc));
4172 return rc;
4173}
4174
4175/** @copydoc VBOXHDDBACKEND::pfnWrite */
4176static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
4177 size_t cbToWrite, size_t *pcbWriteProcess,
4178 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
4179{
4180 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
4181 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4182 PVMDKEXTENT pExtent;
4183 uint64_t uSectorExtentRel;
4184 uint64_t uSectorExtentAbs;
4185 int rc;
4186
4187 Assert(pImage);
4188 Assert(uOffset % 512 == 0);
4189 Assert(cbToWrite % 512 == 0);
4190
4191 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4192 {
4193 rc = VERR_VDI_IMAGE_READ_ONLY;
4194 goto out;
4195 }
4196
4197 if (cbToWrite == 0)
4198 {
4199 rc = VERR_INVALID_PARAMETER;
4200 goto out;
4201 }
4202
4203 /* No size check here, will do that later when the extent is located.
4204 * There are sparse images out there which according to the spec are
4205 * invalid, because the total size is not a multiple of the grain size.
4206 * Also for sparse images which are stitched together in odd ways (not at
4207 * grain boundaries, and with the nominal size not being a multiple of the
4208 * grain size), this would prevent writing to the last grain. */
4209
4210 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
4211 &pExtent, &uSectorExtentRel);
4212 if (RT_FAILURE(rc))
4213 goto out;
4214
4215 /* Check access permissions as defined in the extent descriptor. */
4216 if (pExtent->enmAccess != VMDKACCESS_READWRITE)
4217 {
4218 rc = VERR_VDI_INVALID_STATE;
4219 goto out;
4220 }
4221
4222 /* Handle the write according to the current extent type. */
4223 switch (pExtent->enmType)
4224 {
4225 case VMDKETYPE_HOSTED_SPARSE:
4226#ifdef VBOX_WITH_VMDK_ESX
4227 case VMDKETYPE_ESX_SPARSE:
4228#endif /* VBOX_WITH_VMDK_ESX */
4229 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
4230 &uSectorExtentAbs);
4231 if (RT_FAILURE(rc))
4232 goto out;
4233 /* Clip write range to at most the rest of the grain. */
4234 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
4235 if (uSectorExtentAbs == 0)
4236 {
4237 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
4238 {
4239 /* Full block write to a previously unallocated block.
4240 * Check if the caller wants to avoid this. */
4241 if (!(fWrite & VD_WRITE_NO_ALLOC))
4242 {
4243 /* Allocate GT and find out where to store the grain. */
4244 rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
4245 uSectorExtentRel, pvBuf, cbToWrite);
4246 }
4247 else
4248 rc = VERR_VDI_BLOCK_FREE;
4249 *pcbPreRead = 0;
4250 *pcbPostRead = 0;
4251 }
4252 else
4253 {
4254 /* Clip write range to remain in this extent. */
4255 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4256 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
4257 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
4258 rc = VERR_VDI_BLOCK_FREE;
4259 }
4260 }
4261 else
4262 rc = vmdkFileWriteAt(pExtent->pFile,
4263 VMDK_SECTOR2BYTE(uSectorExtentAbs),
4264 pvBuf, cbToWrite, NULL);
4265 break;
4266 case VMDKETYPE_FLAT:
4267 /* Clip write range to remain in this extent. */
4268 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4269 rc = vmdkFileWriteAt(pExtent->pFile,
4270 VMDK_SECTOR2BYTE(uSectorExtentRel),
4271 pvBuf, cbToWrite, NULL);
4272 break;
4273 case VMDKETYPE_ZERO:
4274 /* Clip write range to remain in this extent. */
4275 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4276 break;
4277 }
4278 if (pcbWriteProcess)
4279 *pcbWriteProcess = cbToWrite;
4280
4281out:
4282 LogFlowFunc(("returns %Rrc\n", rc));
4283 return rc;
4284}
4285
4286/** @copydoc VBOXHDDBACKEND::pfnFlush */
4287static int vmdkFlush(void *pBackendData)
4288{
4289 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4290 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4291 int rc;
4292
4293 Assert(pImage);
4294
4295 rc = vmdkFlushImage(pImage);
4296 LogFlowFunc(("returns %Rrc\n", rc));
4297 return rc;
4298}
4299
4300/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
4301static unsigned vmdkGetVersion(void *pBackendData)
4302{
4303 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4304 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4305
4306 Assert(pImage);
4307
4308 if (pImage)
4309 return VMDK_IMAGE_VERSION;
4310 else
4311 return 0;
4312}
4313
4314/** @copydoc VBOXHDDBACKEND::pfnGetImageType */
4315static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
4316{
4317 LogFlowFunc(("pBackendData=%#p penmImageType=%#p\n", pBackendData, penmImageType));
4318 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4319 int rc = VINF_SUCCESS;
4320
4321 Assert(pImage);
4322 Assert(penmImageType);
4323
4324 if (pImage && pImage->cExtents != 0)
4325 *penmImageType = pImage->enmImageType;
4326 else
4327 rc = VERR_VDI_NOT_OPENED;
4328
4329 LogFlowFunc(("returns %Rrc enmImageType=%u\n", rc, *penmImageType));
4330 return rc;
4331}
4332
4333/** @copydoc VBOXHDDBACKEND::pfnGetSize */
4334static uint64_t vmdkGetSize(void *pBackendData)
4335{
4336 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4337 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4338
4339 Assert(pImage);
4340
4341 if (pImage)
4342 return pImage->cbSize;
4343 else
4344 return 0;
4345}
4346
4347/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
4348static uint64_t vmdkGetFileSize(void *pBackendData)
4349{
4350 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4351 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4352 uint64_t cb = 0;
4353
4354 Assert(pImage);
4355
4356 if (pImage)
4357 {
4358 uint64_t cbFile;
4359 if (pImage->pFile != NULL)
4360 {
4361 int rc = vmdkFileGetSize(pImage->pFile, &cbFile);
4362 if (RT_SUCCESS(rc))
4363 cb += cbFile;
4364 for (unsigned i = 0; i <= pImage->cExtents; i++)
4365 {
4366 rc = vmdkFileGetSize(pImage->pFile, &cbFile);
4367 if (RT_SUCCESS(rc))
4368 cb += cbFile;
4369 }
4370 }
4371 }
4372
4373 LogFlowFunc(("returns %lld\n", cb));
4374 return cb;
4375}
4376
4377/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
4378static int vmdkGetPCHSGeometry(void *pBackendData,
4379 PPDMMEDIAGEOMETRY pPCHSGeometry)
4380{
4381 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
4382 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4383 int rc;
4384
4385 Assert(pImage);
4386
4387 if (pImage)
4388 {
4389 if (pImage->PCHSGeometry.cCylinders)
4390 {
4391 *pPCHSGeometry = pImage->PCHSGeometry;
4392 rc = VINF_SUCCESS;
4393 }
4394 else
4395 rc = VERR_VDI_GEOMETRY_NOT_SET;
4396 }
4397 else
4398 rc = VERR_VDI_NOT_OPENED;
4399
4400 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4401 return rc;
4402}
4403
4404/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
4405static int vmdkSetPCHSGeometry(void *pBackendData,
4406 PCPDMMEDIAGEOMETRY pPCHSGeometry)
4407{
4408 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
4409 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4410 int rc;
4411
4412 Assert(pImage);
4413
4414 if (pImage)
4415 {
4416 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4417 {
4418 rc = VERR_VDI_IMAGE_READ_ONLY;
4419 goto out;
4420 }
4421 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
4422 if (RT_FAILURE(rc))
4423 goto out;
4424
4425 pImage->PCHSGeometry = *pPCHSGeometry;
4426 rc = VINF_SUCCESS;
4427 }
4428 else
4429 rc = VERR_VDI_NOT_OPENED;
4430
4431out:
4432 LogFlowFunc(("returns %Rrc\n", rc));
4433 return rc;
4434}
4435
4436/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
4437static int vmdkGetLCHSGeometry(void *pBackendData,
4438 PPDMMEDIAGEOMETRY pLCHSGeometry)
4439{
4440 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
4441 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4442 int rc;
4443
4444 Assert(pImage);
4445
4446 if (pImage)
4447 {
4448 if (pImage->LCHSGeometry.cCylinders)
4449 {
4450 *pLCHSGeometry = pImage->LCHSGeometry;
4451 rc = VINF_SUCCESS;
4452 }
4453 else
4454 rc = VERR_VDI_GEOMETRY_NOT_SET;
4455 }
4456 else
4457 rc = VERR_VDI_NOT_OPENED;
4458
4459 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4460 return rc;
4461}
4462
4463/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
4464static int vmdkSetLCHSGeometry(void *pBackendData,
4465 PCPDMMEDIAGEOMETRY pLCHSGeometry)
4466{
4467 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
4468 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4469 int rc;
4470
4471 Assert(pImage);
4472
4473 if (pImage)
4474 {
4475 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4476 {
4477 rc = VERR_VDI_IMAGE_READ_ONLY;
4478 goto out;
4479 }
4480 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
4481 if (RT_FAILURE(rc))
4482 goto out;
4483
4484 pImage->LCHSGeometry = *pLCHSGeometry;
4485 rc = VINF_SUCCESS;
4486 }
4487 else
4488 rc = VERR_VDI_NOT_OPENED;
4489
4490out:
4491 LogFlowFunc(("returns %Rrc\n", rc));
4492 return rc;
4493}
4494
4495/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
4496static unsigned vmdkGetImageFlags(void *pBackendData)
4497{
4498 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4499 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4500 unsigned uImageFlags;
4501
4502 Assert(pImage);
4503
4504 if (pImage)
4505 uImageFlags = pImage->uImageFlags;
4506 else
4507 uImageFlags = 0;
4508
4509 LogFlowFunc(("returns %#x\n", uImageFlags));
4510 return uImageFlags;
4511}
4512
4513/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
4514static unsigned vmdkGetOpenFlags(void *pBackendData)
4515{
4516 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
4517 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4518 unsigned uOpenFlags;
4519
4520 Assert(pImage);
4521
4522 if (pImage)
4523 uOpenFlags = pImage->uOpenFlags;
4524 else
4525 uOpenFlags = 0;
4526
4527 LogFlowFunc(("returns %#x\n", uOpenFlags));
4528 return uOpenFlags;
4529}
4530
4531/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
4532static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
4533{
4534 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
4535 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4536 int rc;
4537
4538 /* Image must be opened and the new flags must be valid. Just readonly flag
4539 * is supported. */
4540 if (!pImage || uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_ASYNC_IO))
4541 {
4542 rc = VERR_INVALID_PARAMETER;
4543 goto out;
4544 }
4545
4546 /* Implement this operation via reopening the image. */
4547 vmdkFreeImage(pImage, false);
4548 rc = vmdkOpenImage(pImage, uOpenFlags);
4549
4550out:
4551 LogFlowFunc(("returns %Rrc\n", rc));
4552 return rc;
4553}
4554
4555/** @copydoc VBOXHDDBACKEND::pfnGetComment */
4556static int vmdkGetComment(void *pBackendData, char *pszComment,
4557 size_t cbComment)
4558{
4559 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
4560 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4561 int rc;
4562
4563 Assert(pImage);
4564
4565 if (pImage)
4566 {
4567 const char *pszCommentEncoded = NULL;
4568 rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
4569 "ddb.comment", &pszCommentEncoded);
4570 if (rc == VERR_VDI_VALUE_NOT_FOUND)
4571 pszCommentEncoded = NULL;
4572 else if (RT_FAILURE(rc))
4573 goto out;
4574
4575 if (pszComment)
4576 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
4577 else
4578 rc = VINF_SUCCESS;
4579 if (pszCommentEncoded)
4580 RTStrFree((char *)(void *)pszCommentEncoded);
4581 }
4582 else
4583 rc = VERR_VDI_NOT_OPENED;
4584
4585out:
4586 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
4587 return rc;
4588}
4589
4590/** @copydoc VBOXHDDBACKEND::pfnSetComment */
4591static int vmdkSetComment(void *pBackendData, const char *pszComment)
4592{
4593 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
4594 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4595 int rc;
4596
4597 Assert(pImage);
4598
4599 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
4600 {
4601 rc = VERR_VDI_IMAGE_READ_ONLY;
4602 goto out;
4603 }
4604
4605 if (pImage)
4606 rc = vmdkSetImageComment(pImage, pszComment);
4607 else
4608 rc = VERR_VDI_NOT_OPENED;
4609
4610out:
4611 LogFlowFunc(("returns %Rrc\n", rc));
4612 return rc;
4613}
4614
4615/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
4616static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
4617{
4618 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4619 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4620 int rc;
4621
4622 Assert(pImage);
4623
4624 if (pImage)
4625 {
4626 *pUuid = pImage->ImageUuid;
4627 rc = VINF_SUCCESS;
4628 }
4629 else
4630 rc = VERR_VDI_NOT_OPENED;
4631
4632 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4633 return rc;
4634}
4635
4636/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
4637static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
4638{
4639 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4640 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4641 int rc;
4642
4643 LogFlowFunc(("%RTuuid\n", pUuid));
4644 Assert(pImage);
4645
4646 if (pImage)
4647 {
4648 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4649 {
4650 pImage->ImageUuid = *pUuid;
4651 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4652 VMDK_DDB_IMAGE_UUID, pUuid);
4653 if (RT_FAILURE(rc))
4654 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
4655 rc = VINF_SUCCESS;
4656 }
4657 else
4658 rc = VERR_VDI_IMAGE_READ_ONLY;
4659 }
4660 else
4661 rc = VERR_VDI_NOT_OPENED;
4662
4663 LogFlowFunc(("returns %Rrc\n", rc));
4664 return rc;
4665}
4666
4667/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
4668static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
4669{
4670 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4671 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4672 int rc;
4673
4674 Assert(pImage);
4675
4676 if (pImage)
4677 {
4678 *pUuid = pImage->ModificationUuid;
4679 rc = VINF_SUCCESS;
4680 }
4681 else
4682 rc = VERR_VDI_NOT_OPENED;
4683
4684 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4685 return rc;
4686}
4687
4688/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
4689static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
4690{
4691 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4692 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4693 int rc;
4694
4695 Assert(pImage);
4696
4697 if (pImage)
4698 {
4699 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4700 {
4701 pImage->ModificationUuid = *pUuid;
4702 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4703 VMDK_DDB_MODIFICATION_UUID, pUuid);
4704 if (RT_FAILURE(rc))
4705 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
4706 rc = VINF_SUCCESS;
4707 }
4708 else
4709 rc = VERR_VDI_IMAGE_READ_ONLY;
4710 }
4711 else
4712 rc = VERR_VDI_NOT_OPENED;
4713
4714 LogFlowFunc(("returns %Rrc\n", rc));
4715 return rc;
4716}
4717
4718/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
4719static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
4720{
4721 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4722 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4723 int rc;
4724
4725 Assert(pImage);
4726
4727 if (pImage)
4728 {
4729 *pUuid = pImage->ParentUuid;
4730 rc = VINF_SUCCESS;
4731 }
4732 else
4733 rc = VERR_VDI_NOT_OPENED;
4734
4735 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4736 return rc;
4737}
4738
4739/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
4740static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
4741{
4742 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4743 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4744 int rc;
4745
4746 Assert(pImage);
4747
4748 if (pImage)
4749 {
4750 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4751 {
4752 pImage->ParentUuid = *pUuid;
4753 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4754 VMDK_DDB_PARENT_UUID, pUuid);
4755 if (RT_FAILURE(rc))
4756 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
4757 rc = VINF_SUCCESS;
4758 }
4759 else
4760 rc = VERR_VDI_IMAGE_READ_ONLY;
4761 }
4762 else
4763 rc = VERR_VDI_NOT_OPENED;
4764
4765 LogFlowFunc(("returns %Rrc\n", rc));
4766 return rc;
4767}
4768
4769/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
4770static int vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
4771{
4772 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
4773 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4774 int rc;
4775
4776 Assert(pImage);
4777
4778 if (pImage)
4779 {
4780 *pUuid = pImage->ParentModificationUuid;
4781 rc = VINF_SUCCESS;
4782 }
4783 else
4784 rc = VERR_VDI_NOT_OPENED;
4785
4786 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
4787 return rc;
4788}
4789
4790/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
4791static int vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
4792{
4793 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
4794 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4795 int rc;
4796
4797 Assert(pImage);
4798
4799 if (pImage)
4800 {
4801 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
4802 {
4803 pImage->ParentModificationUuid = *pUuid;
4804 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
4805 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
4806 if (RT_FAILURE(rc))
4807 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
4808 rc = VINF_SUCCESS;
4809 }
4810 else
4811 rc = VERR_VDI_IMAGE_READ_ONLY;
4812 }
4813 else
4814 rc = VERR_VDI_NOT_OPENED;
4815
4816 LogFlowFunc(("returns %Rrc\n", rc));
4817 return rc;
4818}
4819
4820/** @copydoc VBOXHDDBACKEND::pfnDump */
4821static void vmdkDump(void *pBackendData)
4822{
4823 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
4824
4825 Assert(pImage);
4826 if (pImage)
4827 {
4828 RTLogPrintf("Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
4829 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
4830 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
4831 VMDK_BYTE2SECTOR(pImage->cbSize));
4832 RTLogPrintf("Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
4833 RTLogPrintf("Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
4834 RTLogPrintf("Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
4835 RTLogPrintf("Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
4836 }
4837}
4838
4839
4840static int vmdkGetTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
4841{
4842 int rc = VERR_NOT_IMPLEMENTED;
4843 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4844 return rc;
4845}
4846
4847static int vmdkGetParentTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
4848{
4849 int rc = VERR_NOT_IMPLEMENTED;
4850 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4851 return rc;
4852}
4853
4854static int vmdkSetParentTimeStamp(void *pvBackendData, PCRTTIMESPEC pTimeStamp)
4855{
4856 int rc = VERR_NOT_IMPLEMENTED;
4857 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4858 return rc;
4859}
4860
4861static int vmdkGetParentFilename(void *pvBackendData, char **ppszParentFilename)
4862{
4863 int rc = VERR_NOT_IMPLEMENTED;
4864 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4865 return rc;
4866}
4867
4868static int vmdkSetParentFilename(void *pvBackendData, const char *pszParentFilename)
4869{
4870 int rc = VERR_NOT_IMPLEMENTED;
4871 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
4872 return rc;
4873}
4874
4875static bool vmdkIsAsyncIOSupported(void *pvBackendData)
4876{
4877 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
4878 bool fAsyncIOSupported = false;
4879
4880 if (pImage)
4881 {
4882 /* We only support async I/O support if the image only consists of FLAT or ZERO extents. */
4883 fAsyncIOSupported = true;
4884 for (unsigned i = 0; i < pImage->cExtents; i++)
4885 {
4886 if ( (pImage->pExtents[i].enmType != VMDKETYPE_FLAT)
4887 && (pImage->pExtents[i].enmType != VMDKETYPE_ZERO))
4888 {
4889 fAsyncIOSupported = false;
4890 break; /* Stop search */
4891 }
4892 }
4893 }
4894
4895 return fAsyncIOSupported;
4896}
4897
4898static int vmdkAsyncRead(void *pvBackendData, uint64_t uOffset, size_t cbRead,
4899 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
4900{
4901 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
4902 PVMDKEXTENT pExtent;
4903 int rc = VINF_SUCCESS;
4904 unsigned cTasksToSubmit = 0;
4905 PPDMDATASEG paSegCurrent = paSeg;
4906 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
4907 unsigned uOffsetInCurrentSegment = 0;
4908
4909 Assert(pImage);
4910 Assert(uOffset % 512 == 0);
4911 Assert(cbRead % 512 == 0);
4912
4913 if ( uOffset + cbRead > pImage->cbSize
4914 || cbRead == 0)
4915 {
4916 rc = VERR_INVALID_PARAMETER;
4917 goto out;
4918 }
4919
4920 while (cbRead && cSeg)
4921 {
4922 unsigned cbToRead;
4923 uint64_t uSectorExtentRel;
4924
4925 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
4926 &pExtent, &uSectorExtentRel);
4927 if (RT_FAILURE(rc))
4928 goto out;
4929
4930 /* Check access permissions as defined in the extent descriptor. */
4931 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
4932 {
4933 rc = VERR_VDI_INVALID_STATE;
4934 goto out;
4935 }
4936
4937 /* Clip read range to remain in this extent. */
4938 cbToRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
4939 /* Clip read range to remain into current data segment. */
4940 cbToRead = RT_MIN(cbToRead, cbLeftInCurrentSegment);
4941
4942 switch (pExtent->enmType)
4943 {
4944 case VMDKETYPE_FLAT:
4945 {
4946 /* Setup new task. */
4947 void *pTask;
4948 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareRead(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
4949 VMDK_SECTOR2BYTE(uSectorExtentRel),
4950 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
4951 cbToRead, &pTask);
4952 if (RT_FAILURE(rc))
4953 {
4954 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
4955 goto out;
4956 }
4957
4958 /* Check for enough room first. */
4959 if (cTasksToSubmit >= pImage->cTask)
4960 {
4961 /* We reached maximum, resize array. Try to realloc memory first. */
4962 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
4963
4964 if (!apTaskNew)
4965 {
4966 /* We failed. Allocate completely new. */
4967 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
4968 if (!apTaskNew)
4969 {
4970 /* Damn, we are out of memory. */
4971 rc = VERR_NO_MEMORY;
4972 goto out;
4973 }
4974
4975 /* Copy task handles over. */
4976 for (unsigned i = 0; i < cTasksToSubmit; i++)
4977 apTaskNew[i] = pImage->apTask[i];
4978
4979 /* Free old memory. */
4980 RTMemFree(pImage->apTask);
4981 }
4982
4983 pImage->cTask = cTasksToSubmit + 10;
4984 pImage->apTask = apTaskNew;
4985 }
4986
4987 pImage->apTask[cTasksToSubmit] = pTask;
4988 cTasksToSubmit++;
4989 break;
4990 }
4991 case VMDKETYPE_ZERO:
4992 memset((uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment, 0, cbToRead);
4993 break;
4994 default:
4995 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
4996 }
4997
4998 cbRead -= cbToRead;
4999 uOffset += cbToRead;
5000 cbLeftInCurrentSegment -= cbToRead;
5001 uOffsetInCurrentSegment += cbToRead;
5002 /* Go to next extent if there is no space left in current one. */
5003 if (!cbLeftInCurrentSegment)
5004 {
5005 uOffsetInCurrentSegment = 0;
5006 paSegCurrent++;
5007 cSeg--;
5008 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5009 }
5010 }
5011
5012 AssertMsg((cSeg >= 0) && (cbRead == 0), ("No segment left but there is still data to read\n"));
5013
5014 if (cTasksToSubmit == 0)
5015 {
5016 /* The request was completely in a ZERO extent nothing to do. */
5017 rc = VINF_VDI_ASYNC_IO_FINISHED;
5018 }
5019 else
5020 {
5021 /* Submit tasks. */
5022 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5023 pImage->apTask, cTasksToSubmit,
5024 NULL, pvUser,
5025 NULL /* Nothing required after read. */);
5026 AssertMsg(RT_SUCCESS(rc), ("Failed to enqueue tasks rc=%Rrc\n", rc));
5027 }
5028
5029out:
5030 LogFlowFunc(("returns %Rrc\n", rc));
5031 return rc;
5032}
5033
5034static int vmdkAsyncWrite(void *pvBackendData, uint64_t uOffset, size_t cbWrite,
5035 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
5036{
5037 PVMDKIMAGE pImage = (PVMDKIMAGE)pvBackendData;
5038 PVMDKEXTENT pExtent;
5039 int rc = VINF_SUCCESS;
5040 unsigned cTasksToSubmit = 0;
5041 PPDMDATASEG paSegCurrent = paSeg;
5042 unsigned cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5043 unsigned uOffsetInCurrentSegment = 0;
5044
5045 Assert(pImage);
5046 Assert(uOffset % 512 == 0);
5047 Assert(cbWrite % 512 == 0);
5048
5049 if ( uOffset + cbWrite > pImage->cbSize
5050 || cbWrite == 0)
5051 {
5052 rc = VERR_INVALID_PARAMETER;
5053 goto out;
5054 }
5055
5056 while (cbWrite && cSeg)
5057 {
5058 unsigned cbToWrite;
5059 uint64_t uSectorExtentRel;
5060
5061 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
5062 &pExtent, &uSectorExtentRel);
5063 if (RT_FAILURE(rc))
5064 goto out;
5065
5066 /* Check access permissions as defined in the extent descriptor. */
5067 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
5068 {
5069 rc = VERR_VDI_INVALID_STATE;
5070 goto out;
5071 }
5072
5073 /* Clip write range to remain in this extent. */
5074 cbToWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
5075 /* Clip write range to remain into current data segment. */
5076 cbToWrite = RT_MIN(cbToWrite, cbLeftInCurrentSegment);
5077
5078 switch (pExtent->enmType)
5079 {
5080 case VMDKETYPE_FLAT:
5081 {
5082 /* Setup new task. */
5083 void *pTask;
5084 rc = pImage->pInterfaceAsyncIOCallbacks->pfnPrepareWrite(pImage->pInterfaceAsyncIO->pvUser, pExtent->pFile->pStorage,
5085 VMDK_SECTOR2BYTE(uSectorExtentRel),
5086 (uint8_t *)paSegCurrent->pvSeg + uOffsetInCurrentSegment,
5087 cbToWrite, &pTask);
5088 if (RT_FAILURE(rc))
5089 {
5090 AssertMsgFailed(("Preparing read failed rc=%Rrc\n", rc));
5091 goto out;
5092 }
5093
5094 /* Check for enough room first. */
5095 if (cTasksToSubmit >= pImage->cTask)
5096 {
5097 /* We reached maximum, resize array. Try to realloc memory first. */
5098 void **apTaskNew = (void **)RTMemRealloc(pImage->apTask, (cTasksToSubmit + 10)*sizeof(void *));
5099
5100 if (!apTaskNew)
5101 {
5102 /* We failed. Allocate completely new. */
5103 apTaskNew = (void **)RTMemAllocZ((cTasksToSubmit + 10)* sizeof(void *));
5104 if (!apTaskNew)
5105 {
5106 /* Damn, we are out of memory. */
5107 rc = VERR_NO_MEMORY;
5108 goto out;
5109 }
5110
5111 /* Copy task handles over. */
5112 for (unsigned i = 0; i < cTasksToSubmit; i++)
5113 apTaskNew[i] = pImage->apTask[i];
5114
5115 /* Free old memory. */
5116 RTMemFree(pImage->apTask);
5117 }
5118
5119 pImage->cTask = cTasksToSubmit + 10;
5120 pImage->apTask = apTaskNew;
5121 }
5122
5123 pImage->apTask[cTasksToSubmit] = pTask;
5124 cTasksToSubmit++;
5125 break;
5126 }
5127 case VMDKETYPE_ZERO:
5128 /* Nothing left to do. */
5129 break;
5130 default:
5131 AssertMsgFailed(("Unsupported extent type %u\n", pExtent->enmType));
5132 }
5133
5134 cbWrite -= cbToWrite;
5135 uOffset += cbToWrite;
5136 cbLeftInCurrentSegment -= cbToWrite;
5137 uOffsetInCurrentSegment += cbToWrite;
5138 /* Go to next extent if there is no space left in current one. */
5139 if (!cbLeftInCurrentSegment)
5140 {
5141 uOffsetInCurrentSegment = 0;
5142 paSegCurrent++;
5143 cSeg--;
5144 cbLeftInCurrentSegment = paSegCurrent->cbSeg;
5145 }
5146 }
5147
5148 AssertMsg((cSeg >= 0) && (cbWrite == 0), ("No segment left but there is still data to read\n"));
5149
5150 if (cTasksToSubmit == 0)
5151 {
5152 /* The request was completely in a ZERO extent nothing to do. */
5153 rc = VINF_VDI_ASYNC_IO_FINISHED;
5154 }
5155 else
5156 {
5157 /* Submit tasks. */
5158 rc = pImage->pInterfaceAsyncIOCallbacks->pfnTasksSubmit(pImage->pInterfaceAsyncIO->pvUser,
5159 pImage->apTask, cTasksToSubmit,
5160 NULL, pvUser,
5161 NULL /* Nothing required after read. */);
5162 AssertMsg(RT_SUCCESS(rc), ("Failed to enqueue tasks rc=%Rrc\n", rc));
5163 }
5164
5165out:
5166 LogFlowFunc(("returns %Rrc\n", rc));
5167 return rc;
5168
5169}
5170
5171
5172VBOXHDDBACKEND g_VmdkBackend =
5173{
5174 /* pszBackendName */
5175 "VMDK",
5176 /* cbSize */
5177 sizeof(VBOXHDDBACKEND),
5178 /* uBackendCaps */
5179 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
5180 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE |VD_CAP_ASYNC,
5181 /* papszFileExtensions */
5182 s_apszVmdkFileExtensions,
5183 /* pfnCheckIfValid */
5184 vmdkCheckIfValid,
5185 /* pfnOpen */
5186 vmdkOpen,
5187 /* pfnCreate */
5188 vmdkCreate,
5189 /* pfnRename */
5190 vmdkRename,
5191 /* pfnClose */
5192 vmdkClose,
5193 /* pfnRead */
5194 vmdkRead,
5195 /* pfnWrite */
5196 vmdkWrite,
5197 /* pfnFlush */
5198 vmdkFlush,
5199 /* pfnGetVersion */
5200 vmdkGetVersion,
5201 /* pfnGetImageType */
5202 vmdkGetImageType,
5203 /* pfnGetSize */
5204 vmdkGetSize,
5205 /* pfnGetFileSize */
5206 vmdkGetFileSize,
5207 /* pfnGetPCHSGeometry */
5208 vmdkGetPCHSGeometry,
5209 /* pfnSetPCHSGeometry */
5210 vmdkSetPCHSGeometry,
5211 /* pfnGetLCHSGeometry */
5212 vmdkGetLCHSGeometry,
5213 /* pfnSetLCHSGeometry */
5214 vmdkSetLCHSGeometry,
5215 /* pfnGetImageFlags */
5216 vmdkGetImageFlags,
5217 /* pfnGetOpenFlags */
5218 vmdkGetOpenFlags,
5219 /* pfnSetOpenFlags */
5220 vmdkSetOpenFlags,
5221 /* pfnGetComment */
5222 vmdkGetComment,
5223 /* pfnSetComment */
5224 vmdkSetComment,
5225 /* pfnGetUuid */
5226 vmdkGetUuid,
5227 /* pfnSetUuid */
5228 vmdkSetUuid,
5229 /* pfnGetModificationUuid */
5230 vmdkGetModificationUuid,
5231 /* pfnSetModificationUuid */
5232 vmdkSetModificationUuid,
5233 /* pfnGetParentUuid */
5234 vmdkGetParentUuid,
5235 /* pfnSetParentUuid */
5236 vmdkSetParentUuid,
5237 /* pfnGetParentModificationUuid */
5238 vmdkGetParentModificationUuid,
5239 /* pfnSetParentModificationUuid */
5240 vmdkSetParentModificationUuid,
5241 /* pfnDump */
5242 vmdkDump,
5243 /* pfnGetTimeStamp */
5244 vmdkGetTimeStamp,
5245 /* pfnGetParentTimeStamp */
5246 vmdkGetParentTimeStamp,
5247 /* pfnSetParentTimeStamp */
5248 vmdkSetParentTimeStamp,
5249 /* pfnGetParentFilename */
5250 vmdkGetParentFilename,
5251 /* pfnSetParentFilename */
5252 vmdkSetParentFilename,
5253 /* pfnIsAsyncIOSupported */
5254 vmdkIsAsyncIOSupported,
5255 /* pfnAsyncRead */
5256 vmdkAsyncRead,
5257 /* pfnAsyncWrite */
5258 vmdkAsyncWrite
5259};
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