VirtualBox

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

Last change on this file since 2360 was 2360, checked in by vboxsync, 18 years ago

Shut up gcc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.5 KB
Line 
1/** $Id: VmdkHDDCore.cpp 2360 2007-04-26 17:09:34Z vboxsync $ */
2/** @file
3 * VMDK Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD_VMDK
26#include "VBoxHDD-newInternal.h"
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include <iprt/alloc.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/path.h>
35#include <iprt/string.h>
36
37
38/*******************************************************************************
39* Constants And Macros, Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * Magic number for hosted images created by VMware Workstation 4, VMware
44 * Workstation 5, VMware Server or VMware Player.
45 */
46#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
47
48/** VMDK hosted sparse extent header. */
49#pragma pack(1)
50typedef struct SparseExtentHeader
51{
52 uint32_t magicNumber;
53 uint32_t version;
54 uint32_t flags;
55 uint64_t capacity;
56 uint64_t grainSize;
57 uint64_t descriptorOffset;
58 uint64_t descriptorSize;
59 uint32_t numGTEsPerGT;
60 uint64_t rgdOffset;
61 uint64_t gdOffset;
62 uint64_t overHead;
63 bool uncleanShutdown;
64 char singleEndLineChar;
65 char nonEndLineChar;
66 char doubleEndLineChar1;
67 char doubleEndLineChar2;
68 uint8_t pad[435];
69} SparseExtentHeader;
70#pragma pack()
71
72
73#ifdef VBOX_WITH_VMDK_ESX
74
75/** @todo the ESX code is not tested, not used, and lacks error messages. */
76
77/**
78 * Magic number for images created by VMware GSX Server 3 or ESX Server 3.
79 */
80#define VMDK_ESX_SPARSE_MAGICNUMBER 0x44574f43 /* 'C' 'O' 'W' 'D' */
81
82#pragma pack(1)
83typedef struct COWDisk_Header
84{
85 uint32_t magicNumber;
86 uint32_t version;
87 uint32_t flags;
88 uint32_t numSectors;
89 uint32_t grainSize;
90 uint32_t gdOffset;
91 uint32_t numGDEntries;
92 uint32_t freeSector;
93 /* The spec incompletely documents quite a few further fields, but states
94 * that they are not used by the current format. Replace them by padding. */
95 char reserved1[1604];
96 uint32_t savedGeneration;
97 char reserved2[8];
98 uint32_t uncleanShutdown;
99 char padding[396];
100} COWDisk_Header;
101#pragma pack()
102#endif /* VBOX_WITH_VMDK_ESX */
103
104
105/** Convert sector number/size to byte offset/size. */
106#define VMDK_SECTOR2BYTE(u) ((u) << 9)
107
108/** Convert byte offset/size to sector number/size. */
109#define VMDK_BYTE2SECTOR(u) ((u) >> 9)
110
111/**
112 * VMDK extent type.
113 */
114typedef enum VMDKETYPE
115{
116 /** Hosted sparse extent. */
117 VMDKETYPE_HOSTED_SPARSE = 1,
118 /** Flat extent. */
119 VMDKETYPE_FLAT,
120 /** Zero extent. */
121 VMDKETYPE_ZERO
122#ifdef VBOX_WITH_VMDK_ESX
123 ,
124 /** ESX sparse extent. */
125 VMDKETYPE_ESX_SPARSE
126#endif /* VBOX_WITH_VMDK_ESX */
127} VMDKETYPE, *PVMDKETYPE;
128
129/**
130 * VMDK access type for a extent.
131 */
132typedef enum VMDKACCESS
133{
134 /** No access allowed. */
135 VMDKACCESS_NOACCESS = 0,
136 /** Read-only access. */
137 VMDKACCESS_READONLY,
138 /** Read-write access. */
139 VMDKACCESS_READWRITE
140} VMDKACCESS, *PVMDKACCESS;
141
142/**
143 * VMDK extent data structure.
144 */
145typedef struct VMDKEXTENT
146{
147 /** File handle. */
148 RTFILE File;
149 /** Base name of the image extent. */
150 char *pszBasename;
151 /** Full name of the image extent. */
152 char *pszFullname;
153 /** Number of sectors in this extent. */
154 uint64_t cSectors;
155 /** Number of sectors per block (grain in VMDK speak). */
156 uint64_t cSectorsPerGrain;
157 /** Starting sector number of descriptor. */
158 uint64_t uDescriptorSector;
159 /** Size of descriptor in sectors. */
160 uint64_t cDescriptorSectors;
161 /** Starting sector number of grain directory. */
162 uint64_t uSectorGD;
163 /** Starting sector number of redundant grain directory. */
164 uint64_t uSectorRGD;
165 /** Total number of metadata sectors. */
166 uint64_t uOverheadSectors;
167 /** Nominal size (i.e. as described by the descriptor) of this extent. */
168 uint64_t cNominalSectors;
169 /** Sector offset (i.e. as described by the descriptor) of this extent. */
170 uint64_t uSectorOffset;
171 /** Number of entries in a grain table. */
172 uint32_t cGTEntries;
173 /** Number of sectors reachable via a grain directory entry. */
174 uint32_t cSectorsPerGDE;
175 /** Number of entries in the grain directory. */
176 uint32_t cGDEntries;
177 /** Pointer to the next free sector. Legacy information. Do not use. */
178 uint32_t uFreeSector;
179 /** Number of this extent in the list of images. */
180 uint32_t uExtent;
181 /** Pointer to the descriptor (NULL if no descriptor in this extent). */
182 char *pDescData;
183 /** Pointer to the grain directory. */
184 uint32_t *pGD;
185 /** Pointer to the redundant grain directory. */
186 uint32_t *pRGD;
187 /** Type of this extent. */
188 VMDKETYPE enmType;
189 /** Access to this extent. */
190 VMDKACCESS enmAccess;
191 /** Flag whether this extent is marked as unclean. */
192 bool fUncleanShutdown;
193 /** Flag whether the metadata in the extent header needs to be updated. */
194 bool fMetaDirty;
195 /** Reference to the image in which this extent is used. Do not use this
196 * on a regular basis to avoid passing pImage references to functions
197 * explicitly. */
198 struct VMDKIMAGE *pImage;
199} VMDKEXTENT, *PVMDKEXTENT;
200
201/**
202 * Grain table cache size. Allocated per image.
203 */
204#define VMDK_GT_CACHE_SIZE 256
205
206/**
207 * Grain table block size. Smaller than an actual grain table block to allow
208 * more grain table blocks to be cached without having to allocate excessive
209 * amounts of memory for the cache.
210 */
211#define VMDK_GT_CACHELINE_SIZE 128
212
213
214/**
215 * Maximum number of lines in a descriptor file. Not worth the effort of
216 * making it variable. Descriptor files are generally very short (~20 lines).
217 */
218#define VMDK_DESCRIPTOR_LINES_MAX 100
219
220/**
221 * Parsed descriptor information. Allows easy access and update of the
222 * descriptor (whether separate file or not). Free form text files suck.
223 */
224typedef struct VMDKDESCRIPTOR
225{
226 /** Line number of first entry of the disk descriptor. */
227 unsigned uFirstDesc;
228 /** Line number of first entry in the extent description. */
229 unsigned uFirstExtent;
230 /** Line number of first disk database entry. */
231 unsigned uFirstDDB;
232 /** Total number of lines. */
233 unsigned cLines;
234 /** Total amount of memory available for the descriptor. */
235 size_t cbDescAlloc;
236 /** Set if descriptor has been changed and not yer written to disk. */
237 bool fDirty;
238 /** Array of pointers to the data in the descriptor. */
239 char *aLines[VMDK_DESCRIPTOR_LINES_MAX];
240 /** Array of line indices pointing to the next non-comment line. */
241 unsigned aNextLines[VMDK_DESCRIPTOR_LINES_MAX];
242} VMDKDESCRIPTOR, *PVMDKDESCRIPTOR;
243
244
245/**
246 * Cache entry for translating extent/sector to a sector number in that
247 * extent.
248 */
249typedef struct VMDKGTCACHEENTRY
250{
251 /** Extent number for which this entry is valid. */
252 uint32_t uExtent;
253 /** GT data block number. */
254 uint64_t uGTBlock;
255 /** Data part of the cache entry. */
256 uint32_t aGTData[VMDK_GT_CACHELINE_SIZE];
257} VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY;
258
259/**
260 * Cache data structure for blocks of grain table entries. For now this is a
261 * fixed size direct mapping cache, but this should be adapted to the size of
262 * the sparse image and maybe converted to a set-associative cache. The
263 * implementation below implements a write-through cache with write allocate.
264 */
265typedef struct VMDKGTCACHE
266{
267 /** Cache entries. */
268 VMDKGTCACHEENTRY aGTCache[VMDK_GT_CACHE_SIZE];
269 /** Number of cache entries (currently unused). */
270 unsigned cEntries;
271} VMDKGTCACHE, *PVMDKGTCACHE;
272
273/**
274 * Complete VMDK image data structure. Mainly a collection of extents and a few
275 * extra global data fields.
276 */
277typedef struct VMDKIMAGE
278{
279 PVMDKEXTENT pExtents;
280 unsigned cExtents;
281
282 /** Base image name. */
283 const char *pszFilename;
284 /** Descriptor file if applicable. */
285 RTFILE File;
286
287 /** Error callback. */
288 PFNVDERROR pfnError;
289 /** Opaque data for error callback. */
290 void *pvErrorUser;
291
292 /** Open flags passed by VBoxHD layer. */
293 unsigned uOpenFlags;
294 /** Image type. */
295 VDIMAGETYPE enmImageType;
296 /** Total size of the image. */
297 uint64_t cbSize;
298 /** BIOS translation mode. */
299 PDMBIOSTRANSLATION enmTranslation;
300 /** Physical geometry of this image, cylinders. */
301 uint32_t cCylinders;
302 /** Physical geometry of this image, heads. */
303 uint32_t cHeads;
304 /** Physical geometry of this image, sectors. */
305 uint32_t cSectors;
306 /** Image UUID. */
307 RTUUID ImageUuid;
308 /** Image modification UUID. */
309 RTUUID ModificationUuid;
310 /** Parent image UUID. */
311 RTUUID ParentUuid;
312
313 /** Pointer to the grain table cache, if this image contains sparse extents. */
314 PVMDKGTCACHE pGTCache;
315 /** Pointer to the descriptor (NULL if no separate descriptor file). */
316 char *pDescData;
317 /** Allocation size of the descriptor file. */
318 size_t cbDescAlloc;
319 /** Parsed descriptor file content. */
320 VMDKDESCRIPTOR Descriptor;
321} VMDKIMAGE, *PVMDKIMAGE;
322
323
324/*******************************************************************************
325* Internal Functions *
326*******************************************************************************/
327
328static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent);
329static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent);
330
331static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor);
332static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent);
333static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent);
334#ifdef VBOX_WITH_VMDK_ESX
335static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent);
336#endif /* VBOX_WITH_VMDK_ESX */
337static void vmdkFreeExtentData(PVMDKEXTENT pExtent);
338
339static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
340static int vmdkOpenImage(PVMDKIMAGE pImage, const char *pszFilename, unsigned uOpenFlags);
341static int vmdkFlushImage(PVMDKIMAGE pImage);
342static void vmdkFreeImage(PVMDKIMAGE pImage);
343
344static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData);
345static int vmdkClose(void *pBackendData);
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
357static int vmdkReadGrainDirectory(PVMDKEXTENT pExtent)
358{
359 int rc = VINF_SUCCESS;
360 unsigned i;
361 uint32_t *pGD = NULL, *pRGD = NULL, *pGDTmp, *pRGDTmp;
362 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
363
364 pGD = (uint32_t *)RTMemAllocZ(cbGD);
365 if (!pGD)
366 {
367 rc = VERR_NO_MEMORY;
368 goto out;
369 }
370 pExtent->pGD = pGD;
371 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD),
372 pGD, cbGD, NULL);
373 AssertRC(rc);
374 if (VBOX_FAILURE(rc))
375 {
376 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read grain directory for file '%s'"), pExtent->pszFullname);
377 goto out;
378 }
379 for (i = 0, pGDTmp = pGD; i < pExtent->cGDEntries; i++, pGDTmp++)
380 *pGDTmp = RT_LE2H_U32(*pGDTmp);
381
382 if (pExtent->uSectorRGD)
383 {
384 pRGD = (uint32_t *)RTMemAllocZ(cbGD);
385 if (!pRGD)
386 {
387 rc = VERR_NO_MEMORY;
388 goto out;
389 }
390 pExtent->pRGD = pRGD;
391 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
392 pRGD, cbGD, NULL);
393 AssertRC(rc);
394 if (VBOX_FAILURE(rc))
395 {
396 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: could not read redundant grain directory for file '%s'"), pExtent->pszFullname);
397 goto out;
398 }
399 for (i = 0, pRGDTmp = pRGD; i < pExtent->cGDEntries; i++, pRGDTmp++)
400 *pRGDTmp = RT_LE2H_U32(*pRGDTmp);
401
402 /* Check grain table and redundant grain table for consistency. */
403 size_t cbGT = pExtent->cGTEntries;
404 uint32_t *pTmpGT1 = (uint32_t *)RTMemTmpAlloc(cbGT);
405 if (!pTmpGT1)
406 {
407 rc = VERR_NO_MEMORY;
408 goto out;
409 }
410 uint32_t *pTmpGT2 = (uint32_t *)RTMemTmpAlloc(cbGT);
411 if (!pTmpGT2)
412 {
413 RTMemTmpFree(pTmpGT1);
414 rc = VERR_NO_MEMORY;
415 goto out;
416 }
417
418 for (i = 0, pGDTmp = pGD, pRGDTmp = pRGD; i < pExtent->cGDEntries; i++, pGDTmp++, pRGDTmp++)
419 {
420 /* If no grain table is allocated skip the entry. */
421 if (*pGDTmp == 0 && *pRGDTmp == 0)
422 continue;
423
424 if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
425 {
426 /* Just one grain directory entry refers to a not yet allocated
427 * grain table or both grain directory copies refer to the same
428 * grain table. Not allowed. */
429 RTMemTmpFree(pTmpGT1);
430 RTMemTmpFree(pTmpGT2);
431 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent references to grain directory in file '%s'"), pExtent->pszFullname);
432 goto out;
433 }
434 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pGDTmp),
435 pTmpGT1, cbGT, NULL);
436 if (VBOX_FAILURE(rc))
437 {
438 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading grain table in file '%s'"), pExtent->pszFullname);
439 RTMemTmpFree(pTmpGT1);
440 RTMemTmpFree(pTmpGT2);
441 goto out;
442 }
443 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(*pRGDTmp),
444 pTmpGT2, cbGT, NULL);
445 if (VBOX_FAILURE(rc))
446 {
447 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading backup grain table in file '%s'"), pExtent->pszFullname);
448 RTMemTmpFree(pTmpGT1);
449 RTMemTmpFree(pTmpGT2);
450 goto out;
451 }
452 if (memcmp(pTmpGT1, pTmpGT2, cbGT))
453 {
454 RTMemTmpFree(pTmpGT1);
455 RTMemTmpFree(pTmpGT2);
456 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistency between grain table and backup grain table"));
457 goto out;
458 }
459 }
460
461 /** @todo figure out what to do for unclean VMDKs. */
462 }
463
464out:
465 if (VBOX_FAILURE(rc))
466 vmdkFreeGrainDirectory(pExtent);
467 return rc;
468}
469
470static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)
471{
472 if (pExtent->pszBasename)
473 {
474 RTMemTmpFree(pExtent->pszBasename);
475 pExtent->pszBasename = NULL;
476 }
477 if (pExtent->pGD)
478 {
479 RTMemFree(pExtent->pGD);
480 pExtent->pGD = NULL;
481 }
482 if (pExtent->pRGD)
483 {
484 RTMemFree(pExtent->pRGD);
485 pExtent->pRGD = NULL;
486 }
487}
488
489static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr, char **ppszUnquoted, char **ppszNext)
490{
491 char *pszQ;
492 char *pszUnquoted;
493
494 /* Skip over whitespace. */
495 while (*pszStr == ' ' || *pszStr == '\t')
496 pszStr++;
497 if (*pszStr++ != '"')
498 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor"));
499
500 pszQ = strchr(pszStr, '"');
501 if (pszQ == NULL)
502 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor"));
503 pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);
504 if (!pszUnquoted)
505 return VERR_NO_MEMORY;
506 memcpy(pszUnquoted, pszStr, pszQ - pszStr);
507 pszUnquoted[pszQ - pszStr] = '\0';
508 *ppszUnquoted = pszUnquoted;
509 if (ppszNext)
510 *ppszNext = pszQ + 1;
511 return VINF_SUCCESS;
512}
513
514static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,
515 const char *pszKey, const char **ppszValue)
516{
517 size_t cbKey = strlen(pszKey);
518 const char *pszValue;
519
520 while (uStart != 0)
521 {
522 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
523 {
524 /* Key matches, check if there is a '=' (preceded by whitespace). */
525 pszValue = pDescriptor->aLines[uStart] + cbKey;
526 while (*pszValue == ' ' || *pszValue == '\t')
527 pszValue++;
528 if (*pszValue == '=')
529 {
530 *ppszValue = pszValue + 1;
531 break;
532 }
533 }
534 uStart = pDescriptor->aNextLines[uStart];
535 }
536 return !!uStart;
537}
538
539static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
540 unsigned uStart,
541 const char *pszKey, const char *pszValue)
542{
543 char *pszTmp;
544 size_t cbKey = strlen(pszKey);
545 unsigned uLast = NULL;
546
547 while (uStart != 0)
548 {
549 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
550 {
551 /* Key matches, check if there is a '=' (preceded by whitespace). */
552 pszTmp = pDescriptor->aLines[uStart] + cbKey;
553 while (*pszTmp == ' ' || *pszTmp == '\t')
554 pszTmp++;
555 if (*pszTmp == '=')
556 {
557 while (*pszTmp == ' ' || *pszTmp == '\t')
558 pszTmp++;
559 break;
560 }
561 }
562 if (!pDescriptor->aNextLines[uStart])
563 uLast = uStart;
564 uStart = pDescriptor->aNextLines[uStart];
565 }
566 if (uStart)
567 {
568 /* Key already exists, replace existing value. */
569 size_t cbOldVal = strlen(pszTmp);
570 size_t cbNewVal = strlen(pszValue);
571 ssize_t cbDiff = cbNewVal - cbOldVal;
572 /* Check for buffer overflow. */
573 if ( pDescriptor->aLines[pDescriptor->cLines]
574 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
575 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big"));
576
577 memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,
578 pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);
579 memcpy(pszTmp, pszValue, cbNewVal + 1);
580 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
581 pDescriptor->aLines[i] += cbDiff;
582 }
583 else
584 {
585 /* Key doesn't exist, append it after the last entry in this category. */
586 size_t cbKey = strlen(pszKey);
587 size_t cbValue = strlen(pszValue);
588 ssize_t cbDiff = cbKey + 1 + cbValue + 1;
589 /* Check for buffer overflow. */
590 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
591 || ( pDescriptor->aLines[pDescriptor->cLines]
592 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
593 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big"));
594 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
595 {
596 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
597 if (pDescriptor->aNextLines[i])
598 pDescriptor->aNextLines[i]++;
599 }
600 uStart = uLast + 1;
601 pDescriptor->aNextLines[uLast] = uStart;
602 pDescriptor->aNextLines[uStart] = 0;
603 pDescriptor->cLines++;
604 pszTmp = pDescriptor->aLines[uStart];
605 memmove(pszTmp + cbDiff, pszTmp,
606 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
607 memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);
608 pDescriptor->aLines[uStart][cbKey] = '=';
609 memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);
610 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
611 pDescriptor->aLines[i] += cbDiff;
612 }
613 pDescriptor->fDirty = true;
614 return VINF_SUCCESS;
615}
616
617static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,
618 uint32_t *puValue)
619{
620 const char *pszValue;
621
622 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey, &pszValue))
623 return VERR_VDI_VALUE_NOT_FOUND;
624 return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);
625}
626
627static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
628 const char *pszKey, uint32_t *puValue)
629{
630 const char *pszValue;
631 char *pszValueUnquoted;
632
633 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey, &pszValue))
634 return VERR_VDI_VALUE_NOT_FOUND;
635 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
636 if (VBOX_FAILURE(rc))
637 return rc;
638 rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);
639 RTMemTmpFree(pszValueUnquoted);
640 return rc;
641}
642
643static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
644 const char *pszKey, PRTUUID pUuid)
645{
646 const char *pszValue;
647 char *pszValueUnquoted;
648
649 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey, &pszValue))
650 return VERR_VDI_VALUE_NOT_FOUND;
651 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
652 if (VBOX_FAILURE(rc))
653 return rc;
654 rc = RTUuidFromStr(pUuid, pszValueUnquoted);
655 RTMemTmpFree(pszValueUnquoted);
656 return rc;
657}
658
659int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor, const char *pszKey, PCRTUUID pUuid)
660{
661 char *pszUuid;
662
663 int rc = RTStrAPrintf(&pszUuid, "\"%Vuuid\"", pUuid);
664 if (VBOX_FAILURE(rc))
665 return rc;
666 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey, pszUuid);
667 RTStrFree(pszUuid);
668 return rc;
669}
670
671int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor, const char *pszKey, uint32_t uValue)
672{
673 char *pszValue;
674
675 int rc = RTStrAPrintf(&pszValue, "\"%d\"", uValue);
676 if (VBOX_FAILURE(rc))
677 return rc;
678 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey, pszValue);
679 RTStrFree(pszValue);
680 return rc;
681}
682
683static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
684{
685 int rc = VINF_SUCCESS;
686 unsigned cLine = 0, uLastNonEmptyLine = 0;
687 char *pTmp = pDescData;
688
689 pDescriptor->cbDescAlloc = cbDescData;
690 while (*pTmp != '\0')
691 {
692 pDescriptor->aLines[cLine++] = pTmp;
693 if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)
694 {
695 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too long"));
696 goto out;
697 }
698
699 while (*pTmp != '\0' && *pTmp != '\n')
700 {
701 if (*pTmp == '\r')
702 {
703 if (*(pTmp + 1) != '\n')
704 {
705 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor"));
706 goto out;
707 }
708 else
709 {
710 /* Get rid of CR character. */
711 *pTmp = '\0';
712 }
713 }
714 pTmp++;
715 }
716 /* Get rid of LF character. */
717 if (*pTmp == '\n')
718 {
719 *pTmp = '\0';
720 pTmp++;
721 }
722 }
723 pDescriptor->cLines = cLine;
724 /* Pointer right after the end of the used part of the buffer. */
725 pDescriptor->aLines[cLine] = pTmp;
726
727 if (strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile"))
728 {
729 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor does not start as expected"));
730 goto out;
731 }
732
733 /* Initialize those, because we need to be able to reopen an image. */
734 pDescriptor->uFirstDesc = 0;
735 pDescriptor->uFirstExtent = 0;
736 pDescriptor->uFirstDDB = 0;
737 for (unsigned i = 0; i < cLine; i++)
738 {
739 if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
740 {
741 if ( !strncmp(pDescriptor->aLines[i], "RW", 2)
742 || !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
743 || !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
744 {
745 /* An extent descriptor. */
746 if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
747 {
748 /* Incorrect ordering of entries. */
749 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor"));
750 goto out;
751 }
752 if (!pDescriptor->uFirstExtent)
753 {
754 pDescriptor->uFirstExtent = i;
755 uLastNonEmptyLine = 0;
756 }
757 }
758 else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
759 {
760 /* A disk database entry. */
761 if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
762 {
763 /* Incorrect ordering of entries. */
764 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor"));
765 goto out;
766 }
767 if (!pDescriptor->uFirstDDB)
768 {
769 pDescriptor->uFirstDDB = i;
770 uLastNonEmptyLine = 0;
771 }
772 }
773 else
774 {
775 /* A normal entry. */
776 if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
777 {
778 /* Incorrect ordering of entries. */
779 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect ordering of entries in descriptor"));
780 goto out;
781 }
782 if (!pDescriptor->uFirstDesc)
783 {
784 pDescriptor->uFirstDesc = i;
785 uLastNonEmptyLine = 0;
786 }
787 }
788 if (uLastNonEmptyLine)
789 pDescriptor->aNextLines[uLastNonEmptyLine] = i;
790 uLastNonEmptyLine = i;
791 }
792 }
793
794out:
795 return rc;
796}
797
798static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData)
799{
800 int rc;
801 unsigned cExtents;
802 unsigned uLine;
803
804 rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData, &pImage->Descriptor);
805 if (VBOX_FAILURE(rc))
806 return rc;
807
808 /* Check version, must be 1. */
809 uint32_t uVersion;
810 rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
811 if (VBOX_FAILURE(rc))
812 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor"));
813 if (uVersion != 1)
814 return vmdkError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor"));
815
816 /* Count the number of extent config entries. */
817 for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
818 uLine != 0;
819 uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
820 /* nothing */;
821
822 if (!pImage->pDescData && cExtents != 1)
823 {
824 /* Monolithic image, must have only one extent (already opened). */
825 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent"));
826 }
827
828 if (pImage->pDescData)
829 {
830 /* Non-monolithic image, extents need to be allocated. */
831 rc = vmdkCreateExtents(pImage, cExtents);
832 if (VBOX_FAILURE(rc))
833 return rc;
834 }
835
836 for (unsigned i = 0, uLine = pImage->Descriptor.uFirstExtent;
837 i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
838 {
839 char *pszLine = pImage->Descriptor.aLines[uLine];
840
841 /* Access type of the extent. */
842 if (!strncmp(pszLine, "RW", 2))
843 {
844 pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
845 pszLine += 2;
846 }
847 else if (!strncmp(pszLine, "RDONLY", 6))
848 {
849 pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
850 pszLine += 6;
851 }
852 else if (!strncmp(pszLine, "NOACCESS", 8))
853 {
854 pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
855 pszLine += 8;
856 }
857 else
858 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
859 if (*pszLine++ != ' ')
860 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
861
862 /* Nominal size of the extent. */
863 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
864 &pImage->pExtents[i].cNominalSectors);
865 if (VBOX_FAILURE(rc))
866 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
867 if (*pszLine++ != ' ')
868 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
869
870 /* Type of the extent. */
871#ifdef VBOX_WITH_VMDK_ESX
872 /** @todo Add the ESX extent types. Not necessary for now because
873 * the ESX extent types are only used inside an ESX server. They are
874 * automatically converted if the VMDK is exported. */
875#endif /* VBOX_WITH_VMDK_ESX */
876 if (!strncmp(pszLine, "SPARSE", 6))
877 {
878 pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
879 pszLine += 6;
880 }
881 else if (!strncmp(pszLine, "FLAT", 4))
882 {
883 pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
884 pszLine += 4;
885 }
886 else if (!strncmp(pszLine, "ZERO", 4))
887 {
888 pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
889 pszLine += 4;
890 }
891 else
892 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
893 if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
894 {
895 /* This one has no basename or offset. */
896 if (*pszLine == ' ')
897 pszLine++;
898 if (*pszLine != '\0')
899 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
900 pImage->pExtents[i].pszBasename = NULL;
901 }
902 else
903 {
904 /* All other extent types have basename and optional offset. */
905 if (*pszLine++ != ' ')
906 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
907
908 /* Basename of the image. Surrounded by quotes. */
909 rc = vmdkStringUnquote(pImage, pszLine,
910 &pImage->pExtents[i].pszBasename, &pszLine);
911 if (VBOX_FAILURE(rc))
912 return rc;
913 if (*pszLine == ' ')
914 {
915 pszLine++;
916 if (*pszLine != '\0')
917 {
918 /* Optional offset in extent specified. */
919 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
920 &pImage->pExtents[i].uSectorOffset);
921 if (VBOX_FAILURE(rc))
922 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
923 }
924 }
925
926 if (*pszLine != '\0')
927 return vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description"));
928 }
929 }
930
931 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
932 "ddb.geometry.cylinders", &pImage->cCylinders);
933 if (VBOX_FAILURE(rc))
934 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description"));
935 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
936 "ddb.geometry.heads", &pImage->cHeads);
937 if (VBOX_FAILURE(rc))
938 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description"));
939 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
940 "ddb.geometry.sectors", &pImage->cSectors);
941 if (VBOX_FAILURE(rc))
942 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error getting CHS geometry from extent description"));
943 if (pImage->cCylinders >= 1024 || pImage->cHeads != 16)
944 pImage->enmTranslation = PDMBIOSTRANSLATION_LBA;
945 else
946 pImage->enmTranslation = PDMBIOSTRANSLATION_NONE;
947
948 /* Get image UUID. */
949 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.image",
950 &pImage->ImageUuid);
951 if (rc == VERR_VDI_VALUE_NOT_FOUND)
952 {
953 /* Image without UUID. Probably created by VMware and not yet used
954 * by VirtualBox. Can only be added for images opened in read/write
955 * mode, so don't bother producing a sensible UUID otherwise. */
956 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
957 RTUuidClear(&pImage->ImageUuid);
958 else
959 {
960 rc = RTUuidCreate(&pImage->ImageUuid);
961 if (VBOX_FAILURE(rc))
962 return rc;
963 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
964 "ddb.uuid.image", &pImage->ImageUuid);
965 if (VBOX_FAILURE(rc))
966 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor"));
967 }
968 }
969 else if (VBOX_FAILURE(rc))
970 return rc;
971
972 /* Get image modification UUID. */
973 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.modification",
974 &pImage->ModificationUuid);
975 if (rc == VERR_VDI_VALUE_NOT_FOUND)
976 {
977 /* Image without UUID. Probably created by VMware and not yet used
978 * by VirtualBox. Can only be added for images opened in read/write
979 * mode, so don't bother producing a sensible UUID otherwise. */
980 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
981 RTUuidClear(&pImage->ModificationUuid);
982 else
983 {
984 rc = RTUuidCreate(&pImage->ModificationUuid);
985 if (VBOX_FAILURE(rc))
986 return rc;
987 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
988 "ddb.uuid.modification", &pImage->ModificationUuid);
989 if (VBOX_FAILURE(rc))
990 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor"));
991 }
992 }
993 else if (VBOX_FAILURE(rc))
994 return rc;
995
996 /* Get UUID of parent image. */
997 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, "ddb.uuid.parent",
998 &pImage->ParentUuid);
999 if (rc == VERR_VDI_VALUE_NOT_FOUND)
1000 {
1001 /* Image without UUID. Probably created by VMware and not yet used
1002 * by VirtualBox. Can only be added for images opened in read/write
1003 * mode, so don't bother producing a sensible UUID otherwise. */
1004 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1005 RTUuidClear(&pImage->ParentUuid);
1006 else
1007 {
1008 rc = RTUuidClear(&pImage->ParentUuid);
1009 if (VBOX_FAILURE(rc))
1010 return rc;
1011 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
1012 "ddb.uuid.parent", &pImage->ParentUuid);
1013 if (VBOX_FAILURE(rc))
1014 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor"));
1015 }
1016 }
1017 else if (VBOX_FAILURE(rc))
1018 return rc;
1019
1020 return VINF_SUCCESS;
1021}
1022
1023static int vmdkWriteDescriptor(PVMDKIMAGE pImage)
1024{
1025 int rc = VINF_SUCCESS;
1026 uint64_t cbLimit;
1027 uint64_t uOffset;
1028 RTFILE DescFile;
1029
1030 if (pImage->pDescData)
1031 {
1032 /* Separate descriptor file. */
1033 uOffset = 0;
1034 cbLimit = 0;
1035 DescFile = pImage->File;
1036 }
1037 else
1038 {
1039 /* Embedded descriptor file. */
1040 uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
1041 cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
1042 cbLimit += uOffset;
1043 DescFile = pImage->pExtents[0].File;
1044 }
1045 for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
1046 {
1047 const char *psz = pImage->Descriptor.aLines[i];
1048 size_t cb = strlen(psz);
1049
1050 if (cbLimit && uOffset + cb + 1 > cbLimit)
1051 return vmdkError(pImage, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long"));
1052 rc = RTFileWriteAt(DescFile, uOffset, psz, cb, NULL);
1053 if (VBOX_FAILURE(rc))
1054 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor for '%s'"), pImage->pszFilename);
1055 uOffset += cb;
1056 rc = RTFileWriteAt(DescFile, uOffset, "\n", 1, NULL);
1057 if (VBOX_FAILURE(rc))
1058 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor for '%s'"), pImage->pszFilename);
1059 uOffset++;
1060 }
1061 if (cbLimit)
1062 {
1063 /* Inefficient, but simple. */
1064 while (uOffset < cbLimit)
1065 {
1066 rc = RTFileWriteAt(DescFile, uOffset, "", 1, NULL);
1067 if (VBOX_FAILURE(rc))
1068 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error writing descriptor for '%s'"), pImage->pszFilename);
1069 uOffset++;
1070 }
1071 }
1072 else
1073 {
1074 rc = RTFileSetSize(DescFile, uOffset);
1075 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor file '%s'"), pImage->pszFilename);
1076 }
1077 pImage->Descriptor.fDirty = false;
1078 return rc;
1079}
1080
1081static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent)
1082{
1083 SparseExtentHeader Header;
1084 uint64_t cbExtentSize, cSectorsPerGDE;
1085
1086 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1087 AssertRC(rc);
1088 if (VBOX_FAILURE(rc))
1089 {
1090 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header of file '%s'"), pExtent->pszFullname);
1091 goto out;
1092 }
1093 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_SPARSE_MAGICNUMBER
1094 || RT_LE2H_U32(Header.version) != 1)
1095 {
1096 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic/version in extent header of file '%s'"), pExtent->pszFullname);
1097 goto out;
1098 }
1099 /* The image must be a multiple of a sector in size. If not, it means the
1100 * image is at least truncated, or even seriously garbled. */
1101 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1102 if (VBOX_FAILURE(rc))
1103 {
1104 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1105 goto out;
1106 }
1107 if ( (RT_LE2H_U32(Header.flags) & 1)
1108 && ( Header.singleEndLineChar != '\n'
1109 || Header.nonEndLineChar != ' '
1110 || Header.doubleEndLineChar1 != '\r'
1111 || Header.doubleEndLineChar2 != '\n') )
1112 {
1113 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: file '%s' is corrupted by CR/LF translation"), pExtent->pszFullname);
1114 goto out;
1115 }
1116 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
1117 pExtent->cSectors = RT_LE2H_U64(Header.capacity);
1118 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
1119 /* The spec says that this must be a power of two and greater than 8,
1120 * but probably they meant not less than 8. */
1121 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1122 || pExtent->cSectorsPerGrain < 8)
1123 {
1124 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: invalid extent grain size %u for file '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
1125 goto out;
1126 }
1127 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
1128 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
1129 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
1130 {
1131 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config for file '%s'"), pExtent->pszFullname);
1132 goto out;
1133 }
1134 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
1135 /* This code requires that a grain table must hold a power of two multiple
1136 * of the number of entries per GT cache entry. */
1137 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
1138 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
1139 {
1140 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem for file '%s'"), pExtent->pszFullname);
1141 goto out;
1142 }
1143 if (RT_LE2H_U32(Header.flags) & 2)
1144 {
1145 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
1146 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
1147 }
1148 else
1149 {
1150 /** @todo this is just guesswork, the spec doesn't document this
1151 * properly and I don't have a vmdk without RGD. */
1152 pExtent->uSectorGD = RT_LE2H_U64(Header.rgdOffset);
1153 pExtent->uSectorRGD = 0;
1154 }
1155 pExtent->uOverheadSectors = RT_LE2H_U64(Header.overHead);
1156 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1157 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1158 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1159 {
1160 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size for file '%s'"), pExtent->pszFullname);
1161 goto out;
1162 }
1163 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1164 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1165
1166 rc = vmdkReadGrainDirectory(pExtent);
1167
1168out:
1169 if (VBOX_FAILURE(rc))
1170 {
1171 vmdkFreeExtentData(pExtent);
1172 }
1173
1174 return rc;
1175}
1176
1177static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent)
1178{
1179 SparseExtentHeader Header;
1180
1181 memset(&Header, '\0', sizeof(Header));
1182 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
1183 Header.version = RT_H2LE_U32(1);
1184 Header.flags = RT_H2LE_U32(1 | ((pExtent->pRGD) ? 2 : 0));
1185 Header.capacity = RT_H2LE_U64(pExtent->cSectors);
1186 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
1187 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
1188 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
1189 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
1190 if (pExtent->pRGD)
1191 {
1192 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
1193 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1194 }
1195 else
1196 {
1197 /** @todo this is just guesswork, the spec doesn't document this
1198 * properly and I don't have a vmdk without RGD. */
1199 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1200 }
1201 Header.overHead = RT_H2LE_U64(pExtent->uOverheadSectors);
1202 Header.uncleanShutdown = pExtent->fUncleanShutdown;
1203 Header.singleEndLineChar = '\n';
1204 Header.nonEndLineChar = ' ';
1205 Header.doubleEndLineChar1 = '\r';
1206 Header.doubleEndLineChar2 = '\n';
1207
1208 int rc = RTFileWriteAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1209 AssertRC(rc);
1210 if (VBOX_FAILURE(rc))
1211 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header to file '%s'"), pExtent->pszFullname);
1212 return rc;
1213}
1214
1215#ifdef VBOX_WITH_VMDK_ESX
1216static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
1217{
1218 COWDisk_Header Header;
1219 uint64_t cSectorsPerGDE;
1220
1221 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1222 AssertRC(rc);
1223 if (VBOX_FAILURE(rc))
1224 goto out;
1225 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER
1226 || RT_LE2H_U32(Header.version) != 1
1227 || RT_LE2H_U32(Header.flags) != 3)
1228 {
1229 rc = VERR_VDI_INVALID_HEADER;
1230 goto out;
1231 }
1232 pExtent->enmType = VMDKETYPE_ESX_SPARSE;
1233 pExtent->cSectors = RT_LE2H_U32(Header.numSectors);
1234 pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);
1235 /* The spec says that this must be between 1 sector and 1MB. This code
1236 * assumes it's a power of two, so check that requirement, too. */
1237 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1238 || pExtent->cSectorsPerGrain == 0
1239 || pExtent->cSectorsPerGrain > 2048)
1240 {
1241 rc = VERR_VDI_INVALID_HEADER;
1242 goto out;
1243 }
1244 pExtent->uDescriptorSector = 0;
1245 pExtent->cDescriptorSectors = 0;
1246 pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);
1247 pExtent->uSectorRGD = 0;
1248 pExtent->uOverheadSectors = 0;
1249 pExtent->cGTEntries = 4096;
1250 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1251 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1252 {
1253 rc = VERR_VDI_INVALID_HEADER;
1254 goto out;
1255 }
1256 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1257 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1258 if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))
1259 {
1260 /* Inconsistency detected. Computed number of GD entries doesn't match
1261 * stored value. Better be safe than sorry. */
1262 rc = VERR_VDI_INVALID_HEADER;
1263 goto out;
1264 }
1265 pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);
1266 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1267
1268 rc = vmdkReadGrainDirectory(pExtent);
1269
1270out:
1271 if (VBOX_FAILURE(rc))
1272 {
1273 vmdkFreeExtentData(pExtent);
1274 }
1275
1276 return rc;
1277}
1278#endif /* VBOX_WITH_VMDK_ESX */
1279
1280static void vmdkFreeExtentData(PVMDKEXTENT pExtent)
1281{
1282 vmdkFreeGrainDirectory(pExtent);
1283 if (pExtent->pDescData)
1284 {
1285 RTMemFree(pExtent->pDescData);
1286 pExtent->pDescData = NULL;
1287 }
1288 if (pExtent->pszFullname)
1289 {
1290 RTStrFree(pExtent->pszFullname);
1291 pExtent->pszFullname = NULL;
1292 }
1293 if (pExtent->File != NIL_RTFILE)
1294 {
1295 RTFileClose(pExtent->File);
1296 pExtent->File = NIL_RTFILE;
1297 }
1298}
1299
1300static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
1301{
1302 int rc = VINF_SUCCESS;
1303 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
1304 if (pImage)
1305 {
1306 for (unsigned i = 0; i < cExtents; i++)
1307 {
1308 pExtents[i].File = NIL_RTFILE;
1309 pExtents[i].pszBasename = NULL;
1310 pExtents[i].pszFullname = NULL;
1311 pExtents[i].pGD = NULL;
1312 pExtents[i].pRGD = NULL;
1313 pExtents[i].pDescData = NULL;
1314 pExtents[i].uExtent = i;
1315 pExtents[i].pImage = pImage;
1316 }
1317 pImage->pExtents = pExtents;
1318 pImage->cExtents = cExtents;
1319 }
1320 else
1321 rc = VERR_NO_MEMORY;
1322
1323 return rc;
1324}
1325
1326static int vmdkOpenImage(PVMDKIMAGE pImage, const char *pszFilename, unsigned uOpenFlags)
1327{
1328 int rc = VINF_SUCCESS;
1329 uint32_t u32Magic;
1330 RTFILE File;
1331 PVMDKEXTENT pExtent;
1332
1333 pImage->uOpenFlags = uOpenFlags;
1334
1335 /** @todo check the image file name for invalid characters, especially double quotes. */
1336
1337 /** @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. */
1338
1339 /*
1340 * Open the image.
1341 */
1342 rc = RTFileOpen(&File, pszFilename, uOpenFlags & VD_OPEN_FLAGS_READONLY
1343 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1344 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1345 if (VBOX_FAILURE(rc))
1346 {
1347 /* Do NOT signal an appropriate error here, as the VD layer has the
1348 * choice of retrying the open if it failed. */
1349 goto out;
1350 }
1351 pImage->File = File;
1352 rc = RTFileReadAt(File, 0, &u32Magic, sizeof(u32Magic), NULL);
1353 AssertRC(rc);
1354 if (VBOX_FAILURE(rc))
1355 {
1356 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number from file '%s'"), pszFilename);
1357 goto out;
1358 }
1359
1360 /* Handle the file according to its magic number. */
1361 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
1362 {
1363 /* It's a hosted sparse single-extent image. */
1364 rc = vmdkCreateExtents(pImage, 1);
1365 if (VBOX_FAILURE(rc))
1366 goto out;
1367 /* The opened file is passed to the extent. No separate descriptor
1368 * file, so no need to keep anything open for the image. */
1369 pExtent = &pImage->pExtents[0];
1370 pExtent->File = File;
1371 pImage->File = NIL_RTFILE;
1372 rc = vmdkReadMetaSparseExtent(pExtent);
1373 if (VBOX_FAILURE(rc))
1374 goto out;
1375 /* As we're dealing with a monolithic sparse image here, there must
1376 * be a descriptor embedded in the image file. */
1377 if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)
1378 {
1379 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in file '%s'"), pszFilename);
1380 goto out;
1381 }
1382 /* Read the descriptor from the extent. */
1383 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1384 if (!pExtent->pDescData)
1385 {
1386 rc = VERR_NO_MEMORY;
1387 goto out;
1388 }
1389 rc = RTFileReadAt(pExtent->File,
1390 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
1391 pExtent->pDescData,
1392 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
1393 AssertRC(rc);
1394 if (VBOX_FAILURE(rc))
1395 {
1396 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in file '%s'"), pExtent->pszFullname);
1397 goto out;
1398 }
1399
1400 rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
1401 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1402 if (VBOX_FAILURE(rc))
1403 goto out;
1404
1405 /* Mark the extent as unclean if opened in read-write mode. */
1406 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1407 {
1408 pExtent->fUncleanShutdown = true;
1409 pExtent->fMetaDirty = true;
1410 }
1411 }
1412 else
1413 {
1414 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
1415 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
1416 if (!pImage->pDescData)
1417 {
1418 rc = VERR_NO_MEMORY;
1419 goto out;
1420 }
1421
1422 size_t cbRead;
1423 rc = RTFileReadAt(pImage->File, 0, pImage->pDescData,
1424 pImage->cbDescAlloc, &cbRead);
1425 if (VBOX_FAILURE(rc))
1426 {
1427 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in file '%s'"), pExtent->pszFullname);
1428 goto out;
1429 }
1430 if (cbRead == pImage->cbDescAlloc)
1431 {
1432 /* Likely the read is truncated. Better fail a bit too early
1433 * (normally the descriptor is much smaller than our buffer). */
1434 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in file '%s'"), pExtent->pszFullname);
1435 goto out;
1436 }
1437
1438 rc = vmdkParseDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc);
1439 if (VBOX_FAILURE(rc))
1440 goto out;
1441
1442 for (unsigned i = 0; i < pImage->cExtents; i++)
1443 {
1444 PVMDKEXTENT pExtent = &pImage->pExtents[i];
1445
1446 if (pExtent->pszBasename)
1447 {
1448 /* Hack to figure out whether the specified name in the
1449 * extent descriptor is absolute. Doesn't always work, but
1450 * should be good enough for now. */
1451 /** @todo implement proper path absolute check. */
1452 if (pExtent->pszBasename[0] == RTPATH_SLASH)
1453 {
1454 pszFilename = RTStrDup(pExtent->pszBasename);
1455 if (!pszFilename)
1456 {
1457 rc = VERR_NO_MEMORY;
1458 goto out;
1459 }
1460 }
1461 else
1462 {
1463 size_t cbDirname;
1464 char *pszDirname = RTStrDup(pImage->pszFilename);
1465 if (!pszDirname)
1466 {
1467 rc = VERR_NO_MEMORY;
1468 goto out;
1469 }
1470 RTPathStripFilename(pszDirname);
1471 cbDirname = strlen(pszDirname);
1472 char *pszFullname;
1473 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname,
1474 RTPATH_SLASH, pExtent->pszBasename);
1475 RTStrFree(pszDirname);
1476 if (VBOX_FAILURE(rc))
1477 goto out;
1478 pExtent->pszFullname = pszFullname;
1479 }
1480 }
1481 else
1482 pExtent->pszFullname = NULL;
1483
1484 switch (pExtent->enmType)
1485 {
1486 case VMDKETYPE_HOSTED_SPARSE:
1487 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1488 uOpenFlags & VD_OPEN_FLAGS_READONLY
1489 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1490 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1491 if (VBOX_FAILURE(rc))
1492 {
1493 /* Do NOT signal an appropriate error here, as the VD
1494 * layer has the choice of retrying the open if it
1495 * failed. */
1496 goto out;
1497 }
1498 rc = vmdkReadMetaSparseExtent(pExtent);
1499 if (VBOX_FAILURE(rc))
1500 goto out;
1501
1502 /* Mark the extent as unclean if opened in read-write mode. */
1503 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1504 {
1505 pExtent->fUncleanShutdown = true;
1506 pExtent->fMetaDirty = true;
1507 }
1508 break;
1509 case VMDKETYPE_FLAT:
1510 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1511 uOpenFlags & VD_OPEN_FLAGS_READONLY
1512 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1513 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1514 if (VBOX_FAILURE(rc))
1515 {
1516 /* Do NOT signal an appropriate error here, as the VD
1517 * layer has the choice of retrying the open if it
1518 * failed. */
1519 goto out;
1520 }
1521 break;
1522 case VMDKETYPE_ZERO:
1523 /* Nothing to do. */
1524 break;
1525 default:
1526 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
1527 }
1528 }
1529 }
1530
1531 /* Make sure this is not reached accidentally with an error status. */
1532 AssertRC(rc);
1533
1534 /* Update the image metadata now in case has changed. */
1535 rc = vmdkFlushImage(pImage);
1536 if (VBOX_FAILURE(rc))
1537 goto out;
1538
1539 /* Figure out a few per-image constants from the extents. */
1540 pImage->cbSize = 0;
1541 for (unsigned i = 0; i < pImage->cExtents; i++)
1542 {
1543 pExtent = &pImage->pExtents[i];
1544 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1545#ifdef VBOX_WITH_VMDK_ESX
1546 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1547#endif /* VBOX_WITH_VMDK_ESX */
1548 )
1549 {
1550 /* Here used to be a check whether the nominal size of an extent
1551 * is a multiple of the grain size. The spec says that this is
1552 * always the case, but unfortunately some files out there in the
1553 * wild violate the spec (e.g. ReactOS 0.3.1). */
1554 }
1555 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
1556 }
1557
1558 pImage->enmImageType = VD_IMAGE_TYPE_NORMAL;
1559 for (unsigned i = 0; i < pImage->cExtents; i++)
1560 {
1561 pExtent = &pImage->pExtents[i];
1562 if ( pImage->pExtents[i].enmType == VMDKETYPE_FLAT
1563 || pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
1564 {
1565 pImage->enmImageType = VD_IMAGE_TYPE_FIXED;
1566 break;
1567 }
1568 }
1569
1570 /* Allocate grain table cache if any sparse extent is present. */
1571 for (unsigned i = 0; i < pImage->cExtents; i++)
1572 {
1573 pExtent = &pImage->pExtents[i];
1574 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1575#ifdef VBOX_WITH_VMDK_ESX
1576 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1577#endif /* VBOX_WITH_VMDK_ESX */
1578 )
1579 {
1580 /* Allocate grain table cache. */
1581 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
1582 if (!pImage->pGTCache)
1583 {
1584 rc = VERR_NO_MEMORY;
1585 goto out;
1586 }
1587 for (unsigned i = 0; i < VMDK_GT_CACHE_SIZE; i++)
1588 {
1589 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[i];
1590 pGCE->uExtent = UINT32_MAX;
1591 }
1592 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
1593 break;
1594 }
1595 }
1596
1597out:
1598 if (VBOX_FAILURE(rc))
1599 vmdkFreeImage(pImage);
1600 return rc;
1601}
1602
1603static void vmdkFreeImage(PVMDKIMAGE pImage)
1604{
1605 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1606 {
1607 /* Mark all extents as clean. */
1608 for (unsigned i = 0; i < pImage->cExtents; i++)
1609 {
1610 if (( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
1611#ifdef VBOX_WITH_VMDK_ESX
1612 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
1613#endif /* VBOX_WITH_VMDK_ESX */
1614 )
1615 && pImage->pExtents[i].fUncleanShutdown)
1616 {
1617 pImage->pExtents[i].fUncleanShutdown = false;
1618 pImage->pExtents[i].fMetaDirty = true;
1619 }
1620 }
1621 }
1622 (void)vmdkFlushImage(pImage);
1623 if (pImage->pExtents != NULL)
1624 {
1625 for (unsigned i = 0 ; i < pImage->cExtents; i++)
1626 vmdkFreeExtentData(&pImage->pExtents[i]);
1627 RTMemFree(pImage->pExtents);
1628 pImage->pExtents = NULL;
1629 }
1630 if (pImage->File != NIL_RTFILE)
1631 {
1632 RTFileClose(pImage->File);
1633 pImage->File = NIL_RTFILE;
1634 }
1635}
1636
1637static int vmdkFlushImage(PVMDKIMAGE pImage)
1638{
1639 PVMDKEXTENT pExtent;
1640 int rc = VINF_SUCCESS;
1641
1642 /* Update descriptor if changed. */
1643 if (pImage->Descriptor.fDirty)
1644 {
1645 rc = vmdkWriteDescriptor(pImage);
1646 if (VBOX_FAILURE(rc))
1647 goto out;
1648 }
1649
1650 for (unsigned i = 0; i < pImage->cExtents; i++)
1651 {
1652 pExtent = &pImage->pExtents[i];
1653 if (pExtent->File != NIL_RTFILE && pExtent->fMetaDirty)
1654 {
1655 switch (pExtent->enmType)
1656 {
1657 case VMDKETYPE_HOSTED_SPARSE:
1658 rc = vmdkWriteMetaSparseExtent(pExtent);
1659 if (VBOX_FAILURE(rc))
1660 goto out;
1661 break;
1662#ifdef VBOX_WITH_VMDK_ESX
1663 case VMDKETYPE_ESX_SPARSE:
1664 /** @todo update the header. */
1665 break;
1666#endif /* VBOX_WITH_VMDK_ESX */
1667 case VMDKETYPE_FLAT:
1668 /* Nothing to do. */
1669 break;
1670 case VMDKETYPE_ZERO:
1671 default:
1672 AssertMsgFailed(("extent with type %d marked as dirty\n",
1673 pExtent->enmType));
1674 break;
1675 }
1676 }
1677 switch (pExtent->enmType)
1678 {
1679 case VMDKETYPE_HOSTED_SPARSE:
1680#ifdef VBOX_WITH_VMDK_ESX
1681 case VMDKETYPE_ESX_SPARSE:
1682#endif /* VBOX_WITH_VMDK_ESX */
1683 case VMDKETYPE_FLAT:
1684 if (pExtent->File != NIL_RTFILE)
1685 rc = RTFileFlush(pExtent->File);
1686 break;
1687 case VMDKETYPE_ZERO:
1688 /* No need to do anything for this extent. */
1689 break;
1690 default:
1691 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
1692 break;
1693 }
1694 }
1695
1696out:
1697 return rc;
1698}
1699
1700static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector, PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
1701{
1702 PVMDKEXTENT pExtent = NULL;
1703 int rc = VINF_SUCCESS;
1704
1705 for (unsigned i = 0; i < pImage->cExtents; i++)
1706 {
1707 if (offSector < pImage->pExtents[i].cNominalSectors)
1708 {
1709 pExtent = &pImage->pExtents[i];
1710 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
1711 break;
1712 }
1713 offSector -= pImage->pExtents[i].cNominalSectors;
1714 }
1715
1716 if (pExtent)
1717 *ppExtent = pExtent;
1718 else
1719 rc = VERR_IO_SECTOR_NOT_FOUND;
1720
1721 return rc;
1722}
1723
1724static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector, unsigned uExtent)
1725{
1726 /** @todo this hash function is quite simple, maybe use a better one which
1727 * scrambles the bits better. */
1728 return (uSector + uExtent) % pCache->cEntries;
1729}
1730
1731static int vmdkGetSector(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
1732 uint64_t uSector, uint64_t *puExtentSector)
1733{
1734 uint64_t uGDIndex, uGTSector, uGTBlock;
1735 uint32_t uGTHash, uGTBlockIndex;
1736 PVMDKGTCACHEENTRY pGTCacheEntry;
1737 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
1738 int rc;
1739
1740 uGDIndex = uSector / pExtent->cSectorsPerGDE;
1741 if (uGDIndex >= pExtent->cGDEntries)
1742 return VERR_OUT_OF_RANGE;
1743 uGTSector = pExtent->pGD[uGDIndex];
1744 if (!uGTSector)
1745 {
1746 /* There is no grain table referenced by this grain directory
1747 * entry. So there is absolutely no data in this area. */
1748 *puExtentSector = 0;
1749 return VINF_SUCCESS;
1750 }
1751
1752 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
1753 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
1754 pGTCacheEntry = &pCache->aGTCache[uGTHash];
1755 if ( pGTCacheEntry->uExtent != pExtent->uExtent
1756 || pGTCacheEntry->uGTBlock != uGTBlock)
1757 {
1758 /* Cache miss, fetch data from disk. */
1759 rc = RTFileReadAt(pExtent->File,
1760 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1761 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1762 if (VBOX_FAILURE(rc))
1763 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in file '%s'"), pExtent->pszFullname);
1764 pGTCacheEntry->uExtent = pExtent->uExtent;
1765 pGTCacheEntry->uGTBlock = uGTBlock;
1766 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1767 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
1768 }
1769 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
1770 uint64_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
1771 if (uGrainSector)
1772 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
1773 else
1774 *puExtentSector = 0;
1775 return VINF_SUCCESS;
1776}
1777
1778/**
1779 * Internal. Allocates a new grain table (if necessary), writes the grain
1780 * and updates the grain table. The cache is also updated by this operation.
1781 * This is separate from vmdkGetSector, because that should be as fast as
1782 * possible. Most code from vmdkGetSector also appears here.
1783 */
1784static int vmdkAllocGrain(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
1785 uint64_t uSector, const void *pvBuf, uint64_t cbWrite)
1786{
1787 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
1788 uint64_t cbExtentSize;
1789 uint32_t uGTHash, uGTBlockIndex;
1790 PVMDKGTCACHEENTRY pGTCacheEntry;
1791 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
1792 int rc;
1793
1794 uGDIndex = uSector / pExtent->cSectorsPerGDE;
1795 if (uGDIndex >= pExtent->cGDEntries)
1796 return VERR_OUT_OF_RANGE;
1797 uGTSector = pExtent->pGD[uGDIndex];
1798 uRGTSector = pExtent->pRGD[uGDIndex];
1799 if (!uGTSector)
1800 {
1801 /* There is no grain table referenced by this grain directory
1802 * entry. So there is absolutely no data in this area. Allocate
1803 * a new grain table and put the reference to it in the GDs. */
1804 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1805 if (VBOX_FAILURE(rc))
1806 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1807 Assert(!(cbExtentSize % 512));
1808 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
1809 /* Normally the grain table is preallocated for hosted sparse extents
1810 * that support more than 32 bit sector numbers. So this shouldn't
1811 * ever happen on a valid extent. */
1812 if (uGTSector > UINT32_MAX)
1813 return VERR_VDI_INVALID_HEADER;
1814 /* Write grain table by writing the required number of grain table
1815 * cache chunks. Avoids dynamic memory allocation, but is a bit
1816 * slower. But as this is a pretty infrequently occurring case it
1817 * should be acceptable. */
1818 memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
1819 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
1820 {
1821 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
1822 if (VBOX_FAILURE(rc))
1823 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in file '%s'"), pExtent->pszFullname);
1824 }
1825 if (pExtent->pRGD)
1826 {
1827 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1828 if (VBOX_FAILURE(rc))
1829 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1830 Assert(!(cbExtentSize % 512));
1831 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
1832 /* Write backup grain table by writing the required number of grain
1833 * table cache chunks. Avoids dynamic memory allocation, but is a
1834 * bit slower. But as this is a pretty infrequently occurring case
1835 * it should be acceptable. */
1836 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
1837 {
1838 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
1839 if (VBOX_FAILURE(rc))
1840 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in file '%s'"), pExtent->pszFullname);
1841 }
1842 }
1843
1844 /* Update the grain directory on disk (doing it before writing the
1845 * grain table will result in a garbled extent if the operation is
1846 * aborted for some reason. Otherwise the worst that can happen is
1847 * some unused sectors in the extent. */
1848 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
1849 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE), &uGTSectorLE, sizeof(uGTSectorLE), NULL);
1850 if (VBOX_FAILURE(rc))
1851 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in file '%s'"), pExtent->pszFullname);
1852 if (pExtent->pRGD)
1853 {
1854 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
1855 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE), &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
1856 if (VBOX_FAILURE(rc))
1857 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in file '%s'"), pExtent->pszFullname);
1858 }
1859
1860 /* As the final step update the in-memory copy of the GDs. */
1861 pExtent->pGD[uGDIndex] = uGTSector;
1862 if (pExtent->pRGD)
1863 pExtent->pRGD[uGDIndex] = uRGTSector;
1864 }
1865
1866 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1867 if (VBOX_FAILURE(rc))
1868 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1869 Assert(!(cbExtentSize % 512));
1870
1871 /* Write the data. */
1872 rc = RTFileWriteAt(pExtent->File, cbExtentSize, pvBuf, cbWrite, NULL);
1873 if (VBOX_FAILURE(rc))
1874 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in file '%s'"), pExtent->pszFullname);
1875
1876 /* Update the grain table (and the cache). */
1877 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
1878 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
1879 pGTCacheEntry = &pCache->aGTCache[uGTHash];
1880 if ( pGTCacheEntry->uExtent != pExtent->uExtent
1881 || pGTCacheEntry->uGTBlock != uGTBlock)
1882 {
1883 /* Cache miss, fetch data from disk. */
1884 rc = RTFileReadAt(pExtent->File,
1885 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1886 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1887 if (VBOX_FAILURE(rc))
1888 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in file '%s'"), pExtent->pszFullname);
1889 pGTCacheEntry->uExtent = pExtent->uExtent;
1890 pGTCacheEntry->uGTBlock = uGTBlock;
1891 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1892 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
1893 }
1894 else
1895 {
1896 /* Cache hit. Convert grain table block back to disk format, otherwise
1897 * the code below will write garbage for all but the updated entry. */
1898 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1899 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
1900 }
1901 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
1902 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(cbExtentSize));
1903 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(cbExtentSize);
1904 /* Update grain table on disk. */
1905 rc = RTFileWriteAt(pExtent->File,
1906 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1907 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1908 if (VBOX_FAILURE(rc))
1909 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in file '%s'"), pExtent->pszFullname);
1910 if (pExtent->pRGD)
1911 {
1912 /* Update backup grain table on disk. */
1913 rc = RTFileWriteAt(pExtent->File,
1914 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1915 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1916 if (VBOX_FAILURE(rc))
1917 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in file '%s'"), pExtent->pszFullname);
1918 }
1919#ifdef VBOX_WITH_VMDK_ESX
1920 if (VBOX_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
1921 {
1922 pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
1923 pExtent->fMetaDirty = true;
1924 }
1925#endif /* VBOX_WITH_VMDK_ESX */
1926 return rc;
1927}
1928
1929static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData)
1930{
1931 int rc = VINF_SUCCESS;
1932 PVMDKIMAGE pImage;
1933
1934 /* Check open flags. Just readonly flag is supported. */
1935 if (uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
1936 {
1937 rc = VERR_INVALID_PARAMETER;
1938 goto out;
1939 }
1940
1941 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
1942 if (!pImage)
1943 {
1944 rc = VERR_NO_MEMORY;
1945 goto out;
1946 }
1947 pImage->pszFilename = pszFilename;
1948 pImage->File = NIL_RTFILE;
1949 pImage->pExtents = NULL;
1950 pImage->pGTCache = NULL;
1951 pImage->pDescData = NULL;
1952 pImage->pfnError = pfnError;
1953 pImage->pvErrorUser = pvErrorUser;
1954
1955 if (VBOX_SUCCESS(rc))
1956 {
1957 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
1958 if (VBOX_SUCCESS(rc))
1959 *ppvBackendData = pImage;
1960 }
1961
1962out:
1963 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
1964 return rc;
1965}
1966
1967static int vmdkClose(void *pBackendData)
1968{
1969 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
1970 int rc = VINF_SUCCESS;
1971
1972 /* Freeing a never allocated image (e.g. because the open failed) is
1973 * not signalled as an error. After all nothing bad happens. */
1974 if (pImage)
1975 vmdkFreeImage(pImage);
1976
1977 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
1978 return rc;
1979}
1980
1981static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbActuallyRead)
1982{
1983 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
1984 PVMDKEXTENT pExtent;
1985 uint64_t uSectorInExtent;
1986 uint64_t uSectorOffset;
1987 int rc;
1988
1989 Assert(uOffset % 512 == 0);
1990 Assert(cbRead % 512 == 0);
1991
1992 if (uOffset + cbRead > pImage->cbSize)
1993 {
1994 rc = VERR_INVALID_PARAMETER;
1995 goto out;
1996 }
1997
1998 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
1999 &pExtent, &uSectorInExtent);
2000 if (VBOX_FAILURE(rc))
2001 goto out;
2002
2003 /* Check access permissions as defined in the extent descriptor. */
2004 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
2005 {
2006 rc = VERR_VDI_INVALID_STATE;
2007 goto out;
2008 }
2009
2010 /* Clip read range to remain in this extent. */
2011 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
2012
2013 /* Handle the read according to the current extent type. */
2014 switch (pExtent->enmType)
2015 {
2016 case VMDKETYPE_HOSTED_SPARSE:
2017#ifdef VBOX_WITH_VMDK_ESX
2018 case VMDKETYPE_ESX_SPARSE:
2019#endif /* VBOX_WITH_VMDK_ESX */
2020 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorInExtent,
2021 &uSectorOffset);
2022 if (VBOX_FAILURE(rc))
2023 goto out;
2024 /* Clip read range to at most the rest of the grain. */
2025 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - (uSectorOffset % pExtent->cSectorsPerGrain)));
2026 if (uSectorOffset == 0)
2027 rc = VINF_VDI_BLOCK_FREE;
2028 else
2029 rc = RTFileReadAt(pExtent->File,
2030 VMDK_SECTOR2BYTE(uSectorOffset),
2031 pvBuf, cbRead, NULL);
2032 break;
2033 case VMDKETYPE_FLAT:
2034 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorInExtent),
2035 pvBuf, cbRead, NULL);
2036 break;
2037 case VMDKETYPE_ZERO:
2038 memset(pvBuf, '\0', cbRead);
2039 break;
2040 }
2041 *pcbActuallyRead = cbRead;
2042
2043out:
2044 return rc;
2045}
2046
2047static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead)
2048{
2049 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2050 PVMDKEXTENT pExtent;
2051 uint64_t uSectorInExtent;
2052 uint64_t uSectorOffset;
2053 int rc;
2054
2055 Assert(uOffset % 512 == 0);
2056 Assert(cbWrite % 512 == 0);
2057
2058 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2059 {
2060 rc = VERR_VDI_IMAGE_READ_ONLY;
2061 goto out;
2062 }
2063
2064 /* No size check here, will do that later when the extent is located.
2065 * There are sparse images out there which according to the spec are
2066 * invalid, because the total size is not a multiple of the grain size.
2067 * Also for sparse images which are stitched together in odd ways (not at
2068 * grain boundaries, and with the nominal size not being a multiple of the
2069 * grain size), this would prevent writing to the last grain. */
2070
2071 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
2072 &pExtent, &uSectorInExtent);
2073 if (VBOX_FAILURE(rc))
2074 goto out;
2075
2076 /* Check access permissions as defined in the extent descriptor. */
2077 if (pExtent->enmAccess != VMDKACCESS_READWRITE)
2078 {
2079 rc = VERR_VDI_INVALID_STATE;
2080 goto out;
2081 }
2082
2083 /* Handle the write according to the current extent type. */
2084 switch (pExtent->enmType)
2085 {
2086 case VMDKETYPE_HOSTED_SPARSE:
2087#ifdef VBOX_WITH_VMDK_ESX
2088 case VMDKETYPE_ESX_SPARSE:
2089#endif /* VBOX_WITH_VMDK_ESX */
2090 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorInExtent,
2091 &uSectorOffset);
2092 if (VBOX_FAILURE(rc))
2093 goto out;
2094 /* Clip write range to at most the rest of the grain. */
2095 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - (uSectorOffset % pExtent->cSectorsPerGrain)));
2096 if (uSectorOffset == 0)
2097 {
2098 if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
2099 {
2100 /* Full block write to a previously unallocated block.
2101 * Allocate GT and find out where to store the grain. */
2102 rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
2103 uSectorInExtent, pvBuf, cbWrite);
2104 }
2105 else
2106 {
2107 /* Clip write range to remain in this extent. */
2108 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
2109 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorInExtent % pExtent->cSectorsPerGrain);
2110 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
2111 rc = VINF_VDI_BLOCK_FREE;
2112 }
2113 }
2114 else
2115 rc = RTFileWriteAt(pExtent->File,
2116 VMDK_SECTOR2BYTE(uSectorOffset),
2117 pvBuf, cbWrite, NULL);
2118 break;
2119 case VMDKETYPE_FLAT:
2120 /* Clip write range to remain in this extent. */
2121 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
2122 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorInExtent), pvBuf, cbWrite, NULL);
2123 break;
2124 case VMDKETYPE_ZERO:
2125 /* Clip write range to remain in this extent. */
2126 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorInExtent));
2127 break;
2128 }
2129 if (pcbWriteProcess)
2130 *pcbWriteProcess = cbWrite;
2131
2132out:
2133 return rc;
2134}
2135
2136static int vmdkFlush(void *pBackendData)
2137{
2138 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2139
2140 int rc = vmdkFlushImage(pImage);
2141
2142 return rc;
2143}
2144
2145static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
2146{
2147 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2148 int rc = VINF_SUCCESS;
2149
2150 Assert(pImage);
2151 Assert(penmImageType);
2152
2153 if (pImage && pImage->cExtents != 0)
2154 *penmImageType = pImage->enmImageType;
2155 else
2156 rc = VERR_VDI_NOT_OPENED;
2157
2158 return rc;
2159}
2160
2161static uint64_t vmdkGetSize(void *pBackendData)
2162{
2163 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2164
2165 Assert(pImage);
2166
2167 if (pImage)
2168 return pImage->cbSize;
2169 else
2170 return 0;
2171}
2172
2173static int vmdkGetGeometry(void *pBackendData, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
2174{
2175 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2176 int rc;
2177
2178 Assert(pImage);
2179
2180 if (pImage)
2181 {
2182 if (pImage->cCylinders)
2183 {
2184 *pcCylinders = pImage->cCylinders;
2185 *pcHeads = pImage->cHeads;
2186 *pcSectors = pImage->cSectors;
2187 rc = VINF_SUCCESS;
2188 }
2189 else
2190 rc = VERR_VDI_GEOMETRY_NOT_SET;
2191 }
2192 else
2193 rc = VERR_VDI_NOT_OPENED;
2194 LogFlow(("%s: returned %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
2195 pImage->cCylinders, pImage->cHeads, pImage->cSectors));
2196 return rc;
2197}
2198
2199static int vmdkSetGeometry(void *pBackendData, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
2200{
2201 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2202 int rc;
2203
2204 Assert(pImage);
2205
2206 if (pImage)
2207 {
2208 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2209 {
2210 rc = VERR_VDI_IMAGE_READ_ONLY;
2211 goto out;
2212 }
2213 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2214 "ddb.geometry.cylinders", cCylinders);
2215 if (VBOX_FAILURE(rc))
2216 goto out;
2217 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2218 "ddb.geometry.heads", cHeads);
2219 if (VBOX_FAILURE(rc))
2220 goto out;
2221 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2222 "ddb.geometry.sectors", cSectors);
2223 if (VBOX_FAILURE(rc))
2224 goto out;
2225
2226 pImage->cCylinders = cCylinders;
2227 pImage->cHeads = cHeads;
2228 pImage->cSectors = cSectors;
2229 rc = VINF_SUCCESS;
2230 }
2231 else
2232 rc = VERR_VDI_NOT_OPENED;
2233
2234out:
2235 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2236 return rc;
2237}
2238
2239static int vmdkGetTranslation(void *pBackendData, PPDMBIOSTRANSLATION penmTranslation)
2240{
2241 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2242 int rc;
2243
2244 Assert(pImage);
2245
2246 if (pImage)
2247 {
2248 if (pImage->enmTranslation)
2249 {
2250 *penmTranslation = pImage->enmTranslation;
2251 rc = VINF_SUCCESS;
2252 }
2253 else
2254 rc = VERR_VDI_GEOMETRY_NOT_SET;
2255 }
2256 else
2257 rc = VERR_VDI_NOT_OPENED;
2258 LogFlow(("%s: returned %Vrc (%d)\n", __FUNCTION__, rc,
2259 pImage->enmTranslation));
2260 return rc;
2261}
2262
2263static int vmdkSetTranslation(void *pBackendData, PDMBIOSTRANSLATION enmTranslation)
2264{
2265 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2266 int rc;
2267
2268 Assert(pImage);
2269
2270 if (pImage)
2271 {
2272 /** @todo maybe store this in the image descriptor */
2273 pImage->enmTranslation = enmTranslation;
2274 rc = VINF_SUCCESS;
2275 }
2276 else
2277 rc = VERR_VDI_NOT_OPENED;
2278 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2279 return rc;
2280}
2281
2282static unsigned vmdkGetOpenFlags(void *pBackendData)
2283{
2284 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2285 unsigned uOpenFlags;
2286
2287 Assert(pImage);
2288
2289 if (pImage)
2290 uOpenFlags = pImage->uOpenFlags;
2291 else
2292 uOpenFlags = 0;
2293
2294 LogFlow(("%s: returned %d\n", __FUNCTION__, uOpenFlags));
2295 return uOpenFlags;
2296}
2297
2298static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2299{
2300 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2301 int rc;
2302 const char *pszFilename;
2303
2304 /* Image must be opened and the new flags must be valid. Just readonly flag
2305 * is supported. */
2306 if (!pImage || uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
2307 {
2308 rc = VERR_INVALID_PARAMETER;
2309 goto out;
2310 }
2311
2312 /* Implement this operation via reopening the image. */
2313 pszFilename = pImage->pszFilename;
2314 vmdkFreeImage(pImage);
2315 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
2316
2317out:
2318 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2319 return rc;
2320}
2321
2322static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
2323{
2324 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2325 int rc;
2326
2327 Assert(pImage);
2328
2329 if (pImage)
2330 {
2331 *pUuid = pImage->ImageUuid;
2332 rc = VINF_SUCCESS;
2333 }
2334 else
2335 rc = VERR_VDI_NOT_OPENED;
2336 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2337 return rc;
2338}
2339
2340static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
2341{
2342 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2343 int rc;
2344
2345 LogFlow(("%s: %Vuuid\n", pUuid));
2346 Assert(pImage);
2347
2348 if (pImage)
2349 {
2350 pImage->ImageUuid = *pUuid;
2351 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2352 "ddb.uuid.image", pUuid);
2353 if (VBOX_FAILURE(rc))
2354 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor"));
2355 rc = VINF_SUCCESS;
2356 }
2357 else
2358 rc = VERR_VDI_NOT_OPENED;
2359 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2360 return rc;
2361}
2362
2363static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2364{
2365 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2366 int rc;
2367
2368 Assert(pImage);
2369
2370 if (pImage)
2371 {
2372 *pUuid = pImage->ModificationUuid;
2373 rc = VINF_SUCCESS;
2374 }
2375 else
2376 rc = VERR_VDI_NOT_OPENED;
2377 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2378 return rc;
2379}
2380
2381static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2382{
2383 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2384 int rc;
2385
2386 LogFlow(("%s: %Vuuid\n", pUuid));
2387 Assert(pImage);
2388
2389 if (pImage)
2390 {
2391 pImage->ModificationUuid = *pUuid;
2392 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2393 "ddb.uuid.modification", pUuid);
2394 if (VBOX_FAILURE(rc))
2395 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor"));
2396 rc = VINF_SUCCESS;
2397 }
2398 else
2399 rc = VERR_VDI_NOT_OPENED;
2400 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2401 return rc;
2402}
2403
2404static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
2405{
2406 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2407 int rc;
2408
2409 Assert(pImage);
2410
2411 if (pImage)
2412 {
2413 *pUuid = pImage->ParentUuid;
2414 rc = VINF_SUCCESS;
2415 }
2416 else
2417 rc = VERR_VDI_NOT_OPENED;
2418 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2419 return rc;
2420}
2421
2422static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2423{
2424 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2425 int rc;
2426
2427 LogFlow(("%s: %Vuuid\n", pUuid));
2428 Assert(pImage);
2429
2430 if (pImage)
2431 {
2432 pImage->ParentUuid = *pUuid;
2433 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2434 "ddb.uuid.parent", pUuid);
2435 if (VBOX_FAILURE(rc))
2436 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor"));
2437 rc = VINF_SUCCESS;
2438 }
2439 else
2440 rc = VERR_VDI_NOT_OPENED;
2441 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2442 return rc;
2443}
2444
2445
2446VBOXHDDBACKEND g_VmdkBackend =
2447{
2448 /* pfnOpen */
2449 vmdkOpen,
2450 /* pfnClose */
2451 vmdkClose,
2452 /* pfnRead */
2453 vmdkRead,
2454 /* pfnWrite */
2455 vmdkWrite,
2456 /* pfnFlush */
2457 vmdkFlush,
2458 /* pfnGetImageType */
2459 vmdkGetImageType,
2460 /* pfnGetSize */
2461 vmdkGetSize,
2462 /* pfnGetGeometry */
2463 vmdkGetGeometry,
2464 /* pfnSetGeometry */
2465 vmdkSetGeometry,
2466 /* pfnGetTranslation */
2467 vmdkGetTranslation,
2468 /* pfnSetTranslation */
2469 vmdkSetTranslation,
2470 /* pfnGetOpenFlags */
2471 vmdkGetOpenFlags,
2472 /* pfnSetOpenFlags */
2473 vmdkSetOpenFlags,
2474 /* pfnGetUuid */
2475 vmdkGetUuid,
2476 /* pfnGetUuid */
2477 vmdkSetUuid,
2478 /* pfnGetModificationUuid */
2479 vmdkGetModificationUuid,
2480 /* pfnSetModificationUuid */
2481 vmdkSetModificationUuid,
2482 /* pfnGetParentUuid */
2483 vmdkGetParentUuid,
2484 /* pfnSetParentUuid */
2485 vmdkSetParentUuid
2486};
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