VirtualBox

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

Last change on this file since 5101 was 5101, checked in by vboxsync, 17 years ago

Introduce a new harddisk object which supports virtual harddisk through plugins

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 128.8 KB
Line 
1/** $Id: VmdkHDDCore.cpp 5101 2007-09-28 16:13:06Z vboxsync $ */
2/** @file
3 * VMDK Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD_VMDK
22#include "VBoxHDD-newInternal.h"
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <iprt/assert.h>
27#include <iprt/alloc.h>
28#include <iprt/uuid.h>
29#include <iprt/file.h>
30#include <iprt/path.h>
31#include <iprt/string.h>
32#include <iprt/rand.h>
33
34
35/*******************************************************************************
36* Constants And Macros, Structures and Typedefs *
37*******************************************************************************/
38
39/**
40 * Magic number for hosted images created by VMware Workstation 4, VMware
41 * Workstation 5, VMware Server or VMware Player.
42 */
43#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
44
45/** VMDK hosted sparse extent header. */
46#pragma pack(1)
47typedef struct SparseExtentHeader
48{
49 uint32_t magicNumber;
50 uint32_t version;
51 uint32_t flags;
52 uint64_t capacity;
53 uint64_t grainSize;
54 uint64_t descriptorOffset;
55 uint64_t descriptorSize;
56 uint32_t numGTEsPerGT;
57 uint64_t rgdOffset;
58 uint64_t gdOffset;
59 uint64_t overHead;
60 bool uncleanShutdown;
61 char singleEndLineChar;
62 char nonEndLineChar;
63 char doubleEndLineChar1;
64 char doubleEndLineChar2;
65 uint8_t pad[435];
66} SparseExtentHeader;
67#pragma pack()
68
69
70#ifdef VBOX_WITH_VMDK_ESX
71
72/** @todo the ESX code is not tested, not used, and lacks error messages. */
73
74/**
75 * Magic number for images created by VMware GSX Server 3 or ESX Server 3.
76 */
77#define VMDK_ESX_SPARSE_MAGICNUMBER 0x44574f43 /* 'C' 'O' 'W' 'D' */
78
79#pragma pack(1)
80typedef struct COWDisk_Header
81{
82 uint32_t magicNumber;
83 uint32_t version;
84 uint32_t flags;
85 uint32_t numSectors;
86 uint32_t grainSize;
87 uint32_t gdOffset;
88 uint32_t numGDEntries;
89 uint32_t freeSector;
90 /* The spec incompletely documents quite a few further fields, but states
91 * that they are not used by the current format. Replace them by padding. */
92 char reserved1[1604];
93 uint32_t savedGeneration;
94 char reserved2[8];
95 uint32_t uncleanShutdown;
96 char padding[396];
97} COWDisk_Header;
98#pragma pack()
99#endif /* VBOX_WITH_VMDK_ESX */
100
101
102/** Convert sector number/size to byte offset/size. */
103#define VMDK_SECTOR2BYTE(u) ((u) << 9)
104
105/** Convert byte offset/size to sector number/size. */
106#define VMDK_BYTE2SECTOR(u) ((u) >> 9)
107
108/**
109 * VMDK extent type.
110 */
111typedef enum VMDKETYPE
112{
113 /** Hosted sparse extent. */
114 VMDKETYPE_HOSTED_SPARSE = 1,
115 /** Flat extent. */
116 VMDKETYPE_FLAT,
117 /** Zero extent. */
118 VMDKETYPE_ZERO
119#ifdef VBOX_WITH_VMDK_ESX
120 ,
121 /** ESX sparse extent. */
122 VMDKETYPE_ESX_SPARSE
123#endif /* VBOX_WITH_VMDK_ESX */
124} VMDKETYPE, *PVMDKETYPE;
125
126/**
127 * VMDK access type for a extent.
128 */
129typedef enum VMDKACCESS
130{
131 /** No access allowed. */
132 VMDKACCESS_NOACCESS = 0,
133 /** Read-only access. */
134 VMDKACCESS_READONLY,
135 /** Read-write access. */
136 VMDKACCESS_READWRITE
137} VMDKACCESS, *PVMDKACCESS;
138
139/**
140 * VMDK extent data structure.
141 */
142typedef struct VMDKEXTENT
143{
144 /** File handle. */
145 RTFILE File;
146 /** Base name of the image extent. */
147 const char *pszBasename;
148 /** Full name of the image extent. */
149 const char *pszFullname;
150 /** Number of sectors in this extent. */
151 uint64_t cSectors;
152 /** Number of sectors per block (grain in VMDK speak). */
153 uint64_t cSectorsPerGrain;
154 /** Starting sector number of descriptor. */
155 uint64_t uDescriptorSector;
156 /** Size of descriptor in sectors. */
157 uint64_t cDescriptorSectors;
158 /** Starting sector number of grain directory. */
159 uint64_t uSectorGD;
160 /** Starting sector number of redundant grain directory. */
161 uint64_t uSectorRGD;
162 /** Total number of metadata sectors. */
163 uint64_t cOverheadSectors;
164 /** Nominal size (i.e. as described by the descriptor) of this extent. */
165 uint64_t cNominalSectors;
166 /** Sector offset (i.e. as described by the descriptor) of this extent. */
167 uint64_t uSectorOffset;
168 /** Number of entries in a grain table. */
169 uint32_t cGTEntries;
170 /** Number of sectors reachable via a grain directory entry. */
171 uint32_t cSectorsPerGDE;
172 /** Number of entries in the grain directory. */
173 uint32_t cGDEntries;
174 /** Pointer to the next free sector. Legacy information. Do not use. */
175 uint32_t uFreeSector;
176 /** Number of this extent in the list of images. */
177 uint32_t uExtent;
178 /** Pointer to the descriptor (NULL if no descriptor in this extent). */
179 char *pDescData;
180 /** Pointer to the grain directory. */
181 uint32_t *pGD;
182 /** Pointer to the redundant grain directory. */
183 uint32_t *pRGD;
184 /** Type of this extent. */
185 VMDKETYPE enmType;
186 /** Access to this extent. */
187 VMDKACCESS enmAccess;
188 /** Flag whether this extent is marked as unclean. */
189 bool fUncleanShutdown;
190 /** Flag whether the metadata in the extent header needs to be updated. */
191 bool fMetaDirty;
192 /** Reference to the image in which this extent is used. Do not use this
193 * on a regular basis to avoid passing pImage references to functions
194 * explicitly. */
195 struct VMDKIMAGE *pImage;
196} VMDKEXTENT, *PVMDKEXTENT;
197
198/**
199 * Grain table cache size. Allocated per image.
200 */
201#define VMDK_GT_CACHE_SIZE 256
202
203/**
204 * Grain table block size. Smaller than an actual grain table block to allow
205 * more grain table blocks to be cached without having to allocate excessive
206 * amounts of memory for the cache.
207 */
208#define VMDK_GT_CACHELINE_SIZE 128
209
210
211/**
212 * Maximum number of lines in a descriptor file. Not worth the effort of
213 * making it variable. Descriptor files are generally very short (~20 lines).
214 */
215#define VMDK_DESCRIPTOR_LINES_MAX 100U
216
217/**
218 * Parsed descriptor information. Allows easy access and update of the
219 * descriptor (whether separate file or not). Free form text files suck.
220 */
221typedef struct VMDKDESCRIPTOR
222{
223 /** Line number of first entry of the disk descriptor. */
224 unsigned uFirstDesc;
225 /** Line number of first entry in the extent description. */
226 unsigned uFirstExtent;
227 /** Line number of first disk database entry. */
228 unsigned uFirstDDB;
229 /** Total number of lines. */
230 unsigned cLines;
231 /** Total amount of memory available for the descriptor. */
232 size_t cbDescAlloc;
233 /** Set if descriptor has been changed and not yet written to disk. */
234 bool fDirty;
235 /** Array of pointers to the data in the descriptor. */
236 char *aLines[VMDK_DESCRIPTOR_LINES_MAX];
237 /** Array of line indices pointing to the next non-comment line. */
238 unsigned aNextLines[VMDK_DESCRIPTOR_LINES_MAX];
239} VMDKDESCRIPTOR, *PVMDKDESCRIPTOR;
240
241
242/**
243 * Cache entry for translating extent/sector to a sector number in that
244 * extent.
245 */
246typedef struct VMDKGTCACHEENTRY
247{
248 /** Extent number for which this entry is valid. */
249 uint32_t uExtent;
250 /** GT data block number. */
251 uint64_t uGTBlock;
252 /** Data part of the cache entry. */
253 uint32_t aGTData[VMDK_GT_CACHELINE_SIZE];
254} VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY;
255
256/**
257 * Cache data structure for blocks of grain table entries. For now this is a
258 * fixed size direct mapping cache, but this should be adapted to the size of
259 * the sparse image and maybe converted to a set-associative cache. The
260 * implementation below implements a write-through cache with write allocate.
261 */
262typedef struct VMDKGTCACHE
263{
264 /** Cache entries. */
265 VMDKGTCACHEENTRY aGTCache[VMDK_GT_CACHE_SIZE];
266 /** Number of cache entries (currently unused). */
267 unsigned cEntries;
268} VMDKGTCACHE, *PVMDKGTCACHE;
269
270/**
271 * Complete VMDK image data structure. Mainly a collection of extents and a few
272 * extra global data fields.
273 */
274typedef struct VMDKIMAGE
275{
276 PVMDKEXTENT pExtents;
277 unsigned cExtents;
278
279 /** Base image name. */
280 const char *pszFilename;
281 /** Descriptor file if applicable. */
282 RTFILE File;
283
284 /** Error callback. */
285 PFNVDERROR pfnError;
286 /** Opaque data for error callback. */
287 void *pvErrorUser;
288
289 /** Open flags passed by VBoxHD layer. */
290 unsigned uOpenFlags;
291 /** Image type. */
292 VDIMAGETYPE enmImageType;
293 /** Image flags defined during creation or determined during open. */
294 unsigned uImageFlags;
295 /** Total size of the image. */
296 uint64_t cbSize;
297 /** BIOS translation mode. */
298 PDMBIOSTRANSLATION enmTranslation;
299 /** Physical geometry of this image, cylinders. */
300 uint32_t cCylinders;
301 /** Physical geometry of this image, heads. */
302 uint32_t cHeads;
303 /** Physical geometry of this image, sectors. */
304 uint32_t cSectors;
305 /** Image UUID. */
306 RTUUID ImageUuid;
307 /** Image modification UUID. */
308 RTUUID ModificationUuid;
309 /** Parent image UUID. */
310 RTUUID ParentUuid;
311
312 /** Pointer to the grain table cache, if this image contains sparse extents. */
313 PVMDKGTCACHE pGTCache;
314 /** Pointer to the descriptor (NULL if no separate descriptor file). */
315 char *pDescData;
316 /** Allocation size of the descriptor file. */
317 size_t cbDescAlloc;
318 /** Parsed descriptor file content. */
319 VMDKDESCRIPTOR Descriptor;
320} VMDKIMAGE, *PVMDKIMAGE;
321
322
323/*******************************************************************************
324* Internal Functions *
325*******************************************************************************/
326
327static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent);
328static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent);
329
330static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor);
331static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent);
332static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent);
333#ifdef VBOX_WITH_VMDK_ESX
334static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent);
335#endif /* VBOX_WITH_VMDK_ESX */
336static void vmdkFreeExtentData(PVMDKEXTENT pExtent, bool fDelete);
337
338static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
339static int vmdkOpenImage(PVMDKIMAGE pImage, const char *pszFilename, unsigned uOpenFlags);
340static int vmdkFlushImage(PVMDKIMAGE pImage);
341static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment);
342static void vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete);
343
344static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData);
345static int vmdkClose(void *pBackendData, bool fDelete);
346
347
348DECLINLINE(int) vmdkError(PVMDKIMAGE pImage, int rc, RT_SRC_POS_DECL, const char *pszFormat, ...)
349{
350 va_list va;
351 va_start(va, pszFormat);
352 pImage->pfnError(pImage->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
353 va_end(va);
354 return rc;
355}
356
357/**
358 * Internal: truncate a string (at a UTF8 code point boundary) and encode the
359 * critical non-ASCII characters.
360 */
361static char *vmdkEncodeString(const char *psz)
362{
363 /** @todo implement me. */
364 return RTStrDup(psz);
365}
366
367/**
368 * Internal: decode a string and store it into the specified string.
369 */
370static int vmdkDecodeString(const char *pszEncoded, char *psz, size_t cb)
371{
372 /** @todo implement me. */
373 if (!cb)
374 return VINF_SUCCESS;
375 strncpy(psz, pszEncoded, cb);
376 psz[cb - 1] = '\0';
377 return VINF_SUCCESS;
378}
379
380static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent)
381{
382 int rc = VINF_SUCCESS;
383 unsigned i;
384 uint32_t *pGD = NULL, *pRGD = NULL, *pGDTmp, *pRGDTmp;
385 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
386
387 pGD = (uint32_t *)RTMemAllocZ(cbGD);
388 if (!pGD)
389 {
390 rc = VERR_NO_MEMORY;
391 goto out;
392 }
393 pExtent->pGD = pGD;
394 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD),
395 pGD, cbGD, NULL);
396 AssertRC(rc);
397 if (VBOX_FAILURE(rc))
398 {
399 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read grain directory in '%s'"), pExtent->pszFullname);
400 goto out;
401 }
402 for (i = 0, pGDTmp = pGD; i < pExtent->cGDEntries; i++, pGDTmp++)
403 *pGDTmp = RT_LE2H_U32(*pGDTmp);
404
405 if (pExtent->uSectorRGD)
406 {
407 pRGD = (uint32_t *)RTMemAllocZ(cbGD);
408 if (!pRGD)
409 {
410 rc = VERR_NO_MEMORY;
411 goto out;
412 }
413 pExtent->pRGD = pRGD;
414 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
415 pRGD, cbGD, NULL);
416 AssertRC(rc);
417 if (VBOX_FAILURE(rc))
418 {
419 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read redundant grain directory in '%s'"), pExtent->pszFullname);
420 goto out;
421 }
422 for (i = 0, pRGDTmp = pRGD; i < pExtent->cGDEntries; i++, pRGDTmp++)
423 *pRGDTmp = RT_LE2H_U32(*pRGDTmp);
424
425 /* Check grain table and redundant grain table for consistency. */
426 size_t cbGT = pExtent->cGTEntries;
427 uint32_t *pTmpGT1 = (uint32_t *)RTMemTmpAlloc(cbGT);
428 if (!pTmpGT1)
429 {
430 rc = VERR_NO_MEMORY;
431 goto out;
432 }
433 uint32_t *pTmpGT2 = (uint32_t *)RTMemTmpAlloc(cbGT);
434 if (!pTmpGT2)
435 {
436 RTMemTmpFree(pTmpGT1);
437 rc = VERR_NO_MEMORY;
438 goto out;
439 }
440
441 for (i = 0, pGDTmp = pGD, pRGDTmp = pRGD; i < pExtent->cGDEntries; i++, pGDTmp++, pRGDTmp++)
442 {
443 /* If no grain table is allocated skip the entry. */
444 if (*pGDTmp == 0 && *pRGDTmp == 0)
445 continue;
446
447 if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
448 {
449 /* Just one grain directory entry refers to a not yet allocated
450 * grain table or both grain directory copies refer to the same
451 * grain table. Not allowed. */
452 RTMemTmpFree(pTmpGT1);
453 RTMemTmpFree(pTmpGT2);
454 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);
455 goto out;
456 }
457 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pGDTmp),
458 pTmpGT1, cbGT, NULL);
459 if (VBOX_FAILURE(rc))
460 {
461 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);
462 RTMemTmpFree(pTmpGT1);
463 RTMemTmpFree(pTmpGT2);
464 goto out;
465 }
466 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pRGDTmp),
467 pTmpGT2, cbGT, NULL);
468 if (VBOX_FAILURE(rc))
469 {
470 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading backup grain table in '%s'"), pExtent->pszFullname);
471 RTMemTmpFree(pTmpGT1);
472 RTMemTmpFree(pTmpGT2);
473 goto out;
474 }
475 if (memcmp(pTmpGT1, pTmpGT2, cbGT))
476 {
477 RTMemTmpFree(pTmpGT1);
478 RTMemTmpFree(pTmpGT2);
479 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);
480 goto out;
481 }
482 }
483
484 /** @todo figure out what to do for unclean VMDKs. */
485 }
486
487out:
488 if (VBOX_FAILURE(rc))
489 vmdkFreeGrainDirectory(pExtent);
490 return rc;
491}
492
493static int vmdkCreateGrainDirectory(PVMDKEXTENT pExtent, uint64_t uStartSector, bool fPreAlloc)
494{
495 int rc = VINF_SUCCESS;
496 unsigned i;
497 uint32_t *pGD = NULL, *pRGD = NULL;
498 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
499 size_t cbGDRounded = RT_ALIGN_64(pExtent->cGDEntries * sizeof(uint32_t), 512);
500 size_t cbGTRounded;
501 uint64_t cbOverhead;
502
503 if (fPreAlloc)
504 cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
505 else
506 cbGTRounded = 0;
507
508 pGD = (uint32_t *)RTMemAllocZ(cbGD);
509 if (!pGD)
510 {
511 rc = VERR_NO_MEMORY;
512 goto out;
513 }
514 pExtent->pGD = pGD;
515 pRGD = (uint32_t *)RTMemAllocZ(cbGD);
516 if (!pRGD)
517 {
518 rc = VERR_NO_MEMORY;
519 goto out;
520 }
521 pExtent->pRGD = pRGD;
522
523 cbOverhead = RT_ALIGN_64(VMDK_SECTOR2BYTE(uStartSector) + 2 * (cbGDRounded + cbGTRounded), VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
524 rc = RTFileSetSize(pExtent->File, cbOverhead);
525 if (VBOX_FAILURE(rc))
526 goto out;
527 pExtent->uSectorRGD = uStartSector;
528 pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded);
529
530 if (fPreAlloc)
531 {
532 uint32_t uGTSectorLE;
533 uint32_t uOffsetSectors;
534
535 uOffsetSectors = pExtent->uSectorRGD + VMDK_BYTE2SECTOR(cbGDRounded);
536 for (i = 0; i < pExtent->cGDEntries; i++)
537 {
538 pRGD[i] = uOffsetSectors;
539 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
540 /* Write the redundant grain directory entry to disk. */
541 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE), &uGTSectorLE, sizeof(uGTSectorLE), NULL);
542 if (VBOX_FAILURE(rc))
543 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);
544 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
545 }
546
547 uOffsetSectors = pExtent->uSectorGD + VMDK_BYTE2SECTOR(cbGDRounded);
548 for (i = 0; i < pExtent->cGDEntries; i++)
549 {
550 pGD[i] = uOffsetSectors;
551 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
552 /* Write the grain directory entry to disk. */
553 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE), &uGTSectorLE, sizeof(uGTSectorLE), NULL);
554 if (VBOX_FAILURE(rc))
555 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);
556 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
557 }
558 }
559 pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);
560
561out:
562 if (VBOX_FAILURE(rc))
563 vmdkFreeGrainDirectory(pExtent);
564 return rc;
565}
566
567static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)
568{
569 if (pExtent->pGD)
570 {
571 RTMemFree(pExtent->pGD);
572 pExtent->pGD = NULL;
573 }
574 if (pExtent->pRGD)
575 {
576 RTMemFree(pExtent->pRGD);
577 pExtent->pRGD = NULL;
578 }
579}
580
581static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr, char **ppszUnquoted, char **ppszNext)
582{
583 char *pszQ;
584 char *pszUnquoted;
585
586 /* Skip over whitespace. */
587 while (*pszStr == ' ' || *pszStr == '\t')
588 pszStr++;
589 if (*pszStr++ != '"')
590 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);
591
592 pszQ = (char*)strchr(pszStr, '"');
593 if (pszQ == NULL)
594 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s'"), pImage->pszFilename);
595 pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);
596 if (!pszUnquoted)
597 return VERR_NO_MEMORY;
598 memcpy(pszUnquoted, pszStr, pszQ - pszStr);
599 pszUnquoted[pszQ - pszStr] = '\0';
600 *ppszUnquoted = pszUnquoted;
601 if (ppszNext)
602 *ppszNext = pszQ + 1;
603 return VINF_SUCCESS;
604}
605
606static int vmdkDescInitStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
607 const char *pszLine)
608{
609 char *pEnd = pDescriptor->aLines[pDescriptor->cLines];
610 ssize_t cbDiff = strlen(pszLine) + 1;
611
612 if ( pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1
613 && pEnd - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
614 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
615
616 memcpy(pEnd, pszLine, cbDiff);
617 pDescriptor->cLines++;
618 pDescriptor->aLines[pDescriptor->cLines] = pEnd + cbDiff;
619 pDescriptor->fDirty = true;
620
621 return VINF_SUCCESS;
622}
623
624static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,
625 const char *pszKey, const char **ppszValue)
626{
627 size_t cbKey = strlen(pszKey);
628 const char *pszValue;
629
630 while (uStart != 0)
631 {
632 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
633 {
634 /* Key matches, check if there is a '=' (preceded by whitespace). */
635 pszValue = pDescriptor->aLines[uStart] + cbKey;
636 while (*pszValue == ' ' || *pszValue == '\t')
637 pszValue++;
638 if (*pszValue == '=')
639 {
640 *ppszValue = pszValue + 1;
641 break;
642 }
643 }
644 uStart = pDescriptor->aNextLines[uStart];
645 }
646 return !!uStart;
647}
648
649static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
650 unsigned uStart,
651 const char *pszKey, const char *pszValue)
652{
653 char *pszTmp;
654 size_t cbKey = strlen(pszKey);
655 unsigned uLast = 0;
656
657 while (uStart != 0)
658 {
659 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
660 {
661 /* Key matches, check if there is a '=' (preceded by whitespace). */
662 pszTmp = pDescriptor->aLines[uStart] + cbKey;
663 while (*pszTmp == ' ' || *pszTmp == '\t')
664 pszTmp++;
665 if (*pszTmp == '=')
666 {
667 while (*pszTmp == ' ' || *pszTmp == '\t')
668 pszTmp++;
669 break;
670 }
671 }
672 if (!pDescriptor->aNextLines[uStart])
673 uLast = uStart;
674 uStart = pDescriptor->aNextLines[uStart];
675 }
676 if (uStart)
677 {
678 if (pszValue)
679 {
680 /* Key already exists, replace existing value. */
681 size_t cbOldVal = strlen(pszTmp);
682 size_t cbNewVal = strlen(pszValue);
683 ssize_t cbDiff = cbNewVal - cbOldVal;
684 /* Check for buffer overflow. */
685 if ( pDescriptor->aLines[pDescriptor->cLines]
686 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
687 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
688
689 memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,
690 pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);
691 memcpy(pszTmp, pszValue, cbNewVal + 1);
692 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
693 pDescriptor->aLines[i] += cbDiff;
694 }
695 else
696 {
697 memmove(pDescriptor->aLines[uStart], pDescriptor->aLines[uStart+1],
698 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uStart+1] + 1);
699 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
700 {
701 pDescriptor->aLines[i-1] = pDescriptor->aLines[i];
702 if (pDescriptor->aNextLines[i])
703 pDescriptor->aNextLines[i-1] = pDescriptor->aNextLines[i] - 1;
704 else
705 pDescriptor->aNextLines[i-1] = 0;
706 }
707 pDescriptor->cLines--;
708 /* Adjust starting line numbers of following descriptor sections. */
709 if (uStart < pDescriptor->uFirstExtent)
710 pDescriptor->uFirstExtent--;
711 if (uStart < pDescriptor->uFirstDDB)
712 pDescriptor->uFirstDDB--;
713 }
714 }
715 else
716 {
717 /* Key doesn't exist, append it after the last entry in this category. */
718 if (!pszValue)
719 {
720 /* Key doesn't exist, and it should be removed. Simply a no-op. */
721 return VINF_SUCCESS;
722 }
723 size_t cbKey = strlen(pszKey);
724 size_t cbValue = strlen(pszValue);
725 ssize_t cbDiff = cbKey + 1 + cbValue + 1;
726 /* Check for buffer overflow. */
727 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
728 || ( pDescriptor->aLines[pDescriptor->cLines]
729 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
730 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
731 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
732 {
733 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
734 if (pDescriptor->aNextLines[i - 1])
735 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
736 else
737 pDescriptor->aNextLines[i] = 0;
738 }
739 uStart = uLast + 1;
740 pDescriptor->aNextLines[uLast] = uStart;
741 pDescriptor->aNextLines[uStart] = 0;
742 pDescriptor->cLines++;
743 pszTmp = pDescriptor->aLines[uStart];
744 memmove(pszTmp + cbDiff, pszTmp,
745 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
746 memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);
747 pDescriptor->aLines[uStart][cbKey] = '=';
748 memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);
749 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
750 pDescriptor->aLines[i] += cbDiff;
751
752 /* Adjust starting line numbers of following descriptor sections. */
753 if (uStart <= pDescriptor->uFirstExtent)
754 pDescriptor->uFirstExtent++;
755 if (uStart <= pDescriptor->uFirstDDB)
756 pDescriptor->uFirstDDB++;
757 }
758 pDescriptor->fDirty = true;
759 return VINF_SUCCESS;
760}
761
762static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,
763 uint32_t *puValue)
764{
765 const char *pszValue;
766
767 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey, &pszValue))
768 return VERR_VDI_VALUE_NOT_FOUND;
769 return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);
770}
771
772static int vmdkDescBaseGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
773 const char *pszKey, const char **ppszValue)
774{
775 const char *pszValue;
776 char *pszValueUnquoted;
777
778 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey, &pszValue))
779 return VERR_VDI_VALUE_NOT_FOUND;
780 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
781 if (VBOX_FAILURE(rc))
782 return rc;
783 *ppszValue = pszValueUnquoted;
784 return rc;
785}
786
787static int vmdkDescBaseSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
788 const char *pszKey, const char *pszValue)
789{
790 char *pszValueQuoted;
791
792 int rc = RTStrAPrintf(&pszValueQuoted, "\"%s\"", pszValue);
793 if (VBOX_FAILURE(rc))
794 return rc;
795 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, pszKey, pszValueQuoted);
796 RTStrFree(pszValueQuoted);
797 return rc;
798}
799
800static void vmdkDescExtRemoveDummy(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor)
801{
802 unsigned uEntry = pDescriptor->uFirstExtent;
803 ssize_t cbDiff;
804
805 if (!uEntry)
806 return;
807
808 cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;
809 /* Move everything including the \0 in the entry marking the end of buffer. */
810 memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],
811 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);
812 for (unsigned i = uEntry + 1; i <= pDescriptor->cLines; i++)
813 {
814 pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;
815 if (pDescriptor->aNextLines[i])
816 pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;
817 else
818 pDescriptor->aNextLines[i - 1] = 0;
819 }
820 pDescriptor->cLines--;
821 if (pDescriptor->uFirstDDB)
822 pDescriptor->uFirstDDB--;
823
824 return;
825}
826
827static int vmdkDescExtInsert(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
828 VMDKACCESS enmAccess, uint64_t cNominalSectors,
829 VMDKETYPE enmType, const char *pszBasename,
830 uint64_t uSectorOffset)
831{
832 static const char *apszAccess[] = { "NOACCESS", "RDONLY", "RW" };
833 static const char *apszType[] = { "", "SPARSE", "FLAT", "ZERO" };
834 char *pszTmp;
835 unsigned uStart = pDescriptor->uFirstExtent, uLast = 0;
836 char szExt[1024];
837 ssize_t cbDiff;
838
839 /* Find last entry in extent description. */
840 while (uStart)
841 {
842 if (!pDescriptor->aNextLines[uStart])
843 uLast = uStart;
844 uStart = pDescriptor->aNextLines[uStart];
845 }
846
847 if (enmType == VMDKETYPE_ZERO)
848 {
849 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s ", apszAccess[enmAccess],
850 cNominalSectors, apszType[enmType]);
851 }
852 else
853 {
854 if (!uSectorOffset)
855 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\"",
856 apszAccess[enmAccess], cNominalSectors,
857 apszType[enmType], pszBasename);
858 else
859 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\" %llu",
860 apszAccess[enmAccess], cNominalSectors,
861 apszType[enmType], pszBasename, uSectorOffset);
862 }
863 cbDiff = strlen(szExt) + 1;
864
865 /* Check for buffer overflow. */
866 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
867 || ( pDescriptor->aLines[pDescriptor->cLines]
868 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
869 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
870
871 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
872 {
873 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
874 if (pDescriptor->aNextLines[i - 1])
875 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
876 else
877 pDescriptor->aNextLines[i] = 0;
878 }
879 uStart = uLast + 1;
880 pDescriptor->aNextLines[uLast] = uStart;
881 pDescriptor->aNextLines[uStart] = 0;
882 pDescriptor->cLines++;
883 pszTmp = pDescriptor->aLines[uStart];
884 memmove(pszTmp + cbDiff, pszTmp,
885 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
886 memcpy(pDescriptor->aLines[uStart], szExt, cbDiff);
887 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
888 pDescriptor->aLines[i] += cbDiff;
889
890 /* Adjust starting line numbers of following descriptor sections. */
891 if (uStart <= pDescriptor->uFirstDDB)
892 pDescriptor->uFirstDDB++;
893
894 pDescriptor->fDirty = true;
895 return VINF_SUCCESS;
896}
897
898static int vmdkDescDDBGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
899 const char *pszKey, const char **ppszValue)
900{
901 const char *pszValue;
902 char *pszValueUnquoted;
903
904 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey, &pszValue))
905 return VERR_VDI_VALUE_NOT_FOUND;
906 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
907 if (VBOX_FAILURE(rc))
908 return rc;
909 *ppszValue = pszValueUnquoted;
910 return rc;
911}
912
913static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
914 const char *pszKey, uint32_t *puValue)
915{
916 const char *pszValue;
917 char *pszValueUnquoted;
918
919 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey, &pszValue))
920 return VERR_VDI_VALUE_NOT_FOUND;
921 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
922 if (VBOX_FAILURE(rc))
923 return rc;
924 rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);
925 RTMemTmpFree(pszValueUnquoted);
926 return rc;
927}
928
929static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
930 const char *pszKey, PRTUUID pUuid)
931{
932 const char *pszValue;
933 char *pszValueUnquoted;
934
935 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey, &pszValue))
936 return VERR_VDI_VALUE_NOT_FOUND;
937 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
938 if (VBOX_FAILURE(rc))
939 return rc;
940 rc = RTUuidFromStr(pUuid, pszValueUnquoted);
941 RTMemTmpFree(pszValueUnquoted);
942 return rc;
943}
944
945static int vmdkDescDDBSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor, const char *pszKey, const char *pszVal)
946{
947 int rc;
948 char *pszValQuoted;
949
950 if (pszVal)
951 {
952 rc = RTStrAPrintf(&pszValQuoted, "\"%s\"", pszVal);
953 if (VBOX_FAILURE(rc))
954 return rc;
955 }
956 else
957 pszValQuoted = NULL;
958 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey, pszValQuoted);
959 if (pszValQuoted)
960 RTStrFree(pszValQuoted);
961 return rc;
962}
963
964static int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor, const char *pszKey, PCRTUUID pUuid)
965{
966 char *pszUuid;
967
968 int rc = RTStrAPrintf(&pszUuid, "\"%Vuuid\"", pUuid);
969 if (VBOX_FAILURE(rc))
970 return rc;
971 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey, pszUuid);
972 RTStrFree(pszUuid);
973 return rc;
974}
975
976int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor, const char *pszKey, uint32_t uValue)
977{
978 char *pszValue;
979
980 int rc = RTStrAPrintf(&pszValue, "\"%d\"", uValue);
981 if (VBOX_FAILURE(rc))
982 return rc;
983 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey, pszValue);
984 RTStrFree(pszValue);
985 return rc;
986}
987
988static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
989{
990 int rc = VINF_SUCCESS;
991 unsigned cLine = 0, uLastNonEmptyLine = 0;
992 char *pTmp = pDescData;
993
994 pDescriptor->cbDescAlloc = cbDescData;
995 while (*pTmp != '\0')
996 {
997 pDescriptor->aLines[cLine++] = pTmp;
998 if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)
999 {
1000 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
1001 goto out;
1002 }
1003
1004 while (*pTmp != '\0' && *pTmp != '\n')
1005 {
1006 if (*pTmp == '\r')
1007 {
1008 if (*(pTmp + 1) != '\n')
1009 {
1010 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor in '%s'"), pImage->pszFilename);
1011 goto out;
1012 }
1013 else
1014 {
1015 /* Get rid of CR character. */
1016 *pTmp = '\0';
1017 }
1018 }
1019 pTmp++;
1020 }
1021 /* Get rid of LF character. */
1022 if (*pTmp == '\n')
1023 {
1024 *pTmp = '\0';
1025 pTmp++;
1026 }
1027 }
1028 pDescriptor->cLines = cLine;
1029 /* Pointer right after the end of the used part of the buffer. */
1030 pDescriptor->aLines[cLine] = pTmp;
1031
1032 if (strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile"))
1033 {
1034 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);
1035 goto out;
1036 }
1037
1038 /* Initialize those, because we need to be able to reopen an image. */
1039 pDescriptor->uFirstDesc = 0;
1040 pDescriptor->uFirstExtent = 0;
1041 pDescriptor->uFirstDDB = 0;
1042 for (unsigned i = 0; i < cLine; i++)
1043 {
1044 if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
1045 {
1046 if ( !strncmp(pDescriptor->aLines[i], "RW", 2)
1047 || !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
1048 || !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
1049 {
1050 /* An extent descriptor. */
1051 if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
1052 {
1053 /* Incorrect ordering of entries. */
1054 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1055 goto out;
1056 }
1057 if (!pDescriptor->uFirstExtent)
1058 {
1059 pDescriptor->uFirstExtent = i;
1060 uLastNonEmptyLine = 0;
1061 }
1062 }
1063 else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
1064 {
1065 /* A disk database entry. */
1066 if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
1067 {
1068 /* Incorrect ordering of entries. */
1069 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1070 goto out;
1071 }
1072 if (!pDescriptor->uFirstDDB)
1073 {
1074 pDescriptor->uFirstDDB = i;
1075 uLastNonEmptyLine = 0;
1076 }
1077 }
1078 else
1079 {
1080 /* A normal entry. */
1081 if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
1082 {
1083 /* Incorrect ordering of entries. */
1084 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
1085 goto out;
1086 }
1087 if (!pDescriptor->uFirstDesc)
1088 {
1089 pDescriptor->uFirstDesc = i;
1090 uLastNonEmptyLine = 0;
1091 }
1092 }
1093 if (uLastNonEmptyLine)
1094 pDescriptor->aNextLines[uLastNonEmptyLine] = i;
1095 uLastNonEmptyLine = i;
1096 }
1097 }
1098
1099out:
1100 return rc;
1101}
1102
1103static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
1104{
1105 int rc;
1106
1107 pDescriptor->uFirstDesc = 0;
1108 pDescriptor->uFirstExtent = 0;
1109 pDescriptor->uFirstDDB = 0;
1110 pDescriptor->cLines = 0;
1111 pDescriptor->cbDescAlloc = cbDescData;
1112 pDescriptor->fDirty = false;
1113 pDescriptor->aLines[pDescriptor->cLines] = pDescData;
1114 memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));
1115
1116 rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");
1117 if (VBOX_FAILURE(rc))
1118 goto out;
1119 rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");
1120 if (VBOX_FAILURE(rc))
1121 goto out;
1122 pDescriptor->uFirstDesc = pDescriptor->cLines - 1;
1123 rc = vmdkDescInitStr(pImage, pDescriptor, "");
1124 if (VBOX_FAILURE(rc))
1125 goto out;
1126 rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");
1127 if (VBOX_FAILURE(rc))
1128 goto out;
1129 rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");
1130 if (VBOX_FAILURE(rc))
1131 goto out;
1132 pDescriptor->uFirstExtent = pDescriptor->cLines - 1;
1133 rc = vmdkDescInitStr(pImage, pDescriptor, "");
1134 if (VBOX_FAILURE(rc))
1135 goto out;
1136 /* The trailing space is created by VMware, too. */
1137 rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");
1138 if (VBOX_FAILURE(rc))
1139 goto out;
1140 rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");
1141 if (VBOX_FAILURE(rc))
1142 goto out;
1143 rc = vmdkDescInitStr(pImage, pDescriptor, "");
1144 if (VBOX_FAILURE(rc))
1145 goto out;
1146 rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");
1147 if (VBOX_FAILURE(rc))
1148 goto out;
1149 pDescriptor->uFirstDDB = pDescriptor->cLines - 1;
1150
1151 /* Now that the framework is in place, use the normal functions to insert
1152 * the remaining keys. */
1153 char szBuf[9];
1154 RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());
1155 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, "CID", szBuf);
1156 if (VBOX_FAILURE(rc))
1157 goto out;
1158 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, "parentCID", "ffffffff");
1159 if (VBOX_FAILURE(rc))
1160 goto out;
1161
1162 rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");
1163 if (VBOX_FAILURE(rc))
1164 goto out;
1165
1166out:
1167 return rc;
1168}
1169
1170static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData)
1171{
1172 int rc;
1173 unsigned cExtents;
1174 unsigned uLine;
1175
1176 rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData, &pImage->Descriptor);
1177 if (VBOX_FAILURE(rc))
1178 return rc;
1179
1180 /* Check version, must be 1. */
1181 uint32_t uVersion;
1182 rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
1183 if (VBOX_FAILURE(rc))
1184 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);
1185 if (uVersion != 1)
1186 return vmdkError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);
1187
1188 /* Get image creation type and determine image flags. */
1189 const char *pszCreateType;
1190 rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",
1191 &pszCreateType);
1192 if (VBOX_FAILURE(rc))
1193 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);
1194 if ( !strcmp(pszCreateType, "twoGbMaxExtentSparse")
1195 || !strcmp(pszCreateType, "twoGbMaxExtentFlat"))
1196 pImage->uImageFlags = VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
1197 if ( !strcmp(pszCreateType, "partitionedDevice")
1198 || !strcmp(pszCreateType, "fullDevice"))
1199 pImage->uImageFlags = VD_VMDK_IMAGE_FLAGS_RAWDISK;
1200 else
1201 pImage->uImageFlags = 0;
1202 RTStrFree((char *)(void *)pszCreateType);
1203
1204 /* Count the number of extent config entries. */
1205 for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
1206 uLine != 0;
1207 uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
1208 /* nothing */;
1209
1210 if (!pImage->pDescData && cExtents != 1)
1211 {
1212 /* Monolithic image, must have only one extent (already opened). */
1213 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent in '%s'"), pImage->pszFilename);
1214 }
1215
1216 if (pImage->pDescData)
1217 {
1218 /* Non-monolithic image, extents need to be allocated. */
1219 rc = vmdkCreateExtents(pImage, cExtents);
1220 if (VBOX_FAILURE(rc))
1221 return rc;
1222 }
1223
1224 for (unsigned i = 0, uLine = pImage->Descriptor.uFirstExtent;
1225 i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
1226 {
1227 char *pszLine = pImage->Descriptor.aLines[uLine];
1228
1229 /* Access type of the extent. */
1230 if (!strncmp(pszLine, "RW", 2))
1231 {
1232 pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
1233 pszLine += 2;
1234 }
1235 else if (!strncmp(pszLine, "RDONLY", 6))
1236 {
1237 pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
1238 pszLine += 6;
1239 }
1240 else if (!strncmp(pszLine, "NOACCESS", 8))
1241 {
1242 pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
1243 pszLine += 8;
1244 }
1245 else
1246 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1247 if (*pszLine++ != ' ')
1248 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1249
1250 /* Nominal size of the extent. */
1251 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
1252 &pImage->pExtents[i].cNominalSectors);
1253 if (VBOX_FAILURE(rc))
1254 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1255 if (*pszLine++ != ' ')
1256 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1257
1258 /* Type of the extent. */
1259#ifdef VBOX_WITH_VMDK_ESX
1260 /** @todo Add the ESX extent types. Not necessary for now because
1261 * the ESX extent types are only used inside an ESX server. They are
1262 * automatically converted if the VMDK is exported. */
1263#endif /* VBOX_WITH_VMDK_ESX */
1264 if (!strncmp(pszLine, "SPARSE", 6))
1265 {
1266 pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
1267 pszLine += 6;
1268 }
1269 else if (!strncmp(pszLine, "FLAT", 4))
1270 {
1271 pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
1272 pszLine += 4;
1273 }
1274 else if (!strncmp(pszLine, "ZERO", 4))
1275 {
1276 pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
1277 pszLine += 4;
1278 }
1279 else
1280 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1281 if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
1282 {
1283 /* This one has no basename or offset. */
1284 if (*pszLine == ' ')
1285 pszLine++;
1286 if (*pszLine != '\0')
1287 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1288 pImage->pExtents[i].pszBasename = NULL;
1289 }
1290 else
1291 {
1292 /* All other extent types have basename and optional offset. */
1293 if (*pszLine++ != ' ')
1294 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1295
1296 /* Basename of the image. Surrounded by quotes. */
1297 char *pszBasename;
1298 rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);
1299 if (VBOX_FAILURE(rc))
1300 return rc;
1301 pImage->pExtents[i].pszBasename = pszBasename;
1302 if (*pszLine == ' ')
1303 {
1304 pszLine++;
1305 if (*pszLine != '\0')
1306 {
1307 /* Optional offset in extent specified. */
1308 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
1309 &pImage->pExtents[i].uSectorOffset);
1310 if (VBOX_FAILURE(rc))
1311 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1312 }
1313 }
1314
1315 if (*pszLine != '\0')
1316 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
1317 }
1318 }
1319
1320 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
1321 "ddb.geometry.cylinders", &pImage->cCylinders);
1322 if (VBOX_FAILURE(rc))
1323 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description in '%s'"), pImage->pszFilename);
1324 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
1325 "ddb.geometry.heads", &pImage->cHeads);
1326 if (VBOX_FAILURE(rc))
1327 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description in '%s'"), pImage->pszFilename);
1328 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
1329 "ddb.geometry.sectors", &pImage->cSectors);
1330 if (VBOX_FAILURE(rc))
1331 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description in '%s'"), pImage->pszFilename);
1332 if (pImage->cCylinders >= 1024 || pImage->cHeads != 16)
1333 pImage->enmTranslation = PDMBIOSTRANSLATION_LBA;
1334 else
1335 pImage->enmTranslation = PDMBIOSTRANSLATION_NONE;
1336
1337 /* Get image UUID. */
1338 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.image",
1339 &pImage->ImageUuid);
1340 if (rc == VERR_VDI_VALUE_NOT_FOUND)
1341 {
1342 /* Image without UUID. Probably created by VMware and not yet used
1343 * by VirtualBox. Can only be added for images opened in read/write
1344 * mode, so don't bother producing a sensible UUID otherwise. */
1345 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1346 RTUuidClear(&pImage->ImageUuid);
1347 else
1348 {
1349 rc = RTUuidCreate(&pImage->ImageUuid);
1350 if (VBOX_FAILURE(rc))
1351 return rc;
1352 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
1353 "ddb.uuid.image", &pImage->ImageUuid);
1354 if (VBOX_FAILURE(rc))
1355 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
1356 }
1357 }
1358 else if (VBOX_FAILURE(rc))
1359 return rc;
1360
1361 /* Get image modification UUID. */
1362 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.modification",
1363 &pImage->ModificationUuid);
1364 if (rc == VERR_VDI_VALUE_NOT_FOUND)
1365 {
1366 /* Image without UUID. Probably created by VMware and not yet used
1367 * by VirtualBox. Can only be added for images opened in read/write
1368 * mode, so don't bother producing a sensible UUID otherwise. */
1369 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1370 RTUuidClear(&pImage->ModificationUuid);
1371 else
1372 {
1373 rc = RTUuidCreate(&pImage->ModificationUuid);
1374 if (VBOX_FAILURE(rc))
1375 return rc;
1376 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
1377 "ddb.uuid.modification", &pImage->ModificationUuid);
1378 if (VBOX_FAILURE(rc))
1379 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);
1380 }
1381 }
1382 else if (VBOX_FAILURE(rc))
1383 return rc;
1384
1385 /* Get UUID of parent image. */
1386 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.parent",
1387 &pImage->ParentUuid);
1388 if (rc == VERR_VDI_VALUE_NOT_FOUND)
1389 {
1390 /* Image without UUID. Probably created by VMware and not yet used
1391 * by VirtualBox. Can only be added for images opened in read/write
1392 * mode, so don't bother producing a sensible UUID otherwise. */
1393 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1394 RTUuidClear(&pImage->ParentUuid);
1395 else
1396 {
1397 rc = RTUuidClear(&pImage->ParentUuid);
1398 if (VBOX_FAILURE(rc))
1399 return rc;
1400 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
1401 "ddb.uuid.parent", &pImage->ParentUuid);
1402 if (VBOX_FAILURE(rc))
1403 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);
1404 }
1405 }
1406 else if (VBOX_FAILURE(rc))
1407 return rc;
1408
1409 return VINF_SUCCESS;
1410}
1411
1412static int vmdkWriteDescriptor(PVMDKIMAGE pImage)
1413{
1414 int rc = VINF_SUCCESS;
1415 uint64_t cbLimit;
1416 uint64_t uOffset;
1417 RTFILE DescFile;
1418
1419 if (pImage->pDescData)
1420 {
1421 /* Separate descriptor file. */
1422 uOffset = 0;
1423 cbLimit = 0;
1424 DescFile = pImage->File;
1425 }
1426 else
1427 {
1428 /* Embedded descriptor file. */
1429 uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
1430 cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
1431 cbLimit += uOffset;
1432 DescFile = pImage->pExtents[0].File;
1433 }
1434 for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
1435 {
1436 const char *psz = pImage->Descriptor.aLines[i];
1437 size_t cb = strlen(psz);
1438
1439 if (cbLimit && uOffset + cb + 1 > cbLimit)
1440 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);
1441 rc = RTFileWriteAt(DescFile, uOffset, psz, cb, NULL);
1442 if (VBOX_FAILURE(rc))
1443 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
1444 uOffset += cb;
1445 rc = RTFileWriteAt(DescFile, uOffset, "\n", 1, NULL);
1446 if (VBOX_FAILURE(rc))
1447 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
1448 uOffset++;
1449 }
1450 if (cbLimit)
1451 {
1452 /* Inefficient, but simple. */
1453 while (uOffset < cbLimit)
1454 {
1455 rc = RTFileWriteAt(DescFile, uOffset, "", 1, NULL);
1456 if (VBOX_FAILURE(rc))
1457 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
1458 uOffset++;
1459 }
1460 }
1461 else
1462 {
1463 rc = RTFileSetSize(DescFile, uOffset);
1464 if (VBOX_FAILURE(rc))
1465 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
1466 }
1467 pImage->Descriptor.fDirty = false;
1468 return rc;
1469}
1470
1471static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent)
1472{
1473 SparseExtentHeader Header;
1474 uint64_t cbExtentSize, cSectorsPerGDE;
1475
1476 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1477 AssertRC(rc);
1478 if (VBOX_FAILURE(rc))
1479 {
1480 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);
1481 goto out;
1482 }
1483 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_SPARSE_MAGICNUMBER
1484 || RT_LE2H_U32(Header.version) != 1)
1485 {
1486 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic/version in extent header in '%s'"), pExtent->pszFullname);
1487 goto out;
1488 }
1489 /* The image must be a multiple of a sector in size. If not, it means the
1490 * image is at least truncated, or even seriously garbled. */
1491 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1492 if (VBOX_FAILURE(rc))
1493 {
1494 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
1495 goto out;
1496 }
1497 if ( (RT_LE2H_U32(Header.flags) & 1)
1498 && ( Header.singleEndLineChar != '\n'
1499 || Header.nonEndLineChar != ' '
1500 || Header.doubleEndLineChar1 != '\r'
1501 || Header.doubleEndLineChar2 != '\n') )
1502 {
1503 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);
1504 goto out;
1505 }
1506 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
1507 pExtent->cSectors = RT_LE2H_U64(Header.capacity);
1508 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
1509 /* The spec says that this must be a power of two and greater than 8,
1510 * but probably they meant not less than 8. */
1511 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1512 || pExtent->cSectorsPerGrain < 8)
1513 {
1514 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
1515 goto out;
1516 }
1517 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
1518 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
1519 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
1520 {
1521 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);
1522 goto out;
1523 }
1524 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
1525 /* This code requires that a grain table must hold a power of two multiple
1526 * of the number of entries per GT cache entry. */
1527 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
1528 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
1529 {
1530 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);
1531 goto out;
1532 }
1533 if (RT_LE2H_U32(Header.flags) & 2)
1534 {
1535 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
1536 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
1537 }
1538 else
1539 {
1540 /** @todo this is just guesswork, the spec doesn't document this
1541 * properly and I don't have a vmdk without RGD. */
1542 pExtent->uSectorGD = RT_LE2H_U64(Header.rgdOffset);
1543 pExtent->uSectorRGD = 0;
1544 }
1545 pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);
1546 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1547 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1548 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1549 {
1550 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);
1551 goto out;
1552 }
1553 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1554 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1555
1556 rc = vmdkReadGrainDirectory(pExtent);
1557
1558out:
1559 if (VBOX_FAILURE(rc))
1560 vmdkFreeExtentData(pExtent, false);
1561
1562 return rc;
1563}
1564
1565static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent)
1566{
1567 SparseExtentHeader Header;
1568
1569 memset(&Header, '\0', sizeof(Header));
1570 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
1571 Header.version = RT_H2LE_U32(1);
1572 Header.flags = RT_H2LE_U32(1 | ((pExtent->pRGD) ? 2 : 0));
1573 Header.capacity = RT_H2LE_U64(pExtent->cSectors);
1574 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
1575 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
1576 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
1577 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
1578 if (pExtent->pRGD)
1579 {
1580 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
1581 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1582 }
1583 else
1584 {
1585 /** @todo this is just guesswork, the spec doesn't document this
1586 * properly and I don't have a vmdk without RGD. */
1587 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1588 }
1589 Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
1590 Header.uncleanShutdown = pExtent->fUncleanShutdown;
1591 Header.singleEndLineChar = '\n';
1592 Header.nonEndLineChar = ' ';
1593 Header.doubleEndLineChar1 = '\r';
1594 Header.doubleEndLineChar2 = '\n';
1595
1596 int rc = RTFileWriteAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1597 AssertRC(rc);
1598 if (VBOX_FAILURE(rc))
1599 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
1600 return rc;
1601}
1602
1603#ifdef VBOX_WITH_VMDK_ESX
1604static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
1605{
1606 COWDisk_Header Header;
1607 uint64_t cSectorsPerGDE;
1608
1609 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1610 AssertRC(rc);
1611 if (VBOX_FAILURE(rc))
1612 goto out;
1613 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER
1614 || RT_LE2H_U32(Header.version) != 1
1615 || RT_LE2H_U32(Header.flags) != 3)
1616 {
1617 rc = VERR_VDI_INVALID_HEADER;
1618 goto out;
1619 }
1620 pExtent->enmType = VMDKETYPE_ESX_SPARSE;
1621 pExtent->cSectors = RT_LE2H_U32(Header.numSectors);
1622 pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);
1623 /* The spec says that this must be between 1 sector and 1MB. This code
1624 * assumes it's a power of two, so check that requirement, too. */
1625 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1626 || pExtent->cSectorsPerGrain == 0
1627 || pExtent->cSectorsPerGrain > 2048)
1628 {
1629 rc = VERR_VDI_INVALID_HEADER;
1630 goto out;
1631 }
1632 pExtent->uDescriptorSector = 0;
1633 pExtent->cDescriptorSectors = 0;
1634 pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);
1635 pExtent->uSectorRGD = 0;
1636 pExtent->cOverheadSectors = 0;
1637 pExtent->cGTEntries = 4096;
1638 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1639 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1640 {
1641 rc = VERR_VDI_INVALID_HEADER;
1642 goto out;
1643 }
1644 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1645 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1646 if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))
1647 {
1648 /* Inconsistency detected. Computed number of GD entries doesn't match
1649 * stored value. Better be safe than sorry. */
1650 rc = VERR_VDI_INVALID_HEADER;
1651 goto out;
1652 }
1653 pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);
1654 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1655
1656 rc = vmdkReadGrainDirectory(pExtent);
1657
1658out:
1659 if (VBOX_FAILURE(rc))
1660 vmdkFreeExtentData(pExtent, false);
1661
1662 return rc;
1663}
1664#endif /* VBOX_WITH_VMDK_ESX */
1665
1666static void vmdkFreeExtentData(PVMDKEXTENT pExtent, bool fDelete)
1667{
1668 vmdkFreeGrainDirectory(pExtent);
1669 if (pExtent->pDescData)
1670 {
1671 RTMemFree(pExtent->pDescData);
1672 pExtent->pDescData = NULL;
1673 }
1674 if (pExtent->File != NIL_RTFILE)
1675 {
1676 RTFileClose(pExtent->File);
1677 pExtent->File = NIL_RTFILE;
1678 if ( fDelete
1679 && strcmp(pExtent->pszFullname, pExtent->pszBasename) != 0
1680 && pExtent->pszFullname)
1681 RTFileDelete(pExtent->pszFullname);
1682 }
1683 if (pExtent->pszBasename)
1684 {
1685 RTMemTmpFree((void *)pExtent->pszBasename);
1686 pExtent->pszBasename = NULL;
1687 }
1688 if (pExtent->pszFullname)
1689 {
1690 RTStrFree((char *)(void *)pExtent->pszFullname);
1691 pExtent->pszFullname = NULL;
1692 }
1693}
1694
1695static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)
1696{
1697 PVMDKEXTENT pExtent;
1698
1699 /* Allocate grain table cache if any sparse extent is present. */
1700 for (unsigned i = 0; i < pImage->cExtents; i++)
1701 {
1702 pExtent = &pImage->pExtents[i];
1703 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1704#ifdef VBOX_WITH_VMDK_ESX
1705 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1706#endif /* VBOX_WITH_VMDK_ESX */
1707 )
1708 {
1709 /* Allocate grain table cache. */
1710 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
1711 if (!pImage->pGTCache)
1712 return VERR_NO_MEMORY;
1713 for (unsigned i = 0; i < VMDK_GT_CACHE_SIZE; i++)
1714 {
1715 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[i];
1716 pGCE->uExtent = UINT32_MAX;
1717 }
1718 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
1719 break;
1720 }
1721 }
1722
1723 return VINF_SUCCESS;
1724}
1725
1726static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
1727{
1728 int rc = VINF_SUCCESS;
1729 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
1730 if (pImage)
1731 {
1732 for (unsigned i = 0; i < cExtents; i++)
1733 {
1734 pExtents[i].File = NIL_RTFILE;
1735 pExtents[i].pszBasename = NULL;
1736 pExtents[i].pszFullname = NULL;
1737 pExtents[i].pGD = NULL;
1738 pExtents[i].pRGD = NULL;
1739 pExtents[i].pDescData = NULL;
1740 pExtents[i].uExtent = i;
1741 pExtents[i].pImage = pImage;
1742 }
1743 pImage->pExtents = pExtents;
1744 pImage->cExtents = cExtents;
1745 }
1746 else
1747 rc = VERR_NO_MEMORY;
1748
1749 return rc;
1750}
1751
1752static int vmdkOpenImage(PVMDKIMAGE pImage, const char *pszFilename, unsigned uOpenFlags)
1753{
1754 int rc = VINF_SUCCESS;
1755 uint32_t u32Magic;
1756 RTFILE File;
1757 PVMDKEXTENT pExtent;
1758
1759 pImage->uOpenFlags = uOpenFlags;
1760
1761 /** @todo check whether the same file is used somewhere else. don't open any file twice, leads to locking problems and can cause trouble with file caching. */
1762
1763 /*
1764 * Open the image.
1765 */
1766 rc = RTFileOpen(&File, pszFilename, uOpenFlags & VD_OPEN_FLAGS_READONLY
1767 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1768 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1769 if (VBOX_FAILURE(rc))
1770 {
1771 /* Do NOT signal an appropriate error here, as the VD layer has the
1772 * choice of retrying the open if it failed. */
1773 goto out;
1774 }
1775 pImage->File = File;
1776 rc = RTFileReadAt(File, 0, &u32Magic, sizeof(u32Magic), NULL);
1777 AssertRC(rc);
1778 if (VBOX_FAILURE(rc))
1779 {
1780 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pszFilename);
1781 goto out;
1782 }
1783
1784 /* Handle the file according to its magic number. */
1785 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
1786 {
1787 /* It's a hosted sparse single-extent image. */
1788 rc = vmdkCreateExtents(pImage, 1);
1789 if (VBOX_FAILURE(rc))
1790 goto out;
1791 /* The opened file is passed to the extent. No separate descriptor
1792 * file, so no need to keep anything open for the image. */
1793 pExtent = &pImage->pExtents[0];
1794 pExtent->File = File;
1795 pImage->File = NIL_RTFILE;
1796 rc = vmdkReadMetaSparseExtent(pExtent);
1797 if (VBOX_FAILURE(rc))
1798 goto out;
1799 /* As we're dealing with a monolithic sparse image here, there must
1800 * be a descriptor embedded in the image file. */
1801 if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)
1802 {
1803 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in '%s'"), pszFilename);
1804 goto out;
1805 }
1806 /* Read the descriptor from the extent. */
1807 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1808 if (!pExtent->pDescData)
1809 {
1810 rc = VERR_NO_MEMORY;
1811 goto out;
1812 }
1813 rc = RTFileReadAt(pExtent->File,
1814 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
1815 pExtent->pDescData,
1816 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
1817 AssertRC(rc);
1818 if (VBOX_FAILURE(rc))
1819 {
1820 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname);
1821 goto out;
1822 }
1823
1824 rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
1825 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1826 if (VBOX_FAILURE(rc))
1827 goto out;
1828
1829 /* Mark the extent as unclean if opened in read-write mode. */
1830 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1831 {
1832 pExtent->fUncleanShutdown = true;
1833 pExtent->fMetaDirty = true;
1834 }
1835 }
1836 else
1837 {
1838 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
1839 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
1840 if (!pImage->pDescData)
1841 {
1842 rc = VERR_NO_MEMORY;
1843 goto out;
1844 }
1845
1846 size_t cbRead;
1847 rc = RTFileReadAt(pImage->File, 0, pImage->pDescData,
1848 pImage->cbDescAlloc, &cbRead);
1849 if (VBOX_FAILURE(rc))
1850 {
1851 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pszFilename);
1852 goto out;
1853 }
1854 if (cbRead == pImage->cbDescAlloc)
1855 {
1856 /* Likely the read is truncated. Better fail a bit too early
1857 * (normally the descriptor is much smaller than our buffer). */
1858 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pszFilename);
1859 goto out;
1860 }
1861
1862 rc = vmdkParseDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc);
1863 if (VBOX_FAILURE(rc))
1864 goto out;
1865
1866 for (unsigned i = 0; i < pImage->cExtents; i++)
1867 {
1868 PVMDKEXTENT pExtent = &pImage->pExtents[i];
1869
1870 if (pExtent->pszBasename)
1871 {
1872 /* Hack to figure out whether the specified name in the
1873 * extent descriptor is absolute. Doesn't always work, but
1874 * should be good enough for now. */
1875 char *pszFullname;
1876 /** @todo implement proper path absolute check. */
1877 if (pExtent->pszBasename[0] == RTPATH_SLASH)
1878 {
1879 pszFullname = RTStrDup(pExtent->pszBasename);
1880 if (!pszFullname)
1881 {
1882 rc = VERR_NO_MEMORY;
1883 goto out;
1884 }
1885 }
1886 else
1887 {
1888 size_t cbDirname;
1889 char *pszDirname = RTStrDup(pImage->pszFilename);
1890 if (!pszDirname)
1891 {
1892 rc = VERR_NO_MEMORY;
1893 goto out;
1894 }
1895 RTPathStripFilename(pszDirname);
1896 cbDirname = strlen(pszDirname);
1897 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname,
1898 RTPATH_SLASH, pExtent->pszBasename);
1899 RTStrFree(pszDirname);
1900 if (VBOX_FAILURE(rc))
1901 goto out;
1902 }
1903 pExtent->pszFullname = pszFullname;
1904 }
1905 else
1906 pExtent->pszFullname = NULL;
1907
1908 switch (pExtent->enmType)
1909 {
1910 case VMDKETYPE_HOSTED_SPARSE:
1911 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1912 uOpenFlags & VD_OPEN_FLAGS_READONLY
1913 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1914 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1915 if (VBOX_FAILURE(rc))
1916 {
1917 /* Do NOT signal an appropriate error here, as the VD
1918 * layer has the choice of retrying the open if it
1919 * failed. */
1920 goto out;
1921 }
1922 rc = vmdkReadMetaSparseExtent(pExtent);
1923 if (VBOX_FAILURE(rc))
1924 goto out;
1925
1926 /* Mark the extent as unclean if opened in read-write mode. */
1927 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1928 {
1929 pExtent->fUncleanShutdown = true;
1930 pExtent->fMetaDirty = true;
1931 }
1932 break;
1933 case VMDKETYPE_FLAT:
1934 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1935 uOpenFlags & VD_OPEN_FLAGS_READONLY
1936 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1937 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1938 if (VBOX_FAILURE(rc))
1939 {
1940 /* Do NOT signal an appropriate error here, as the VD
1941 * layer has the choice of retrying the open if it
1942 * failed. */
1943 goto out;
1944 }
1945 break;
1946 case VMDKETYPE_ZERO:
1947 /* Nothing to do. */
1948 break;
1949 default:
1950 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
1951 }
1952 }
1953 }
1954
1955 /* Make sure this is not reached accidentally with an error status. */
1956 AssertRC(rc);
1957
1958 /* Update the image metadata now in case has changed. */
1959 rc = vmdkFlushImage(pImage);
1960 if (VBOX_FAILURE(rc))
1961 goto out;
1962
1963 /* Figure out a few per-image constants from the extents. */
1964 pImage->cbSize = 0;
1965 for (unsigned i = 0; i < pImage->cExtents; i++)
1966 {
1967 pExtent = &pImage->pExtents[i];
1968 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1969#ifdef VBOX_WITH_VMDK_ESX
1970 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1971#endif /* VBOX_WITH_VMDK_ESX */
1972 )
1973 {
1974 /* Here used to be a check whether the nominal size of an extent
1975 * is a multiple of the grain size. The spec says that this is
1976 * always the case, but unfortunately some files out there in the
1977 * wild violate the spec (e.g. ReactOS 0.3.1). */
1978 }
1979 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
1980 }
1981
1982 pImage->enmImageType = VD_IMAGE_TYPE_NORMAL;
1983 for (unsigned i = 0; i < pImage->cExtents; i++)
1984 {
1985 pExtent = &pImage->pExtents[i];
1986 if ( pImage->pExtents[i].enmType == VMDKETYPE_FLAT
1987 || pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
1988 {
1989 pImage->enmImageType = VD_IMAGE_TYPE_FIXED;
1990 break;
1991 }
1992 }
1993
1994 rc = vmdkAllocateGrainTableCache(pImage);
1995 if (VBOX_FAILURE(rc))
1996 goto out;
1997
1998out:
1999 if (VBOX_FAILURE(rc))
2000 vmdkFreeImage(pImage, false);
2001 return rc;
2002}
2003
2004static int vmdkCreateImage(PVMDKIMAGE pImage, const char *pszFilename, VDIMAGETYPE enmType, uint64_t cbSize, unsigned uImageFlags, const char *pszComment, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
2005{
2006 int rc;
2007 uint64_t cSectorsPerGDE, cSectorsPerGD;
2008 PVMDKEXTENT pExtent;
2009
2010 pImage->uImageFlags = uImageFlags;
2011 rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc, &pImage->Descriptor);
2012 if (VBOX_FAILURE(rc))
2013 {
2014 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pszFilename);
2015 goto out;
2016 }
2017
2018 if ( enmType == VD_IMAGE_TYPE_FIXED
2019 || (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
2020 || (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
2021 {
2022 /* Fixed images and split images in general have a separate descriptor
2023 * file. This is the more complicated case, as it requires setting up
2024 * potentially more than one extent, including filename generation. */
2025
2026 if ( enmType == VD_IMAGE_TYPE_FIXED
2027 && (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
2028 {
2029 PVBOXHDDRAW pRaw = (PVBOXHDDRAW)(void *)pszComment;
2030 /* As the comment is misused, zap it so that no garbage comment
2031 * is set below. */
2032 pszComment = NULL;
2033 if (pRaw->fRawDisk)
2034 {
2035 /* Full raw disk access. This requires setting up a descriptor
2036 * file and open the (flat) raw disk. */
2037 rc = vmdkCreateExtents(pImage, 1);
2038 if (VBOX_FAILURE(rc))
2039 {
2040 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pszFilename);
2041 goto out;
2042 }
2043 pExtent = &pImage->pExtents[0];
2044 rc = RTFileOpen(&pImage->File, pszFilename,
2045 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
2046 if (VBOX_FAILURE(rc))
2047 {
2048 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pszFilename);
2049 goto out;
2050 }
2051
2052 /* Set up basename for extent description. Cannot use StrDup. */
2053 size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
2054 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
2055 if (!pszBasename)
2056 {
2057 rc = VERR_NO_MEMORY;
2058 goto out;
2059 }
2060 memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
2061 pExtent->pszBasename = pszBasename;
2062 /* For raw disks the full name is identical to the base name. */
2063 pExtent->pszFullname = RTStrDup(pszBasename);
2064 if (!pExtent->pszFullname)
2065 {
2066 rc = VERR_NO_MEMORY;
2067 goto out;
2068 }
2069 pExtent->enmType = VMDKETYPE_FLAT;
2070 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
2071 pExtent->uSectorOffset = 0;
2072 pExtent->enmAccess = VMDKACCESS_READWRITE;
2073 pExtent->fMetaDirty = false;
2074
2075 pImage->enmImageType = enmType;
2076 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", "fullDevice");
2077 if (VBOX_FAILURE(rc))
2078 {
2079 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pszFilename);
2080 goto out;
2081 }
2082
2083 /* Open flat image, the raw disk. */
2084 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
2085 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
2086 if (VBOX_FAILURE(rc))
2087 {
2088 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
2089 goto out;
2090 }
2091 }
2092 else
2093 {
2094 /* Raw partition access. This requires setting up a descriptor
2095 * file, write the partition information to a flat extent and
2096 * open all the (flat) raw disk partitions. */
2097
2098 /* First pass over the partitions to determine how many
2099 * extents we need. One partition can require up to 4 extents.
2100 * One to skip over unpartitioned space, one for the
2101 * partitioning data, one to skip over unpartitioned space
2102 * and one for the partition data. */
2103 unsigned cExtents = 0;
2104 uint64_t uStart = 0;
2105 for (unsigned i = 0; i < pRaw->cPartitions; i++)
2106 {
2107 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i];
2108 if (pPart->cbPartitionData)
2109 {
2110 if (uStart > pPart->uPartitionDataStart)
2111 {
2112 rc = vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partitioning information in '%s'"), pszFilename);
2113 goto out;
2114 } else if (uStart != pPart->uPartitionDataStart)
2115 cExtents++;
2116 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData;
2117 cExtents++;
2118 }
2119 if (pPart->cbPartition)
2120 {
2121 if (uStart > pPart->uPartitionStart)
2122 {
2123 rc = vmdkError(pImage, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("VMDK: cannot go backwards for partition data in '%s'"), pszFilename);
2124 goto out;
2125 } else if (uStart != pPart->uPartitionStart)
2126 cExtents++;
2127 uStart = pPart->uPartitionStart + pPart->cbPartition;
2128 cExtents++;
2129 }
2130 }
2131 /* Another extent for filling up the rest of the image. */
2132 if (uStart != cbSize)
2133 cExtents++;
2134
2135 rc = vmdkCreateExtents(pImage, cExtents);
2136 if (VBOX_FAILURE(rc))
2137 {
2138 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pszFilename);
2139 goto out;
2140 }
2141
2142 rc = RTFileOpen(&pImage->File, pszFilename,
2143 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
2144 if (VBOX_FAILURE(rc))
2145 {
2146 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pszFilename);
2147 goto out;
2148 }
2149
2150 /* Create base filename for the partition table extent. */
2151 /** @todo remove fixed buffer. */
2152 char pszPartition[1024];
2153 const char *pszBase = RTPathFilename(pszFilename);
2154 const char *pszExt = RTPathExt(pszBase);
2155 if (pszExt == NULL)
2156 {
2157 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pszFilename);
2158 goto out;
2159 }
2160 memcpy(pszPartition, pszBase, pszExt - pszBase);
2161 memcpy(pszPartition + (pszExt - pszBase), "-pt", 3);
2162 memcpy(pszPartition + (pszExt - pszBase) + 3, pszExt, strlen(pszExt) + 1);
2163
2164 /* Second pass over the partitions, now define all extents. */
2165 uint64_t uPartOffset = 0;
2166 cExtents = 0;
2167 uStart = 0;
2168 for (unsigned i = 0; i < pRaw->cPartitions; i++)
2169 {
2170 PVBOXHDDRAWPART pPart = &pRaw->pPartitions[i];
2171 if (pPart->cbPartitionData)
2172 {
2173 if (uStart != pPart->uPartitionDataStart)
2174 {
2175 pExtent = &pImage->pExtents[cExtents++];
2176 pExtent->pszBasename = NULL;
2177 pExtent->pszFullname = NULL;
2178 pExtent->enmType = VMDKETYPE_ZERO;
2179 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionDataStart - uStart);
2180 pExtent->uSectorOffset = 0;
2181 pExtent->enmAccess = VMDKACCESS_READWRITE;
2182 pExtent->fMetaDirty = false;
2183 }
2184 uStart = pPart->uPartitionDataStart + pPart->cbPartitionData;
2185 pExtent = &pImage->pExtents[cExtents++];
2186 /* Set up basename for extent description. Cannot use StrDup. */
2187 size_t cbBasename = strlen(pszPartition) + 1;
2188 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
2189 if (!pszBasename)
2190 {
2191 rc = VERR_NO_MEMORY;
2192 goto out;
2193 }
2194 memcpy(pszBasename, pszPartition, cbBasename);
2195 pExtent->pszBasename = pszBasename;
2196
2197 /* Set up full name for partition extent. */
2198 size_t cbDirname;
2199 char *pszDirname = RTStrDup(pImage->pszFilename);
2200 if (!pszDirname)
2201 {
2202 rc = VERR_NO_MEMORY;
2203 goto out;
2204 }
2205 RTPathStripFilename(pszDirname);
2206 cbDirname = strlen(pszDirname);
2207 char *pszFullname;
2208 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname,
2209 RTPATH_SLASH, pExtent->pszBasename);
2210 RTStrFree(pszDirname);
2211 if (VBOX_FAILURE(rc))
2212 goto out;
2213 pExtent->pszFullname = pszFullname;
2214 pExtent->enmType = VMDKETYPE_FLAT;
2215 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartitionData);
2216 pExtent->uSectorOffset = uPartOffset;
2217 pExtent->enmAccess = VMDKACCESS_READWRITE;
2218 pExtent->fMetaDirty = false;
2219
2220 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
2221 RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
2222 if (VBOX_FAILURE(rc))
2223 {
2224 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
2225 goto out;
2226 }
2227 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uPartOffset), pPart->pvPartitionData, pPart->cbPartitionData, NULL);
2228 if (VBOX_FAILURE(rc))
2229 {
2230 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
2231 goto out;
2232 }
2233 uPartOffset += VMDK_BYTE2SECTOR(pPart->cbPartitionData);
2234 }
2235 if (pPart->cbPartition)
2236 {
2237 if (uStart != pPart->uPartitionStart)
2238 {
2239 pExtent = &pImage->pExtents[cExtents++];
2240 pExtent->pszBasename = NULL;
2241 pExtent->pszFullname = NULL;
2242 pExtent->enmType = VMDKETYPE_ZERO;
2243 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->uPartitionStart - uStart);
2244 pExtent->uSectorOffset = 0;
2245 pExtent->enmAccess = VMDKACCESS_READWRITE;
2246 pExtent->fMetaDirty = false;
2247 }
2248 uStart = pPart->uPartitionStart + pPart->cbPartition;
2249 pExtent = &pImage->pExtents[cExtents++];
2250 if (pPart->pszRawDevice)
2251 {
2252 /* Set up basename for extent description. Cannot use StrDup. */
2253 size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
2254 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
2255 if (!pszBasename)
2256 {
2257 rc = VERR_NO_MEMORY;
2258 goto out;
2259 }
2260 memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
2261 pExtent->pszBasename = pszBasename;
2262 /* For raw disks the full name is identical to the base name. */
2263 pExtent->pszFullname = RTStrDup(pszBasename);
2264 if (!pExtent->pszFullname)
2265 {
2266 rc = VERR_NO_MEMORY;
2267 goto out;
2268 }
2269 pExtent->enmType = VMDKETYPE_FLAT;
2270 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition);
2271 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->uPartitionStartOffset);
2272 pExtent->enmAccess = VMDKACCESS_READWRITE;
2273 pExtent->fMetaDirty = false;
2274
2275 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
2276 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
2277 if (VBOX_FAILURE(rc))
2278 {
2279 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
2280 goto out;
2281 }
2282 }
2283 else
2284 {
2285 pExtent->pszBasename = NULL;
2286 pExtent->pszFullname = NULL;
2287 pExtent->enmType = VMDKETYPE_ZERO;
2288 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbPartition);
2289 pExtent->uSectorOffset = 0;
2290 pExtent->enmAccess = VMDKACCESS_READWRITE;
2291 pExtent->fMetaDirty = false;
2292 }
2293 }
2294 }
2295 /* Another extent for filling up the rest of the image. */
2296 if (uStart != cbSize)
2297 {
2298 pExtent = &pImage->pExtents[cExtents++];
2299 pExtent->pszBasename = NULL;
2300 pExtent->pszFullname = NULL;
2301 pExtent->enmType = VMDKETYPE_ZERO;
2302 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
2303 pExtent->uSectorOffset = 0;
2304 pExtent->enmAccess = VMDKACCESS_READWRITE;
2305 pExtent->fMetaDirty = false;
2306 }
2307
2308 pImage->enmImageType = enmType;
2309 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", "partitionedDevice");
2310 if (VBOX_FAILURE(rc))
2311 {
2312 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pszFilename);
2313 goto out;
2314 }
2315 }
2316 }
2317 else
2318 {
2319 rc = VERR_NOT_IMPLEMENTED;
2320 goto out;
2321 }
2322 }
2323 else
2324 {
2325 /* Normal (growing) image which is not split into pieces. */
2326 rc = vmdkCreateExtents(pImage, 1);
2327 if (VBOX_FAILURE(rc))
2328 {
2329 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pszFilename);
2330 goto out;
2331 }
2332 pExtent = &pImage->pExtents[0];
2333 pImage->File = NIL_RTFILE;
2334 rc = RTFileOpen(&pExtent->File, pszFilename,
2335 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
2336 if (VBOX_FAILURE(rc))
2337 {
2338 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pszFilename);
2339 goto out;
2340 }
2341
2342 /* Set up basename for extent description. Cannot use StrDup, as it is
2343 * not guaranteed that the memory can be freed with RTMemTmpFree, which
2344 * must be used as in other code paths StrDup is not usable. */
2345 char *pszBasenameSubstr = RTPathFilename(pszFilename);
2346 Assert(pszBasenameSubstr);
2347 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
2348 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
2349 if (!pszBasename)
2350 {
2351 rc = VERR_NO_MEMORY;
2352 goto out;
2353 }
2354 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
2355 pExtent->pszBasename = pszBasename;
2356 pExtent->pszFullname = RTStrDup(pszFilename);
2357 if (!pExtent->pszFullname)
2358 {
2359 rc = VERR_NO_MEMORY;
2360 goto out;
2361 }
2362 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
2363 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, 65536));
2364 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(65536);
2365 pExtent->uDescriptorSector = 1;
2366 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
2367 pExtent->cGTEntries = 512;
2368 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
2369 pExtent->cSectorsPerGDE = cSectorsPerGDE;
2370 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
2371 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
2372 pExtent->enmAccess = VMDKACCESS_READWRITE;
2373 pExtent->fUncleanShutdown = true;
2374 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
2375 pExtent->uSectorOffset = 0;
2376 pExtent->fMetaDirty = true;
2377
2378 rc = vmdkCreateGrainDirectory(pExtent, pExtent->uDescriptorSector + pExtent->cDescriptorSectors, true);
2379 if (VBOX_FAILURE(rc))
2380 {
2381 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pszFilename);
2382 goto out;
2383 }
2384
2385 pImage->enmImageType = enmType;
2386 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType", "monolithicSparse");
2387 if (VBOX_FAILURE(rc))
2388 {
2389 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pszFilename);
2390 goto out;
2391 }
2392
2393 /* The descriptor is part of the extent, move info to extent. */
2394 pExtent->pDescData = pImage->pDescData;
2395 pImage->pDescData = NULL;
2396 }
2397
2398 pImage->cbSize = cbSize;
2399 if (pImage->cCylinders >= 1024 || pImage->cHeads != 16)
2400 pImage->enmTranslation = PDMBIOSTRANSLATION_LBA;
2401 else
2402 pImage->enmTranslation = PDMBIOSTRANSLATION_NONE;
2403
2404 for (unsigned i = 0; i < pImage->cExtents; i++)
2405 {
2406 pExtent = &pImage->pExtents[i];
2407
2408 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
2409 pExtent->cNominalSectors, pExtent->enmType,
2410 pExtent->pszBasename, pExtent->uSectorOffset);
2411 if (VBOX_FAILURE(rc))
2412 {
2413 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pszFilename);
2414 goto out;
2415 }
2416 }
2417 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
2418
2419 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2420 "ddb.geometry.cylinders", cCylinders);
2421 if (VBOX_FAILURE(rc))
2422 goto out;
2423 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2424 "ddb.geometry.heads", cHeads);
2425 if (VBOX_FAILURE(rc))
2426 goto out;
2427 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2428 "ddb.geometry.sectors", cSectors);
2429 if (VBOX_FAILURE(rc))
2430 goto out;
2431
2432 pImage->cCylinders = cCylinders;
2433 pImage->cHeads = cHeads;
2434 pImage->cSectors = cSectors;
2435
2436 rc = RTUuidCreate(&pImage->ImageUuid);
2437 if (VBOX_FAILURE(rc))
2438 goto out;
2439 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2440 "ddb.uuid.image", &pImage->ImageUuid);
2441 if (VBOX_FAILURE(rc))
2442 {
2443 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in new descriptor in '%s'"), pszFilename);
2444 goto out;
2445 }
2446 RTUuidClear(&pImage->ParentUuid);
2447 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2448 "ddb.uuid.parent", &pImage->ParentUuid);
2449 if (VBOX_FAILURE(rc))
2450 {
2451 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pszFilename);
2452 goto out;
2453 }
2454 RTUuidClear(&pImage->ModificationUuid);
2455 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2456 "ddb.uuid.modification", &pImage->ModificationUuid);
2457 if (VBOX_FAILURE(rc))
2458 {
2459 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pszFilename);
2460 goto out;
2461 }
2462
2463 rc = vmdkAllocateGrainTableCache(pImage);
2464 if (VBOX_FAILURE(rc))
2465 goto out;
2466
2467 rc = vmdkSetImageComment(pImage, pszComment);
2468 if (VBOX_FAILURE(rc))
2469 {
2470 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pszFilename);
2471 goto out;
2472 }
2473
2474 rc = vmdkFlushImage(pImage);
2475
2476out:
2477 if (VBOX_FAILURE(rc))
2478 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
2479 return rc;
2480}
2481
2482static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
2483{
2484 char *pszCommentEncoded;
2485 if (pszComment)
2486 {
2487 pszCommentEncoded = vmdkEncodeString(pszComment);
2488 if (!pszCommentEncoded)
2489 return VERR_NO_MEMORY;
2490 }
2491 else
2492 pszCommentEncoded = NULL;
2493 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
2494 "ddb.comment", pszCommentEncoded);
2495 if (pszComment)
2496 RTStrFree(pszCommentEncoded);
2497 if (VBOX_FAILURE(rc))
2498 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
2499 return VINF_SUCCESS;
2500}
2501
2502static void vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete)
2503{
2504 if (pImage->enmImageType)
2505 {
2506 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2507 {
2508 /* Mark all extents as clean. */
2509 for (unsigned i = 0; i < pImage->cExtents; i++)
2510 {
2511 if (( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
2512#ifdef VBOX_WITH_VMDK_ESX
2513 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
2514#endif /* VBOX_WITH_VMDK_ESX */
2515 )
2516 && pImage->pExtents[i].fUncleanShutdown)
2517 {
2518 pImage->pExtents[i].fUncleanShutdown = false;
2519 pImage->pExtents[i].fMetaDirty = true;
2520 }
2521 }
2522 }
2523 (void)vmdkFlushImage(pImage);
2524 }
2525 if (pImage->pExtents != NULL)
2526 {
2527 for (unsigned i = 0 ; i < pImage->cExtents; i++)
2528 vmdkFreeExtentData(&pImage->pExtents[i], fDelete);
2529 RTMemFree(pImage->pExtents);
2530 pImage->pExtents = NULL;
2531 }
2532 if (pImage->File != NIL_RTFILE)
2533 {
2534 RTFileClose(pImage->File);
2535 pImage->File = NIL_RTFILE;
2536 }
2537 if (fDelete && pImage->pszFilename)
2538 RTFileDelete(pImage->pszFilename);
2539}
2540
2541static int vmdkFlushImage(PVMDKIMAGE pImage)
2542{
2543 PVMDKEXTENT pExtent;
2544 int rc = VINF_SUCCESS;
2545
2546 /* Update descriptor if changed. */
2547 if (pImage->Descriptor.fDirty)
2548 {
2549 rc = vmdkWriteDescriptor(pImage);
2550 if (VBOX_FAILURE(rc))
2551 goto out;
2552 }
2553
2554 for (unsigned i = 0; i < pImage->cExtents; i++)
2555 {
2556 pExtent = &pImage->pExtents[i];
2557 if (pExtent->File != NIL_RTFILE && pExtent->fMetaDirty)
2558 {
2559 switch (pExtent->enmType)
2560 {
2561 case VMDKETYPE_HOSTED_SPARSE:
2562 rc = vmdkWriteMetaSparseExtent(pExtent);
2563 if (VBOX_FAILURE(rc))
2564 goto out;
2565 break;
2566#ifdef VBOX_WITH_VMDK_ESX
2567 case VMDKETYPE_ESX_SPARSE:
2568 /** @todo update the header. */
2569 break;
2570#endif /* VBOX_WITH_VMDK_ESX */
2571 case VMDKETYPE_FLAT:
2572 /* Nothing to do. */
2573 break;
2574 case VMDKETYPE_ZERO:
2575 default:
2576 AssertMsgFailed(("extent with type %d marked as dirty\n",
2577 pExtent->enmType));
2578 break;
2579 }
2580 }
2581 switch (pExtent->enmType)
2582 {
2583 case VMDKETYPE_HOSTED_SPARSE:
2584#ifdef VBOX_WITH_VMDK_ESX
2585 case VMDKETYPE_ESX_SPARSE:
2586#endif /* VBOX_WITH_VMDK_ESX */
2587 case VMDKETYPE_FLAT:
2588 /** @todo implement proper path absolute check. */
2589 if (pExtent->File != NIL_RTFILE && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) && !(pExtent->pszBasename[0] == RTPATH_SLASH))
2590 rc = RTFileFlush(pExtent->File);
2591 break;
2592 case VMDKETYPE_ZERO:
2593 /* No need to do anything for this extent. */
2594 break;
2595 default:
2596 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
2597 break;
2598 }
2599 }
2600
2601out:
2602 return rc;
2603}
2604
2605static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector, PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
2606{
2607 PVMDKEXTENT pExtent = NULL;
2608 int rc = VINF_SUCCESS;
2609
2610 for (unsigned i = 0; i < pImage->cExtents; i++)
2611 {
2612 if (offSector < pImage->pExtents[i].cNominalSectors)
2613 {
2614 pExtent = &pImage->pExtents[i];
2615 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
2616 break;
2617 }
2618 offSector -= pImage->pExtents[i].cNominalSectors;
2619 }
2620
2621 if (pExtent)
2622 *ppExtent = pExtent;
2623 else
2624 rc = VERR_IO_SECTOR_NOT_FOUND;
2625
2626 return rc;
2627}
2628
2629static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector, unsigned uExtent)
2630{
2631 /** @todo this hash function is quite simple, maybe use a better one which
2632 * scrambles the bits better. */
2633 return (uSector + uExtent) % pCache->cEntries;
2634}
2635
2636static int vmdkGetSector(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
2637 uint64_t uSector, uint64_t *puExtentSector)
2638{
2639 uint64_t uGDIndex, uGTSector, uGTBlock;
2640 uint32_t uGTHash, uGTBlockIndex;
2641 PVMDKGTCACHEENTRY pGTCacheEntry;
2642 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
2643 int rc;
2644
2645 uGDIndex = uSector / pExtent->cSectorsPerGDE;
2646 if (uGDIndex >= pExtent->cGDEntries)
2647 return VERR_OUT_OF_RANGE;
2648 uGTSector = pExtent->pGD[uGDIndex];
2649 if (!uGTSector)
2650 {
2651 /* There is no grain table referenced by this grain directory
2652 * entry. So there is absolutely no data in this area. */
2653 *puExtentSector = 0;
2654 return VINF_SUCCESS;
2655 }
2656
2657 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
2658 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
2659 pGTCacheEntry = &pCache->aGTCache[uGTHash];
2660 if ( pGTCacheEntry->uExtent != pExtent->uExtent
2661 || pGTCacheEntry->uGTBlock != uGTBlock)
2662 {
2663 /* Cache miss, fetch data from disk. */
2664 rc = RTFileReadAt(pExtent->File,
2665 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
2666 aGTDataTmp, sizeof(aGTDataTmp), NULL);
2667 if (VBOX_FAILURE(rc))
2668 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in '%s'"), pExtent->pszFullname);
2669 pGTCacheEntry->uExtent = pExtent->uExtent;
2670 pGTCacheEntry->uGTBlock = uGTBlock;
2671 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
2672 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
2673 }
2674 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
2675 uint64_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
2676 if (uGrainSector)
2677 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
2678 else
2679 *puExtentSector = 0;
2680 return VINF_SUCCESS;
2681}
2682
2683/**
2684 * Internal. Allocates a new grain table (if necessary), writes the grain
2685 * and updates the grain table. The cache is also updated by this operation.
2686 * This is separate from vmdkGetSector, because that should be as fast as
2687 * possible. Most code from vmdkGetSector also appears here.
2688 */
2689static int vmdkAllocGrain(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
2690 uint64_t uSector, const void *pvBuf, uint64_t cbWrite)
2691{
2692 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
2693 uint64_t cbExtentSize;
2694 uint32_t uGTHash, uGTBlockIndex;
2695 PVMDKGTCACHEENTRY pGTCacheEntry;
2696 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
2697 int rc;
2698
2699 uGDIndex = uSector / pExtent->cSectorsPerGDE;
2700 if (uGDIndex >= pExtent->cGDEntries)
2701 return VERR_OUT_OF_RANGE;
2702 uGTSector = pExtent->pGD[uGDIndex];
2703 uRGTSector = pExtent->pRGD[uGDIndex];
2704 if (!uGTSector)
2705 {
2706 /* There is no grain table referenced by this grain directory
2707 * entry. So there is absolutely no data in this area. Allocate
2708 * a new grain table and put the reference to it in the GDs. */
2709 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
2710 if (VBOX_FAILURE(rc))
2711 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
2712 Assert(!(cbExtentSize % 512));
2713 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
2714 /* Normally the grain table is preallocated for hosted sparse extents
2715 * that support more than 32 bit sector numbers. So this shouldn't
2716 * ever happen on a valid extent. */
2717 if (uGTSector > UINT32_MAX)
2718 return VERR_VDI_INVALID_HEADER;
2719 /* Write grain table by writing the required number of grain table
2720 * cache chunks. Avoids dynamic memory allocation, but is a bit
2721 * slower. But as this is a pretty infrequently occurring case it
2722 * should be acceptable. */
2723 memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
2724 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
2725 {
2726 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
2727 if (VBOX_FAILURE(rc))
2728 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
2729 }
2730 if (pExtent->pRGD)
2731 {
2732 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
2733 if (VBOX_FAILURE(rc))
2734 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
2735 Assert(!(cbExtentSize % 512));
2736 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
2737 /* Write backup grain table by writing the required number of grain
2738 * table cache chunks. Avoids dynamic memory allocation, but is a
2739 * bit slower. But as this is a pretty infrequently occurring case
2740 * it should be acceptable. */
2741 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
2742 {
2743 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
2744 if (VBOX_FAILURE(rc))
2745 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
2746 }
2747 }
2748
2749 /* Update the grain directory on disk (doing it before writing the
2750 * grain table will result in a garbled extent if the operation is
2751 * aborted for some reason. Otherwise the worst that can happen is
2752 * some unused sectors in the extent. */
2753 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
2754 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE), &uGTSectorLE, sizeof(uGTSectorLE), NULL);
2755 if (VBOX_FAILURE(rc))
2756 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
2757 if (pExtent->pRGD)
2758 {
2759 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
2760 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE), &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
2761 if (VBOX_FAILURE(rc))
2762 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
2763 }
2764
2765 /* As the final step update the in-memory copy of the GDs. */
2766 pExtent->pGD[uGDIndex] = uGTSector;
2767 if (pExtent->pRGD)
2768 pExtent->pRGD[uGDIndex] = uRGTSector;
2769 }
2770
2771 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
2772 if (VBOX_FAILURE(rc))
2773 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
2774 Assert(!(cbExtentSize % 512));
2775
2776 /* Write the data. */
2777 rc = RTFileWriteAt(pExtent->File, cbExtentSize, pvBuf, cbWrite, NULL);
2778 if (VBOX_FAILURE(rc))
2779 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
2780
2781 /* Update the grain table (and the cache). */
2782 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
2783 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
2784 pGTCacheEntry = &pCache->aGTCache[uGTHash];
2785 if ( pGTCacheEntry->uExtent != pExtent->uExtent
2786 || pGTCacheEntry->uGTBlock != uGTBlock)
2787 {
2788 /* Cache miss, fetch data from disk. */
2789 rc = RTFileReadAt(pExtent->File,
2790 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
2791 aGTDataTmp, sizeof(aGTDataTmp), NULL);
2792 if (VBOX_FAILURE(rc))
2793 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
2794 pGTCacheEntry->uExtent = pExtent->uExtent;
2795 pGTCacheEntry->uGTBlock = uGTBlock;
2796 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
2797 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
2798 }
2799 else
2800 {
2801 /* Cache hit. Convert grain table block back to disk format, otherwise
2802 * the code below will write garbage for all but the updated entry. */
2803 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
2804 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
2805 }
2806 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
2807 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(cbExtentSize));
2808 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(cbExtentSize);
2809 /* Update grain table on disk. */
2810 rc = RTFileWriteAt(pExtent->File,
2811 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
2812 aGTDataTmp, sizeof(aGTDataTmp), NULL);
2813 if (VBOX_FAILURE(rc))
2814 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
2815 if (pExtent->pRGD)
2816 {
2817 /* Update backup grain table on disk. */
2818 rc = RTFileWriteAt(pExtent->File,
2819 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
2820 aGTDataTmp, sizeof(aGTDataTmp), NULL);
2821 if (VBOX_FAILURE(rc))
2822 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
2823 }
2824#ifdef VBOX_WITH_VMDK_ESX
2825 if (VBOX_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
2826 {
2827 pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
2828 pExtent->fMetaDirty = true;
2829 }
2830#endif /* VBOX_WITH_VMDK_ESX */
2831 return rc;
2832}
2833
2834static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData)
2835{
2836 int rc;
2837 PVMDKIMAGE pImage;
2838
2839 /** @todo check the image file name for invalid characters, especially double quotes. */
2840
2841 /* Check open flags. All valid flags are supported. */
2842 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
2843 {
2844 rc = VERR_INVALID_PARAMETER;
2845 goto out;
2846 }
2847
2848 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
2849 if (!pImage)
2850 {
2851 rc = VERR_NO_MEMORY;
2852 goto out;
2853 }
2854 pImage->pszFilename = pszFilename;
2855 pImage->File = NIL_RTFILE;
2856 pImage->pExtents = NULL;
2857 pImage->pGTCache = NULL;
2858 pImage->pDescData = NULL;
2859 pImage->pfnError = pfnError;
2860 pImage->pvErrorUser = pvErrorUser;
2861
2862 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
2863 if (VBOX_SUCCESS(rc))
2864 *ppvBackendData = pImage;
2865
2866out:
2867 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2868 return rc;
2869}
2870
2871static int vmdkCreate(const char *pszFilename, VDIMAGETYPE enmType,
2872 uint64_t cbSize, unsigned uImageFlags,
2873 const char *pszComment, uint32_t cCylinders,
2874 uint32_t cHeads, uint32_t cSectors, unsigned uOpenFlags,
2875 PFNVMPROGRESS pfnProgress, void *pvUser,
2876 PFNVDERROR pfnError, void *pvErrorUser,
2877 void **ppvBackendData)
2878{
2879 int rc;
2880 PVMDKIMAGE pImage;
2881
2882 /** @todo check the image file name for invalid characters, especially double quotes. */
2883
2884 /* Check open flags. All valid flags are supported. */
2885 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
2886 {
2887 rc = VERR_INVALID_PARAMETER;
2888 goto out;
2889 }
2890
2891 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
2892 if (!pImage)
2893 {
2894 rc = VERR_NO_MEMORY;
2895 goto out;
2896 }
2897 pImage->pszFilename = pszFilename;
2898 pImage->File = NIL_RTFILE;
2899 pImage->pExtents = NULL;
2900 pImage->pGTCache = NULL;
2901 pImage->pDescData = NULL;
2902 pImage->pfnError = pfnError;
2903 pImage->pvErrorUser = pvErrorUser;
2904 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
2905 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
2906 if (!pImage->pDescData)
2907 {
2908 rc = VERR_NO_MEMORY;
2909 goto out;
2910 }
2911
2912 rc = vmdkCreateImage(pImage, pszFilename, enmType, cbSize, uImageFlags,
2913 pszComment, cCylinders, cHeads, cSectors);
2914 if (VBOX_SUCCESS(rc))
2915 {
2916 /* So far the image is opened in read/write mode. Make sure the
2917 * image is opened in read-only mode if the caller requested that. */
2918 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
2919 {
2920 vmdkFreeImage(pImage, false);
2921 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
2922 if (VBOX_FAILURE(rc))
2923 goto out;
2924 }
2925 *ppvBackendData = pImage;
2926 }
2927
2928out:
2929 /** @todo implement meaningful progress stuff (especially for fixed images). */
2930 if ( VBOX_SUCCESS(rc)
2931 && pfnProgress)
2932 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2933
2934 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2935 return rc;
2936}
2937
2938static int vmdkClose(void *pBackendData, bool fDelete)
2939{
2940 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2941 int rc = VINF_SUCCESS;
2942
2943 /* Freeing a never allocated image (e.g. because the open failed) is
2944 * not signalled as an error. After all nothing bad happens. */
2945 if (pImage)
2946 vmdkFreeImage(pImage, fDelete);
2947
2948 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2949 return rc;
2950}
2951
2952static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbActuallyRead)
2953{
2954 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2955 PVMDKEXTENT pExtent;
2956 uint64_t uSectorExtentRel;
2957 uint64_t uSectorExtentAbs;
2958 int rc;
2959
2960 Assert(uOffset % 512 == 0);
2961 Assert(cbRead % 512 == 0);
2962
2963 if (uOffset + cbRead > pImage->cbSize)
2964 {
2965 rc = VERR_INVALID_PARAMETER;
2966 goto out;
2967 }
2968
2969 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
2970 &pExtent, &uSectorExtentRel);
2971 if (VBOX_FAILURE(rc))
2972 goto out;
2973
2974 /* Check access permissions as defined in the extent descriptor. */
2975 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
2976 {
2977 rc = VERR_VDI_INVALID_STATE;
2978 goto out;
2979 }
2980
2981 /* Clip read range to remain in this extent. */
2982 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
2983
2984 /* Handle the read according to the current extent type. */
2985 switch (pExtent->enmType)
2986 {
2987 case VMDKETYPE_HOSTED_SPARSE:
2988#ifdef VBOX_WITH_VMDK_ESX
2989 case VMDKETYPE_ESX_SPARSE:
2990#endif /* VBOX_WITH_VMDK_ESX */
2991 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
2992 &uSectorExtentAbs);
2993 if (VBOX_FAILURE(rc))
2994 goto out;
2995 /* Clip read range to at most the rest of the grain. */
2996 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
2997 Assert(!(cbRead % 512));
2998 if (uSectorExtentAbs == 0)
2999 rc = VINF_VDI_BLOCK_FREE;
3000 else
3001 rc = RTFileReadAt(pExtent->File,
3002 VMDK_SECTOR2BYTE(uSectorExtentAbs),
3003 pvBuf, cbRead, NULL);
3004 break;
3005 case VMDKETYPE_FLAT:
3006 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel),
3007 pvBuf, cbRead, NULL);
3008 break;
3009 case VMDKETYPE_ZERO:
3010 memset(pvBuf, '\0', cbRead);
3011 break;
3012 }
3013 *pcbActuallyRead = cbRead;
3014
3015out:
3016 return rc;
3017}
3018
3019static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead)
3020{
3021 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3022 PVMDKEXTENT pExtent;
3023 uint64_t uSectorExtentRel;
3024 uint64_t uSectorExtentAbs;
3025 int rc;
3026
3027 Assert(uOffset % 512 == 0);
3028 Assert(cbWrite % 512 == 0);
3029
3030 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3031 {
3032 rc = VERR_VDI_IMAGE_READ_ONLY;
3033 goto out;
3034 }
3035
3036 /* No size check here, will do that later when the extent is located.
3037 * There are sparse images out there which according to the spec are
3038 * invalid, because the total size is not a multiple of the grain size.
3039 * Also for sparse images which are stitched together in odd ways (not at
3040 * grain boundaries, and with the nominal size not being a multiple of the
3041 * grain size), this would prevent writing to the last grain. */
3042
3043 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
3044 &pExtent, &uSectorExtentRel);
3045 if (VBOX_FAILURE(rc))
3046 goto out;
3047
3048 /* Check access permissions as defined in the extent descriptor. */
3049 if (pExtent->enmAccess != VMDKACCESS_READWRITE)
3050 {
3051 rc = VERR_VDI_INVALID_STATE;
3052 goto out;
3053 }
3054
3055 /** @todo implement suppressing of zero data writes (a bit tricky in this
3056 * case, as VMDK has no marker for zero blocks). We somehow need to get the
3057 * information whether the information in this area is all zeroes as of the
3058 * parent image. Then (based on the assumption that parent images are
3059 * immutable) the write can be ignored. */
3060
3061 /* Handle the write according to the current extent type. */
3062 switch (pExtent->enmType)
3063 {
3064 case VMDKETYPE_HOSTED_SPARSE:
3065#ifdef VBOX_WITH_VMDK_ESX
3066 case VMDKETYPE_ESX_SPARSE:
3067#endif /* VBOX_WITH_VMDK_ESX */
3068 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
3069 &uSectorExtentAbs);
3070 if (VBOX_FAILURE(rc))
3071 goto out;
3072 /* Clip write range to at most the rest of the grain. */
3073 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
3074 if (uSectorExtentAbs == 0)
3075 {
3076 if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
3077 {
3078 /* Full block write to a previously unallocated block.
3079 * Allocate GT and find out where to store the grain. */
3080 rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
3081 uSectorExtentRel, pvBuf, cbWrite);
3082 *pcbPreRead = 0;
3083 *pcbPostRead = 0;
3084 }
3085 else
3086 {
3087 /* Clip write range to remain in this extent. */
3088 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
3089 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
3090 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
3091 rc = VINF_VDI_BLOCK_FREE;
3092 }
3093 }
3094 else
3095 rc = RTFileWriteAt(pExtent->File,
3096 VMDK_SECTOR2BYTE(uSectorExtentAbs),
3097 pvBuf, cbWrite, NULL);
3098 break;
3099 case VMDKETYPE_FLAT:
3100 /* Clip write range to remain in this extent. */
3101 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
3102 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel), pvBuf, cbWrite, NULL);
3103 break;
3104 case VMDKETYPE_ZERO:
3105 /* Clip write range to remain in this extent. */
3106 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
3107 break;
3108 }
3109 if (pcbWriteProcess)
3110 *pcbWriteProcess = cbWrite;
3111
3112out:
3113 return rc;
3114}
3115
3116static int vmdkFlush(void *pBackendData)
3117{
3118 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3119
3120 int rc = vmdkFlushImage(pImage);
3121
3122 return rc;
3123}
3124
3125static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
3126{
3127 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3128 int rc = VINF_SUCCESS;
3129
3130 Assert(pImage);
3131 Assert(penmImageType);
3132
3133 if (pImage && pImage->cExtents != 0)
3134 *penmImageType = pImage->enmImageType;
3135 else
3136 rc = VERR_VDI_NOT_OPENED;
3137
3138 return rc;
3139}
3140
3141static uint64_t vmdkGetSize(void *pBackendData)
3142{
3143 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3144
3145 Assert(pImage);
3146
3147 if (pImage)
3148 return pImage->cbSize;
3149 else
3150 return 0;
3151}
3152
3153static int vmdkGetGeometry(void *pBackendData, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
3154{
3155 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3156 int rc;
3157
3158 Assert(pImage);
3159
3160 if (pImage)
3161 {
3162 if (pImage->cCylinders)
3163 {
3164 *pcCylinders = pImage->cCylinders;
3165 *pcHeads = pImage->cHeads;
3166 *pcSectors = pImage->cSectors;
3167 rc = VINF_SUCCESS;
3168 }
3169 else
3170 rc = VERR_VDI_GEOMETRY_NOT_SET;
3171 }
3172 else
3173 rc = VERR_VDI_NOT_OPENED;
3174 LogFlow(("%s: returned %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
3175 pImage->cCylinders, pImage->cHeads, pImage->cSectors));
3176 return rc;
3177}
3178
3179static int vmdkSetGeometry(void *pBackendData, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
3180{
3181 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3182 int rc;
3183
3184 Assert(pImage);
3185
3186 if (pImage)
3187 {
3188 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3189 {
3190 rc = VERR_VDI_IMAGE_READ_ONLY;
3191 goto out;
3192 }
3193 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
3194 "ddb.geometry.cylinders", cCylinders);
3195 if (VBOX_FAILURE(rc))
3196 goto out;
3197 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
3198 "ddb.geometry.heads", cHeads);
3199 if (VBOX_FAILURE(rc))
3200 goto out;
3201 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
3202 "ddb.geometry.sectors", cSectors);
3203 if (VBOX_FAILURE(rc))
3204 goto out;
3205
3206 pImage->cCylinders = cCylinders;
3207 pImage->cHeads = cHeads;
3208 pImage->cSectors = cSectors;
3209 rc = VINF_SUCCESS;
3210 }
3211 else
3212 rc = VERR_VDI_NOT_OPENED;
3213
3214out:
3215 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3216 return rc;
3217}
3218
3219static int vmdkGetTranslation(void *pBackendData, PPDMBIOSTRANSLATION penmTranslation)
3220{
3221 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3222 int rc;
3223
3224 Assert(pImage);
3225
3226 if (pImage)
3227 {
3228 if (pImage->enmTranslation)
3229 {
3230 *penmTranslation = pImage->enmTranslation;
3231 rc = VINF_SUCCESS;
3232 }
3233 else
3234 rc = VERR_VDI_GEOMETRY_NOT_SET;
3235 }
3236 else
3237 rc = VERR_VDI_NOT_OPENED;
3238 LogFlow(("%s: returned %Vrc (%d)\n", __FUNCTION__, rc,
3239 pImage->enmTranslation));
3240 return rc;
3241}
3242
3243static int vmdkSetTranslation(void *pBackendData, PDMBIOSTRANSLATION enmTranslation)
3244{
3245 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3246 int rc;
3247
3248 Assert(pImage);
3249
3250 if (pImage)
3251 {
3252 /** @todo maybe store this in the image descriptor */
3253 pImage->enmTranslation = enmTranslation;
3254 rc = VINF_SUCCESS;
3255 }
3256 else
3257 rc = VERR_VDI_NOT_OPENED;
3258 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3259 return rc;
3260}
3261
3262static unsigned vmdkGetOpenFlags(void *pBackendData)
3263{
3264 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3265 unsigned uOpenFlags;
3266
3267 Assert(pImage);
3268
3269 if (pImage)
3270 uOpenFlags = pImage->uOpenFlags;
3271 else
3272 uOpenFlags = 0;
3273
3274 LogFlow(("%s: returned %d\n", __FUNCTION__, uOpenFlags));
3275 return uOpenFlags;
3276}
3277
3278static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
3279{
3280 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3281 int rc;
3282 const char *pszFilename;
3283
3284 /* Image must be opened and the new flags must be valid. Just readonly flag
3285 * is supported. */
3286 if (!pImage || uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
3287 {
3288 rc = VERR_INVALID_PARAMETER;
3289 goto out;
3290 }
3291
3292 /* Implement this operation via reopening the image. */
3293 pszFilename = pImage->pszFilename;
3294 vmdkFreeImage(pImage, false);
3295 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
3296
3297out:
3298 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3299 return rc;
3300}
3301
3302static int vmdkGetComment(void *pBackendData, char *pszComment, size_t cbComment)
3303{
3304 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3305 int rc;
3306
3307 Assert(pImage);
3308
3309 if (pImage)
3310 {
3311 const char *pszCommentEncoded = NULL;
3312 rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
3313 "ddb.comment", &pszCommentEncoded);
3314 if (rc == VERR_VDI_VALUE_NOT_FOUND)
3315 pszCommentEncoded = NULL;
3316 else if (VBOX_FAILURE(rc))
3317 goto out;
3318
3319 if (pszComment)
3320 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
3321 else
3322 {
3323 *pszComment = '\0';
3324 rc = VINF_SUCCESS;
3325 }
3326 if (pszCommentEncoded)
3327 RTStrFree((char *)(void *)pszCommentEncoded);
3328 }
3329 else
3330 rc = VERR_VDI_NOT_OPENED;
3331
3332out:
3333 LogFlow(("%s: returned %Vrc comment='%s'\n", __FUNCTION__, rc, pszComment));
3334 return rc;
3335}
3336
3337static int vmdkSetComment(void *pBackendData, const char *pszComment)
3338{
3339 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3340 int rc;
3341
3342 LogFlow(("%s: comment '%s'\n", pszComment));
3343 Assert(pImage);
3344
3345 if (pImage)
3346 {
3347 rc = vmdkSetImageComment(pImage, pszComment);
3348 }
3349 else
3350 rc = VERR_VDI_NOT_OPENED;
3351
3352 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3353 return rc;
3354}
3355
3356static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
3357{
3358 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3359 int rc;
3360
3361 Assert(pImage);
3362
3363 if (pImage)
3364 {
3365 *pUuid = pImage->ImageUuid;
3366 rc = VINF_SUCCESS;
3367 }
3368 else
3369 rc = VERR_VDI_NOT_OPENED;
3370 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
3371 return rc;
3372}
3373
3374static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
3375{
3376 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3377 int rc;
3378
3379 LogFlow(("%s: %Vuuid\n", pUuid));
3380 Assert(pImage);
3381
3382 if (pImage)
3383 {
3384 pImage->ImageUuid = *pUuid;
3385 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3386 "ddb.uuid.image", pUuid);
3387 if (VBOX_FAILURE(rc))
3388 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
3389 rc = VINF_SUCCESS;
3390 }
3391 else
3392 rc = VERR_VDI_NOT_OPENED;
3393 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3394 return rc;
3395}
3396
3397static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
3398{
3399 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3400 int rc;
3401
3402 Assert(pImage);
3403
3404 if (pImage)
3405 {
3406 *pUuid = pImage->ModificationUuid;
3407 rc = VINF_SUCCESS;
3408 }
3409 else
3410 rc = VERR_VDI_NOT_OPENED;
3411 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
3412 return rc;
3413}
3414
3415static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
3416{
3417 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3418 int rc;
3419
3420 LogFlow(("%s: %Vuuid\n", pUuid));
3421 Assert(pImage);
3422
3423 if (pImage)
3424 {
3425 pImage->ModificationUuid = *pUuid;
3426 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3427 "ddb.uuid.modification", pUuid);
3428 if (VBOX_FAILURE(rc))
3429 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
3430 rc = VINF_SUCCESS;
3431 }
3432 else
3433 rc = VERR_VDI_NOT_OPENED;
3434 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3435 return rc;
3436}
3437
3438static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
3439{
3440 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3441 int rc;
3442
3443 Assert(pImage);
3444
3445 if (pImage)
3446 {
3447 *pUuid = pImage->ParentUuid;
3448 rc = VINF_SUCCESS;
3449 }
3450 else
3451 rc = VERR_VDI_NOT_OPENED;
3452 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
3453 return rc;
3454}
3455
3456static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
3457{
3458 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
3459 int rc;
3460
3461 LogFlow(("%s: %Vuuid\n", pUuid));
3462 Assert(pImage);
3463
3464 if (pImage)
3465 {
3466 pImage->ParentUuid = *pUuid;
3467 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
3468 "ddb.uuid.parent", pUuid);
3469 if (VBOX_FAILURE(rc))
3470 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
3471 rc = VINF_SUCCESS;
3472 }
3473 else
3474 rc = VERR_VDI_NOT_OPENED;
3475 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
3476 return rc;
3477}
3478
3479
3480VBOXHDDBACKEND g_VmdkBackend =
3481{
3482 /* cbSize */
3483 sizeof(VBOXHDDBACKEND),
3484 /* pfnCheckIfValid */
3485 NULL,
3486 /* pfnOpen */
3487 vmdkOpen,
3488 /* pfnCreate */
3489 vmdkCreate,
3490 /* pfnClose */
3491 vmdkClose,
3492 /* pfnRead */
3493 vmdkRead,
3494 /* pfnWrite */
3495 vmdkWrite,
3496 /* pfnFlush */
3497 vmdkFlush,
3498 /* pfnGetImageType */
3499 vmdkGetImageType,
3500 /* pfnGetSize */
3501 vmdkGetSize,
3502 /* pfnGetGeometry */
3503 vmdkGetGeometry,
3504 /* pfnSetGeometry */
3505 vmdkSetGeometry,
3506 /* pfnGetTranslation */
3507 vmdkGetTranslation,
3508 /* pfnSetTranslation */
3509 vmdkSetTranslation,
3510 /* pfnGetOpenFlags */
3511 vmdkGetOpenFlags,
3512 /* pfnSetOpenFlags */
3513 vmdkSetOpenFlags,
3514 /* pfnGetComment */
3515 vmdkGetComment,
3516 /* pfnSetComment */
3517 vmdkSetComment,
3518 /* pfnGetUuid */
3519 vmdkGetUuid,
3520 /* pfnSetUuid */
3521 vmdkSetUuid,
3522 /* pfnGetModificationUuid */
3523 vmdkGetModificationUuid,
3524 /* pfnSetModificationUuid */
3525 vmdkSetModificationUuid,
3526 /* pfnGetParentUuid */
3527 vmdkGetParentUuid,
3528 /* pfnSetParentUuid */
3529 vmdkSetParentUuid
3530};
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