VirtualBox

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

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

Fix incorrect error check for VMDK with separate descriptor file.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.4 KB
Line 
1/** $Id: VmdkHDDCore.cpp 2583 2007-05-10 16:38:18Z 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 = (char*)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 = 0;
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 if (VBOX_FAILURE(rc))
1076 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor file '%s'"), pImage->pszFilename);
1077 }
1078 pImage->Descriptor.fDirty = false;
1079 return rc;
1080}
1081
1082static int vmdkReadMetaSparseExtent(PVMDKEXTENT pExtent)
1083{
1084 SparseExtentHeader Header;
1085 uint64_t cbExtentSize, cSectorsPerGDE;
1086
1087 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1088 AssertRC(rc);
1089 if (VBOX_FAILURE(rc))
1090 {
1091 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error reading extent header of file '%s'"), pExtent->pszFullname);
1092 goto out;
1093 }
1094 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_SPARSE_MAGICNUMBER
1095 || RT_LE2H_U32(Header.version) != 1)
1096 {
1097 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic/version in extent header of file '%s'"), pExtent->pszFullname);
1098 goto out;
1099 }
1100 /* The image must be a multiple of a sector in size. If not, it means the
1101 * image is at least truncated, or even seriously garbled. */
1102 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1103 if (VBOX_FAILURE(rc))
1104 {
1105 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1106 goto out;
1107 }
1108 if ( (RT_LE2H_U32(Header.flags) & 1)
1109 && ( Header.singleEndLineChar != '\n'
1110 || Header.nonEndLineChar != ' '
1111 || Header.doubleEndLineChar1 != '\r'
1112 || Header.doubleEndLineChar2 != '\n') )
1113 {
1114 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: file '%s' is corrupted by CR/LF translation"), pExtent->pszFullname);
1115 goto out;
1116 }
1117 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
1118 pExtent->cSectors = RT_LE2H_U64(Header.capacity);
1119 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
1120 /* The spec says that this must be a power of two and greater than 8,
1121 * but probably they meant not less than 8. */
1122 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1123 || pExtent->cSectorsPerGrain < 8)
1124 {
1125 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);
1126 goto out;
1127 }
1128 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
1129 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
1130 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
1131 {
1132 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: inconsistent embedded descriptor config for file '%s'"), pExtent->pszFullname);
1133 goto out;
1134 }
1135 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
1136 /* This code requires that a grain table must hold a power of two multiple
1137 * of the number of entries per GT cache entry. */
1138 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
1139 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
1140 {
1141 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: grain table cache size problem for file '%s'"), pExtent->pszFullname);
1142 goto out;
1143 }
1144 if (RT_LE2H_U32(Header.flags) & 2)
1145 {
1146 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
1147 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
1148 }
1149 else
1150 {
1151 /** @todo this is just guesswork, the spec doesn't document this
1152 * properly and I don't have a vmdk without RGD. */
1153 pExtent->uSectorGD = RT_LE2H_U64(Header.rgdOffset);
1154 pExtent->uSectorRGD = 0;
1155 }
1156 pExtent->uOverheadSectors = RT_LE2H_U64(Header.overHead);
1157 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1158 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1159 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1160 {
1161 rc = vmdkError(pExtent->pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect grain directory size for file '%s'"), pExtent->pszFullname);
1162 goto out;
1163 }
1164 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1165 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1166
1167 rc = vmdkReadGrainDirectory(pExtent);
1168
1169out:
1170 if (VBOX_FAILURE(rc))
1171 {
1172 vmdkFreeExtentData(pExtent);
1173 }
1174
1175 return rc;
1176}
1177
1178static int vmdkWriteMetaSparseExtent(PVMDKEXTENT pExtent)
1179{
1180 SparseExtentHeader Header;
1181
1182 memset(&Header, '\0', sizeof(Header));
1183 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
1184 Header.version = RT_H2LE_U32(1);
1185 Header.flags = RT_H2LE_U32(1 | ((pExtent->pRGD) ? 2 : 0));
1186 Header.capacity = RT_H2LE_U64(pExtent->cSectors);
1187 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
1188 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
1189 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
1190 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
1191 if (pExtent->pRGD)
1192 {
1193 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
1194 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1195 }
1196 else
1197 {
1198 /** @todo this is just guesswork, the spec doesn't document this
1199 * properly and I don't have a vmdk without RGD. */
1200 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorGD);
1201 }
1202 Header.overHead = RT_H2LE_U64(pExtent->uOverheadSectors);
1203 Header.uncleanShutdown = pExtent->fUncleanShutdown;
1204 Header.singleEndLineChar = '\n';
1205 Header.nonEndLineChar = ' ';
1206 Header.doubleEndLineChar1 = '\r';
1207 Header.doubleEndLineChar2 = '\n';
1208
1209 int rc = RTFileWriteAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1210 AssertRC(rc);
1211 if (VBOX_FAILURE(rc))
1212 rc = vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error writing extent header to file '%s'"), pExtent->pszFullname);
1213 return rc;
1214}
1215
1216#ifdef VBOX_WITH_VMDK_ESX
1217static int vmdkReadMetaESXSparseExtent(PVMDKEXTENT pExtent)
1218{
1219 COWDisk_Header Header;
1220 uint64_t cSectorsPerGDE;
1221
1222 int rc = RTFileReadAt(pExtent->File, 0, &Header, sizeof(Header), NULL);
1223 AssertRC(rc);
1224 if (VBOX_FAILURE(rc))
1225 goto out;
1226 if ( RT_LE2H_U32(Header.magicNumber) != VMDK_ESX_SPARSE_MAGICNUMBER
1227 || RT_LE2H_U32(Header.version) != 1
1228 || RT_LE2H_U32(Header.flags) != 3)
1229 {
1230 rc = VERR_VDI_INVALID_HEADER;
1231 goto out;
1232 }
1233 pExtent->enmType = VMDKETYPE_ESX_SPARSE;
1234 pExtent->cSectors = RT_LE2H_U32(Header.numSectors);
1235 pExtent->cSectorsPerGrain = RT_LE2H_U32(Header.grainSize);
1236 /* The spec says that this must be between 1 sector and 1MB. This code
1237 * assumes it's a power of two, so check that requirement, too. */
1238 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
1239 || pExtent->cSectorsPerGrain == 0
1240 || pExtent->cSectorsPerGrain > 2048)
1241 {
1242 rc = VERR_VDI_INVALID_HEADER;
1243 goto out;
1244 }
1245 pExtent->uDescriptorSector = 0;
1246 pExtent->cDescriptorSectors = 0;
1247 pExtent->uSectorGD = RT_LE2H_U32(Header.gdOffset);
1248 pExtent->uSectorRGD = 0;
1249 pExtent->uOverheadSectors = 0;
1250 pExtent->cGTEntries = 4096;
1251 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
1252 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
1253 {
1254 rc = VERR_VDI_INVALID_HEADER;
1255 goto out;
1256 }
1257 pExtent->cSectorsPerGDE = cSectorsPerGDE;
1258 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
1259 if (pExtent->cGDEntries != RT_LE2H_U32(Header.numGDEntries))
1260 {
1261 /* Inconsistency detected. Computed number of GD entries doesn't match
1262 * stored value. Better be safe than sorry. */
1263 rc = VERR_VDI_INVALID_HEADER;
1264 goto out;
1265 }
1266 pExtent->uFreeSector = RT_LE2H_U32(Header.freeSector);
1267 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
1268
1269 rc = vmdkReadGrainDirectory(pExtent);
1270
1271out:
1272 if (VBOX_FAILURE(rc))
1273 {
1274 vmdkFreeExtentData(pExtent);
1275 }
1276
1277 return rc;
1278}
1279#endif /* VBOX_WITH_VMDK_ESX */
1280
1281static void vmdkFreeExtentData(PVMDKEXTENT pExtent)
1282{
1283 vmdkFreeGrainDirectory(pExtent);
1284 if (pExtent->pDescData)
1285 {
1286 RTMemFree(pExtent->pDescData);
1287 pExtent->pDescData = NULL;
1288 }
1289 if (pExtent->pszFullname)
1290 {
1291 RTStrFree(pExtent->pszFullname);
1292 pExtent->pszFullname = NULL;
1293 }
1294 if (pExtent->File != NIL_RTFILE)
1295 {
1296 RTFileClose(pExtent->File);
1297 pExtent->File = NIL_RTFILE;
1298 }
1299}
1300
1301static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
1302{
1303 int rc = VINF_SUCCESS;
1304 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
1305 if (pImage)
1306 {
1307 for (unsigned i = 0; i < cExtents; i++)
1308 {
1309 pExtents[i].File = NIL_RTFILE;
1310 pExtents[i].pszBasename = NULL;
1311 pExtents[i].pszFullname = NULL;
1312 pExtents[i].pGD = NULL;
1313 pExtents[i].pRGD = NULL;
1314 pExtents[i].pDescData = NULL;
1315 pExtents[i].uExtent = i;
1316 pExtents[i].pImage = pImage;
1317 }
1318 pImage->pExtents = pExtents;
1319 pImage->cExtents = cExtents;
1320 }
1321 else
1322 rc = VERR_NO_MEMORY;
1323
1324 return rc;
1325}
1326
1327static int vmdkOpenImage(PVMDKIMAGE pImage, const char *pszFilename, unsigned uOpenFlags)
1328{
1329 int rc = VINF_SUCCESS;
1330 uint32_t u32Magic;
1331 RTFILE File;
1332 PVMDKEXTENT pExtent;
1333
1334 pImage->uOpenFlags = uOpenFlags;
1335
1336 /** @todo check the image file name for invalid characters, especially double quotes. */
1337
1338 /** @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. */
1339
1340 /*
1341 * Open the image.
1342 */
1343 rc = RTFileOpen(&File, pszFilename, uOpenFlags & VD_OPEN_FLAGS_READONLY
1344 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1345 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1346 if (VBOX_FAILURE(rc))
1347 {
1348 /* Do NOT signal an appropriate error here, as the VD layer has the
1349 * choice of retrying the open if it failed. */
1350 goto out;
1351 }
1352 pImage->File = File;
1353 rc = RTFileReadAt(File, 0, &u32Magic, sizeof(u32Magic), NULL);
1354 AssertRC(rc);
1355 if (VBOX_FAILURE(rc))
1356 {
1357 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error reading the magic number from file '%s'"), pszFilename);
1358 goto out;
1359 }
1360
1361 /* Handle the file according to its magic number. */
1362 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
1363 {
1364 /* It's a hosted sparse single-extent image. */
1365 rc = vmdkCreateExtents(pImage, 1);
1366 if (VBOX_FAILURE(rc))
1367 goto out;
1368 /* The opened file is passed to the extent. No separate descriptor
1369 * file, so no need to keep anything open for the image. */
1370 pExtent = &pImage->pExtents[0];
1371 pExtent->File = File;
1372 pImage->File = NIL_RTFILE;
1373 rc = vmdkReadMetaSparseExtent(pExtent);
1374 if (VBOX_FAILURE(rc))
1375 goto out;
1376 /* As we're dealing with a monolithic sparse image here, there must
1377 * be a descriptor embedded in the image file. */
1378 if (!pExtent->uDescriptorSector || !pExtent->cDescriptorSectors)
1379 {
1380 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in file '%s'"), pszFilename);
1381 goto out;
1382 }
1383 /* Read the descriptor from the extent. */
1384 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1385 if (!pExtent->pDescData)
1386 {
1387 rc = VERR_NO_MEMORY;
1388 goto out;
1389 }
1390 rc = RTFileReadAt(pExtent->File,
1391 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
1392 pExtent->pDescData,
1393 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors), NULL);
1394 AssertRC(rc);
1395 if (VBOX_FAILURE(rc))
1396 {
1397 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in file '%s'"), pExtent->pszFullname);
1398 goto out;
1399 }
1400
1401 rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
1402 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
1403 if (VBOX_FAILURE(rc))
1404 goto out;
1405
1406 /* Mark the extent as unclean if opened in read-write mode. */
1407 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1408 {
1409 pExtent->fUncleanShutdown = true;
1410 pExtent->fMetaDirty = true;
1411 }
1412 }
1413 else
1414 {
1415 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
1416 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
1417 if (!pImage->pDescData)
1418 {
1419 rc = VERR_NO_MEMORY;
1420 goto out;
1421 }
1422
1423 /*size_t*/unsigned cbRead;
1424 rc = RTFileReadAt(pImage->File, 0, pImage->pDescData,
1425 pImage->cbDescAlloc, &cbRead);
1426 if (VBOX_FAILURE(rc))
1427 {
1428 rc = vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in file '%s'"), pszFilename);
1429 goto out;
1430 }
1431 if (cbRead == pImage->cbDescAlloc)
1432 {
1433 /* Likely the read is truncated. Better fail a bit too early
1434 * (normally the descriptor is much smaller than our buffer). */
1435 rc = vmdkError(pImage, VERR_VDI_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in file '%s'"), pszFilename);
1436 goto out;
1437 }
1438
1439 rc = vmdkParseDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc);
1440 if (VBOX_FAILURE(rc))
1441 goto out;
1442
1443 for (unsigned i = 0; i < pImage->cExtents; i++)
1444 {
1445 PVMDKEXTENT pExtent = &pImage->pExtents[i];
1446
1447 if (pExtent->pszBasename)
1448 {
1449 /* Hack to figure out whether the specified name in the
1450 * extent descriptor is absolute. Doesn't always work, but
1451 * should be good enough for now. */
1452 /** @todo implement proper path absolute check. */
1453 if (pExtent->pszBasename[0] == RTPATH_SLASH)
1454 {
1455 pszFilename = RTStrDup(pExtent->pszBasename);
1456 if (!pszFilename)
1457 {
1458 rc = VERR_NO_MEMORY;
1459 goto out;
1460 }
1461 }
1462 else
1463 {
1464 size_t cbDirname;
1465 char *pszDirname = RTStrDup(pImage->pszFilename);
1466 if (!pszDirname)
1467 {
1468 rc = VERR_NO_MEMORY;
1469 goto out;
1470 }
1471 RTPathStripFilename(pszDirname);
1472 cbDirname = strlen(pszDirname);
1473 char *pszFullname;
1474 rc = RTStrAPrintf(&pszFullname, "%s%c%s", pszDirname,
1475 RTPATH_SLASH, pExtent->pszBasename);
1476 RTStrFree(pszDirname);
1477 if (VBOX_FAILURE(rc))
1478 goto out;
1479 pExtent->pszFullname = pszFullname;
1480 }
1481 }
1482 else
1483 pExtent->pszFullname = NULL;
1484
1485 switch (pExtent->enmType)
1486 {
1487 case VMDKETYPE_HOSTED_SPARSE:
1488 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1489 uOpenFlags & VD_OPEN_FLAGS_READONLY
1490 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1491 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1492 if (VBOX_FAILURE(rc))
1493 {
1494 /* Do NOT signal an appropriate error here, as the VD
1495 * layer has the choice of retrying the open if it
1496 * failed. */
1497 goto out;
1498 }
1499 rc = vmdkReadMetaSparseExtent(pExtent);
1500 if (VBOX_FAILURE(rc))
1501 goto out;
1502
1503 /* Mark the extent as unclean if opened in read-write mode. */
1504 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1505 {
1506 pExtent->fUncleanShutdown = true;
1507 pExtent->fMetaDirty = true;
1508 }
1509 break;
1510 case VMDKETYPE_FLAT:
1511 rc = RTFileOpen(&pExtent->File, pExtent->pszFullname,
1512 uOpenFlags & VD_OPEN_FLAGS_READONLY
1513 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1514 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1515 if (VBOX_FAILURE(rc))
1516 {
1517 /* Do NOT signal an appropriate error here, as the VD
1518 * layer has the choice of retrying the open if it
1519 * failed. */
1520 goto out;
1521 }
1522 break;
1523 case VMDKETYPE_ZERO:
1524 /* Nothing to do. */
1525 break;
1526 default:
1527 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
1528 }
1529 }
1530 }
1531
1532 /* Make sure this is not reached accidentally with an error status. */
1533 AssertRC(rc);
1534
1535 /* Update the image metadata now in case has changed. */
1536 rc = vmdkFlushImage(pImage);
1537 if (VBOX_FAILURE(rc))
1538 goto out;
1539
1540 /* Figure out a few per-image constants from the extents. */
1541 pImage->cbSize = 0;
1542 for (unsigned i = 0; i < pImage->cExtents; i++)
1543 {
1544 pExtent = &pImage->pExtents[i];
1545 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1546#ifdef VBOX_WITH_VMDK_ESX
1547 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1548#endif /* VBOX_WITH_VMDK_ESX */
1549 )
1550 {
1551 /* Here used to be a check whether the nominal size of an extent
1552 * is a multiple of the grain size. The spec says that this is
1553 * always the case, but unfortunately some files out there in the
1554 * wild violate the spec (e.g. ReactOS 0.3.1). */
1555 }
1556 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
1557 }
1558
1559 pImage->enmImageType = VD_IMAGE_TYPE_NORMAL;
1560 for (unsigned i = 0; i < pImage->cExtents; i++)
1561 {
1562 pExtent = &pImage->pExtents[i];
1563 if ( pImage->pExtents[i].enmType == VMDKETYPE_FLAT
1564 || pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
1565 {
1566 pImage->enmImageType = VD_IMAGE_TYPE_FIXED;
1567 break;
1568 }
1569 }
1570
1571 /* Allocate grain table cache if any sparse extent is present. */
1572 for (unsigned i = 0; i < pImage->cExtents; i++)
1573 {
1574 pExtent = &pImage->pExtents[i];
1575 if ( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1576#ifdef VBOX_WITH_VMDK_ESX
1577 || pExtent->enmType == VMDKETYPE_ESX_SPARSE
1578#endif /* VBOX_WITH_VMDK_ESX */
1579 )
1580 {
1581 /* Allocate grain table cache. */
1582 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
1583 if (!pImage->pGTCache)
1584 {
1585 rc = VERR_NO_MEMORY;
1586 goto out;
1587 }
1588 for (unsigned i = 0; i < VMDK_GT_CACHE_SIZE; i++)
1589 {
1590 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[i];
1591 pGCE->uExtent = UINT32_MAX;
1592 }
1593 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
1594 break;
1595 }
1596 }
1597
1598out:
1599 if (VBOX_FAILURE(rc))
1600 vmdkFreeImage(pImage);
1601 return rc;
1602}
1603
1604static void vmdkFreeImage(PVMDKIMAGE pImage)
1605{
1606 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1607 {
1608 /* Mark all extents as clean. */
1609 for (unsigned i = 0; i < pImage->cExtents; i++)
1610 {
1611 if (( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
1612#ifdef VBOX_WITH_VMDK_ESX
1613 || pImage->pExtents[i].enmType == VMDKETYPE_ESX_SPARSE
1614#endif /* VBOX_WITH_VMDK_ESX */
1615 )
1616 && pImage->pExtents[i].fUncleanShutdown)
1617 {
1618 pImage->pExtents[i].fUncleanShutdown = false;
1619 pImage->pExtents[i].fMetaDirty = true;
1620 }
1621 }
1622 }
1623 (void)vmdkFlushImage(pImage);
1624 if (pImage->pExtents != NULL)
1625 {
1626 for (unsigned i = 0 ; i < pImage->cExtents; i++)
1627 vmdkFreeExtentData(&pImage->pExtents[i]);
1628 RTMemFree(pImage->pExtents);
1629 pImage->pExtents = NULL;
1630 }
1631 if (pImage->File != NIL_RTFILE)
1632 {
1633 RTFileClose(pImage->File);
1634 pImage->File = NIL_RTFILE;
1635 }
1636}
1637
1638static int vmdkFlushImage(PVMDKIMAGE pImage)
1639{
1640 PVMDKEXTENT pExtent;
1641 int rc = VINF_SUCCESS;
1642
1643 /* Update descriptor if changed. */
1644 if (pImage->Descriptor.fDirty)
1645 {
1646 rc = vmdkWriteDescriptor(pImage);
1647 if (VBOX_FAILURE(rc))
1648 goto out;
1649 }
1650
1651 for (unsigned i = 0; i < pImage->cExtents; i++)
1652 {
1653 pExtent = &pImage->pExtents[i];
1654 if (pExtent->File != NIL_RTFILE && pExtent->fMetaDirty)
1655 {
1656 switch (pExtent->enmType)
1657 {
1658 case VMDKETYPE_HOSTED_SPARSE:
1659 rc = vmdkWriteMetaSparseExtent(pExtent);
1660 if (VBOX_FAILURE(rc))
1661 goto out;
1662 break;
1663#ifdef VBOX_WITH_VMDK_ESX
1664 case VMDKETYPE_ESX_SPARSE:
1665 /** @todo update the header. */
1666 break;
1667#endif /* VBOX_WITH_VMDK_ESX */
1668 case VMDKETYPE_FLAT:
1669 /* Nothing to do. */
1670 break;
1671 case VMDKETYPE_ZERO:
1672 default:
1673 AssertMsgFailed(("extent with type %d marked as dirty\n",
1674 pExtent->enmType));
1675 break;
1676 }
1677 }
1678 switch (pExtent->enmType)
1679 {
1680 case VMDKETYPE_HOSTED_SPARSE:
1681#ifdef VBOX_WITH_VMDK_ESX
1682 case VMDKETYPE_ESX_SPARSE:
1683#endif /* VBOX_WITH_VMDK_ESX */
1684 case VMDKETYPE_FLAT:
1685 if (pExtent->File != NIL_RTFILE && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1686 rc = RTFileFlush(pExtent->File);
1687 break;
1688 case VMDKETYPE_ZERO:
1689 /* No need to do anything for this extent. */
1690 break;
1691 default:
1692 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
1693 break;
1694 }
1695 }
1696
1697out:
1698 return rc;
1699}
1700
1701static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector, PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
1702{
1703 PVMDKEXTENT pExtent = NULL;
1704 int rc = VINF_SUCCESS;
1705
1706 for (unsigned i = 0; i < pImage->cExtents; i++)
1707 {
1708 if (offSector < pImage->pExtents[i].cNominalSectors)
1709 {
1710 pExtent = &pImage->pExtents[i];
1711 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
1712 break;
1713 }
1714 offSector -= pImage->pExtents[i].cNominalSectors;
1715 }
1716
1717 if (pExtent)
1718 *ppExtent = pExtent;
1719 else
1720 rc = VERR_IO_SECTOR_NOT_FOUND;
1721
1722 return rc;
1723}
1724
1725static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector, unsigned uExtent)
1726{
1727 /** @todo this hash function is quite simple, maybe use a better one which
1728 * scrambles the bits better. */
1729 return (uSector + uExtent) % pCache->cEntries;
1730}
1731
1732static int vmdkGetSector(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
1733 uint64_t uSector, uint64_t *puExtentSector)
1734{
1735 uint64_t uGDIndex, uGTSector, uGTBlock;
1736 uint32_t uGTHash, uGTBlockIndex;
1737 PVMDKGTCACHEENTRY pGTCacheEntry;
1738 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
1739 int rc;
1740
1741 uGDIndex = uSector / pExtent->cSectorsPerGDE;
1742 if (uGDIndex >= pExtent->cGDEntries)
1743 return VERR_OUT_OF_RANGE;
1744 uGTSector = pExtent->pGD[uGDIndex];
1745 if (!uGTSector)
1746 {
1747 /* There is no grain table referenced by this grain directory
1748 * entry. So there is absolutely no data in this area. */
1749 *puExtentSector = 0;
1750 return VINF_SUCCESS;
1751 }
1752
1753 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
1754 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
1755 pGTCacheEntry = &pCache->aGTCache[uGTHash];
1756 if ( pGTCacheEntry->uExtent != pExtent->uExtent
1757 || pGTCacheEntry->uGTBlock != uGTBlock)
1758 {
1759 /* Cache miss, fetch data from disk. */
1760 rc = RTFileReadAt(pExtent->File,
1761 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1762 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1763 if (VBOX_FAILURE(rc))
1764 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read grain table entry in file '%s'"), pExtent->pszFullname);
1765 pGTCacheEntry->uExtent = pExtent->uExtent;
1766 pGTCacheEntry->uGTBlock = uGTBlock;
1767 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1768 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
1769 }
1770 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
1771 uint64_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
1772 if (uGrainSector)
1773 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
1774 else
1775 *puExtentSector = 0;
1776 return VINF_SUCCESS;
1777}
1778
1779/**
1780 * Internal. Allocates a new grain table (if necessary), writes the grain
1781 * and updates the grain table. The cache is also updated by this operation.
1782 * This is separate from vmdkGetSector, because that should be as fast as
1783 * possible. Most code from vmdkGetSector also appears here.
1784 */
1785static int vmdkAllocGrain(PVMDKGTCACHE pCache, PVMDKEXTENT pExtent,
1786 uint64_t uSector, const void *pvBuf, uint64_t cbWrite)
1787{
1788 uint64_t uGDIndex, uGTSector, uRGTSector, uGTBlock;
1789 uint64_t cbExtentSize;
1790 uint32_t uGTHash, uGTBlockIndex;
1791 PVMDKGTCACHEENTRY pGTCacheEntry;
1792 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
1793 int rc;
1794
1795 uGDIndex = uSector / pExtent->cSectorsPerGDE;
1796 if (uGDIndex >= pExtent->cGDEntries)
1797 return VERR_OUT_OF_RANGE;
1798 uGTSector = pExtent->pGD[uGDIndex];
1799 uRGTSector = pExtent->pRGD[uGDIndex];
1800 if (!uGTSector)
1801 {
1802 /* There is no grain table referenced by this grain directory
1803 * entry. So there is absolutely no data in this area. Allocate
1804 * a new grain table and put the reference to it in the GDs. */
1805 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1806 if (VBOX_FAILURE(rc))
1807 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1808 Assert(!(cbExtentSize % 512));
1809 uGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
1810 /* Normally the grain table is preallocated for hosted sparse extents
1811 * that support more than 32 bit sector numbers. So this shouldn't
1812 * ever happen on a valid extent. */
1813 if (uGTSector > UINT32_MAX)
1814 return VERR_VDI_INVALID_HEADER;
1815 /* Write grain table by writing the required number of grain table
1816 * cache chunks. Avoids dynamic memory allocation, but is a bit
1817 * slower. But as this is a pretty infrequently occurring case it
1818 * should be acceptable. */
1819 memset(aGTDataTmp, '\0', sizeof(aGTDataTmp));
1820 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
1821 {
1822 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
1823 if (VBOX_FAILURE(rc))
1824 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in file '%s'"), pExtent->pszFullname);
1825 }
1826 if (pExtent->pRGD)
1827 {
1828 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1829 if (VBOX_FAILURE(rc))
1830 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1831 Assert(!(cbExtentSize % 512));
1832 uRGTSector = VMDK_BYTE2SECTOR(cbExtentSize);
1833 /* Write backup grain table by writing the required number of grain
1834 * table cache chunks. Avoids dynamic memory allocation, but is a
1835 * bit slower. But as this is a pretty infrequently occurring case
1836 * it should be acceptable. */
1837 for (unsigned i = 0; i < pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE; i++)
1838 {
1839 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uRGTSector) + i * sizeof(aGTDataTmp), aGTDataTmp, sizeof(aGTDataTmp), NULL);
1840 if (VBOX_FAILURE(rc))
1841 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in file '%s'"), pExtent->pszFullname);
1842 }
1843 }
1844
1845 /* Update the grain directory on disk (doing it before writing the
1846 * grain table will result in a garbled extent if the operation is
1847 * aborted for some reason. Otherwise the worst that can happen is
1848 * some unused sectors in the extent. */
1849 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
1850 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE), &uGTSectorLE, sizeof(uGTSectorLE), NULL);
1851 if (VBOX_FAILURE(rc))
1852 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in file '%s'"), pExtent->pszFullname);
1853 if (pExtent->pRGD)
1854 {
1855 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
1856 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uRGTSectorLE), &uRGTSectorLE, sizeof(uRGTSectorLE), NULL);
1857 if (VBOX_FAILURE(rc))
1858 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in file '%s'"), pExtent->pszFullname);
1859 }
1860
1861 /* As the final step update the in-memory copy of the GDs. */
1862 pExtent->pGD[uGDIndex] = uGTSector;
1863 if (pExtent->pRGD)
1864 pExtent->pRGD[uGDIndex] = uRGTSector;
1865 }
1866
1867 rc = RTFileGetSize(pExtent->File, &cbExtentSize);
1868 if (VBOX_FAILURE(rc))
1869 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: error getting size of file '%s'"), pExtent->pszFullname);
1870 Assert(!(cbExtentSize % 512));
1871
1872 /* Write the data. */
1873 rc = RTFileWriteAt(pExtent->File, cbExtentSize, pvBuf, cbWrite, NULL);
1874 if (VBOX_FAILURE(rc))
1875 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in file '%s'"), pExtent->pszFullname);
1876
1877 /* Update the grain table (and the cache). */
1878 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
1879 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
1880 pGTCacheEntry = &pCache->aGTCache[uGTHash];
1881 if ( pGTCacheEntry->uExtent != pExtent->uExtent
1882 || pGTCacheEntry->uGTBlock != uGTBlock)
1883 {
1884 /* Cache miss, fetch data from disk. */
1885 rc = RTFileReadAt(pExtent->File,
1886 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1887 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1888 if (VBOX_FAILURE(rc))
1889 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in file '%s'"), pExtent->pszFullname);
1890 pGTCacheEntry->uExtent = pExtent->uExtent;
1891 pGTCacheEntry->uGTBlock = uGTBlock;
1892 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1893 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
1894 }
1895 else
1896 {
1897 /* Cache hit. Convert grain table block back to disk format, otherwise
1898 * the code below will write garbage for all but the updated entry. */
1899 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
1900 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
1901 }
1902 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
1903 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(cbExtentSize));
1904 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(cbExtentSize);
1905 /* Update grain table on disk. */
1906 rc = RTFileWriteAt(pExtent->File,
1907 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1908 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1909 if (VBOX_FAILURE(rc))
1910 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in file '%s'"), pExtent->pszFullname);
1911 if (pExtent->pRGD)
1912 {
1913 /* Update backup grain table on disk. */
1914 rc = RTFileWriteAt(pExtent->File,
1915 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
1916 aGTDataTmp, sizeof(aGTDataTmp), NULL);
1917 if (VBOX_FAILURE(rc))
1918 return vmdkError(pExtent->pImage, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in file '%s'"), pExtent->pszFullname);
1919 }
1920#ifdef VBOX_WITH_VMDK_ESX
1921 if (VBOX_SUCCESS(rc) && pExtent->enmType == VMDKETYPE_ESX_SPARSE)
1922 {
1923 pExtent->uFreeSector = uGTSector + VMDK_BYTE2SECTOR(cbWrite);
1924 pExtent->fMetaDirty = true;
1925 }
1926#endif /* VBOX_WITH_VMDK_ESX */
1927 return rc;
1928}
1929
1930static int vmdkOpen(const char *pszFilename, unsigned uOpenFlags, PFNVDERROR pfnError, void *pvErrorUser, void **ppvBackendData)
1931{
1932 int rc = VINF_SUCCESS;
1933 PVMDKIMAGE pImage;
1934
1935 /* Check open flags. Just readonly flag is supported. */
1936 if (uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
1937 {
1938 rc = VERR_INVALID_PARAMETER;
1939 goto out;
1940 }
1941
1942 pImage = (PVMDKIMAGE)RTMemAllocZ(sizeof(VMDKIMAGE));
1943 if (!pImage)
1944 {
1945 rc = VERR_NO_MEMORY;
1946 goto out;
1947 }
1948 pImage->pszFilename = pszFilename;
1949 pImage->File = NIL_RTFILE;
1950 pImage->pExtents = NULL;
1951 pImage->pGTCache = NULL;
1952 pImage->pDescData = NULL;
1953 pImage->pfnError = pfnError;
1954 pImage->pvErrorUser = pvErrorUser;
1955
1956 if (VBOX_SUCCESS(rc))
1957 {
1958 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
1959 if (VBOX_SUCCESS(rc))
1960 *ppvBackendData = pImage;
1961 }
1962
1963out:
1964 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
1965 return rc;
1966}
1967
1968static int vmdkCreate(const char *pszFilename, VDIMAGETYPE penmType,
1969 uint64_t cbSize, unsigned uImageFlags,
1970 const char *pszComment, unsigned uOpenFlags,
1971 PFNVMPROGRESS pfnProgress, void *pvUser,
1972 PFNVDERROR pfnError, void *pvErrorUser,
1973 void **ppvBackendData)
1974{
1975 return VERR_NOT_IMPLEMENTED;
1976}
1977
1978static int vmdkClose(void *pBackendData)
1979{
1980 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
1981 int rc = VINF_SUCCESS;
1982
1983 /* Freeing a never allocated image (e.g. because the open failed) is
1984 * not signalled as an error. After all nothing bad happens. */
1985 if (pImage)
1986 vmdkFreeImage(pImage);
1987
1988 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
1989 return rc;
1990}
1991
1992static int vmdkRead(void *pBackendData, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbActuallyRead)
1993{
1994 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
1995 PVMDKEXTENT pExtent;
1996 uint64_t uSectorExtentRel;
1997 uint64_t uSectorExtentAbs;
1998 int rc;
1999
2000 Assert(uOffset % 512 == 0);
2001 Assert(cbRead % 512 == 0);
2002
2003 if (uOffset + cbRead > pImage->cbSize)
2004 {
2005 rc = VERR_INVALID_PARAMETER;
2006 goto out;
2007 }
2008
2009 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
2010 &pExtent, &uSectorExtentRel);
2011 if (VBOX_FAILURE(rc))
2012 goto out;
2013
2014 /* Check access permissions as defined in the extent descriptor. */
2015 if (pExtent->enmAccess == VMDKACCESS_NOACCESS)
2016 {
2017 rc = VERR_VDI_INVALID_STATE;
2018 goto out;
2019 }
2020
2021 /* Clip read range to remain in this extent. */
2022 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
2023
2024 /* Handle the read according to the current extent type. */
2025 switch (pExtent->enmType)
2026 {
2027 case VMDKETYPE_HOSTED_SPARSE:
2028#ifdef VBOX_WITH_VMDK_ESX
2029 case VMDKETYPE_ESX_SPARSE:
2030#endif /* VBOX_WITH_VMDK_ESX */
2031 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
2032 &uSectorExtentAbs);
2033 if (VBOX_FAILURE(rc))
2034 goto out;
2035 /* Clip read range to at most the rest of the grain. */
2036 cbRead = RT_MIN(cbRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
2037 Assert(!(cbRead % 512));
2038 if (uSectorExtentAbs == 0)
2039 rc = VINF_VDI_BLOCK_FREE;
2040 else
2041 rc = RTFileReadAt(pExtent->File,
2042 VMDK_SECTOR2BYTE(uSectorExtentAbs),
2043 pvBuf, cbRead, NULL);
2044 break;
2045 case VMDKETYPE_FLAT:
2046 rc = RTFileReadAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel),
2047 pvBuf, cbRead, NULL);
2048 break;
2049 case VMDKETYPE_ZERO:
2050 memset(pvBuf, '\0', cbRead);
2051 break;
2052 }
2053 *pcbActuallyRead = cbRead;
2054
2055out:
2056 return rc;
2057}
2058
2059static int vmdkWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead)
2060{
2061 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2062 PVMDKEXTENT pExtent;
2063 uint64_t uSectorExtentRel;
2064 uint64_t uSectorExtentAbs;
2065 int rc;
2066
2067 Assert(uOffset % 512 == 0);
2068 Assert(cbWrite % 512 == 0);
2069
2070 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2071 {
2072 rc = VERR_VDI_IMAGE_READ_ONLY;
2073 goto out;
2074 }
2075
2076 /* No size check here, will do that later when the extent is located.
2077 * There are sparse images out there which according to the spec are
2078 * invalid, because the total size is not a multiple of the grain size.
2079 * Also for sparse images which are stitched together in odd ways (not at
2080 * grain boundaries, and with the nominal size not being a multiple of the
2081 * grain size), this would prevent writing to the last grain. */
2082
2083 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
2084 &pExtent, &uSectorExtentRel);
2085 if (VBOX_FAILURE(rc))
2086 goto out;
2087
2088 /* Check access permissions as defined in the extent descriptor. */
2089 if (pExtent->enmAccess != VMDKACCESS_READWRITE)
2090 {
2091 rc = VERR_VDI_INVALID_STATE;
2092 goto out;
2093 }
2094
2095 /** @todo implement suppressing of zero data writes (a bit tricky in this
2096 * case, as VMDK has no marker for zero blocks). We somehow need to get the
2097 * information whether the information in this area is all zeroes as of the
2098 * parent image. Then (based on the assumption that parent images are
2099 * immutable) the write can be ignored. */
2100
2101 /* Handle the write according to the current extent type. */
2102 switch (pExtent->enmType)
2103 {
2104 case VMDKETYPE_HOSTED_SPARSE:
2105#ifdef VBOX_WITH_VMDK_ESX
2106 case VMDKETYPE_ESX_SPARSE:
2107#endif /* VBOX_WITH_VMDK_ESX */
2108 rc = vmdkGetSector(pImage->pGTCache, pExtent, uSectorExtentRel,
2109 &uSectorExtentAbs);
2110 if (VBOX_FAILURE(rc))
2111 goto out;
2112 /* Clip write range to at most the rest of the grain. */
2113 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
2114 if (uSectorExtentAbs == 0)
2115 {
2116 if (cbWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
2117 {
2118 /* Full block write to a previously unallocated block.
2119 * Allocate GT and find out where to store the grain. */
2120 rc = vmdkAllocGrain(pImage->pGTCache, pExtent,
2121 uSectorExtentRel, pvBuf, cbWrite);
2122 }
2123 else
2124 {
2125 /* Clip write range to remain in this extent. */
2126 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
2127 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
2128 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite - *pcbPreRead;
2129 rc = VINF_VDI_BLOCK_FREE;
2130 }
2131 }
2132 else
2133 rc = RTFileWriteAt(pExtent->File,
2134 VMDK_SECTOR2BYTE(uSectorExtentAbs),
2135 pvBuf, cbWrite, NULL);
2136 break;
2137 case VMDKETYPE_FLAT:
2138 /* Clip write range to remain in this extent. */
2139 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
2140 rc = RTFileWriteAt(pExtent->File, VMDK_SECTOR2BYTE(uSectorExtentRel), pvBuf, cbWrite, NULL);
2141 break;
2142 case VMDKETYPE_ZERO:
2143 /* Clip write range to remain in this extent. */
2144 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cNominalSectors - uSectorExtentRel));
2145 break;
2146 }
2147 if (pcbWriteProcess)
2148 *pcbWriteProcess = cbWrite;
2149
2150out:
2151 return rc;
2152}
2153
2154static int vmdkFlush(void *pBackendData)
2155{
2156 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2157
2158 int rc = vmdkFlushImage(pImage);
2159
2160 return rc;
2161}
2162
2163static int vmdkGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
2164{
2165 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2166 int rc = VINF_SUCCESS;
2167
2168 Assert(pImage);
2169 Assert(penmImageType);
2170
2171 if (pImage && pImage->cExtents != 0)
2172 *penmImageType = pImage->enmImageType;
2173 else
2174 rc = VERR_VDI_NOT_OPENED;
2175
2176 return rc;
2177}
2178
2179static uint64_t vmdkGetSize(void *pBackendData)
2180{
2181 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2182
2183 Assert(pImage);
2184
2185 if (pImage)
2186 return pImage->cbSize;
2187 else
2188 return 0;
2189}
2190
2191static int vmdkGetGeometry(void *pBackendData, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
2192{
2193 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2194 int rc;
2195
2196 Assert(pImage);
2197
2198 if (pImage)
2199 {
2200 if (pImage->cCylinders)
2201 {
2202 *pcCylinders = pImage->cCylinders;
2203 *pcHeads = pImage->cHeads;
2204 *pcSectors = pImage->cSectors;
2205 rc = VINF_SUCCESS;
2206 }
2207 else
2208 rc = VERR_VDI_GEOMETRY_NOT_SET;
2209 }
2210 else
2211 rc = VERR_VDI_NOT_OPENED;
2212 LogFlow(("%s: returned %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
2213 pImage->cCylinders, pImage->cHeads, pImage->cSectors));
2214 return rc;
2215}
2216
2217static int vmdkSetGeometry(void *pBackendData, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
2218{
2219 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2220 int rc;
2221
2222 Assert(pImage);
2223
2224 if (pImage)
2225 {
2226 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2227 {
2228 rc = VERR_VDI_IMAGE_READ_ONLY;
2229 goto out;
2230 }
2231 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2232 "ddb.geometry.cylinders", cCylinders);
2233 if (VBOX_FAILURE(rc))
2234 goto out;
2235 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2236 "ddb.geometry.heads", cHeads);
2237 if (VBOX_FAILURE(rc))
2238 goto out;
2239 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2240 "ddb.geometry.sectors", cSectors);
2241 if (VBOX_FAILURE(rc))
2242 goto out;
2243
2244 pImage->cCylinders = cCylinders;
2245 pImage->cHeads = cHeads;
2246 pImage->cSectors = cSectors;
2247 rc = VINF_SUCCESS;
2248 }
2249 else
2250 rc = VERR_VDI_NOT_OPENED;
2251
2252out:
2253 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2254 return rc;
2255}
2256
2257static int vmdkGetTranslation(void *pBackendData, PPDMBIOSTRANSLATION penmTranslation)
2258{
2259 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2260 int rc;
2261
2262 Assert(pImage);
2263
2264 if (pImage)
2265 {
2266 if (pImage->enmTranslation)
2267 {
2268 *penmTranslation = pImage->enmTranslation;
2269 rc = VINF_SUCCESS;
2270 }
2271 else
2272 rc = VERR_VDI_GEOMETRY_NOT_SET;
2273 }
2274 else
2275 rc = VERR_VDI_NOT_OPENED;
2276 LogFlow(("%s: returned %Vrc (%d)\n", __FUNCTION__, rc,
2277 pImage->enmTranslation));
2278 return rc;
2279}
2280
2281static int vmdkSetTranslation(void *pBackendData, PDMBIOSTRANSLATION enmTranslation)
2282{
2283 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2284 int rc;
2285
2286 Assert(pImage);
2287
2288 if (pImage)
2289 {
2290 /** @todo maybe store this in the image descriptor */
2291 pImage->enmTranslation = enmTranslation;
2292 rc = VINF_SUCCESS;
2293 }
2294 else
2295 rc = VERR_VDI_NOT_OPENED;
2296 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2297 return rc;
2298}
2299
2300static unsigned vmdkGetOpenFlags(void *pBackendData)
2301{
2302 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2303 unsigned uOpenFlags;
2304
2305 Assert(pImage);
2306
2307 if (pImage)
2308 uOpenFlags = pImage->uOpenFlags;
2309 else
2310 uOpenFlags = 0;
2311
2312 LogFlow(("%s: returned %d\n", __FUNCTION__, uOpenFlags));
2313 return uOpenFlags;
2314}
2315
2316static int vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2317{
2318 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2319 int rc;
2320 const char *pszFilename;
2321
2322 /* Image must be opened and the new flags must be valid. Just readonly flag
2323 * is supported. */
2324 if (!pImage || uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
2325 {
2326 rc = VERR_INVALID_PARAMETER;
2327 goto out;
2328 }
2329
2330 /* Implement this operation via reopening the image. */
2331 pszFilename = pImage->pszFilename;
2332 vmdkFreeImage(pImage);
2333 rc = vmdkOpenImage(pImage, pszFilename, uOpenFlags);
2334
2335out:
2336 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2337 return rc;
2338}
2339
2340static int vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
2341{
2342 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2343 int rc;
2344
2345 Assert(pImage);
2346
2347 if (pImage)
2348 {
2349 *pUuid = pImage->ImageUuid;
2350 rc = VINF_SUCCESS;
2351 }
2352 else
2353 rc = VERR_VDI_NOT_OPENED;
2354 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2355 return rc;
2356}
2357
2358static int vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
2359{
2360 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2361 int rc;
2362
2363 LogFlow(("%s: %Vuuid\n", pUuid));
2364 Assert(pImage);
2365
2366 if (pImage)
2367 {
2368 pImage->ImageUuid = *pUuid;
2369 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2370 "ddb.uuid.image", pUuid);
2371 if (VBOX_FAILURE(rc))
2372 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor"));
2373 rc = VINF_SUCCESS;
2374 }
2375 else
2376 rc = VERR_VDI_NOT_OPENED;
2377 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2378 return rc;
2379}
2380
2381static int vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2382{
2383 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2384 int rc;
2385
2386 Assert(pImage);
2387
2388 if (pImage)
2389 {
2390 *pUuid = pImage->ModificationUuid;
2391 rc = VINF_SUCCESS;
2392 }
2393 else
2394 rc = VERR_VDI_NOT_OPENED;
2395 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2396 return rc;
2397}
2398
2399static int vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2400{
2401 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2402 int rc;
2403
2404 LogFlow(("%s: %Vuuid\n", pUuid));
2405 Assert(pImage);
2406
2407 if (pImage)
2408 {
2409 pImage->ModificationUuid = *pUuid;
2410 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2411 "ddb.uuid.modification", pUuid);
2412 if (VBOX_FAILURE(rc))
2413 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor"));
2414 rc = VINF_SUCCESS;
2415 }
2416 else
2417 rc = VERR_VDI_NOT_OPENED;
2418 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2419 return rc;
2420}
2421
2422static int vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
2423{
2424 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2425 int rc;
2426
2427 Assert(pImage);
2428
2429 if (pImage)
2430 {
2431 *pUuid = pImage->ParentUuid;
2432 rc = VINF_SUCCESS;
2433 }
2434 else
2435 rc = VERR_VDI_NOT_OPENED;
2436 LogFlow(("%s: returned %Vrc (%Vuuid)\n", __FUNCTION__, rc, pUuid));
2437 return rc;
2438}
2439
2440static int vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2441{
2442 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
2443 int rc;
2444
2445 LogFlow(("%s: %Vuuid\n", pUuid));
2446 Assert(pImage);
2447
2448 if (pImage)
2449 {
2450 pImage->ParentUuid = *pUuid;
2451 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2452 "ddb.uuid.parent", pUuid);
2453 if (VBOX_FAILURE(rc))
2454 return vmdkError(pImage, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor"));
2455 rc = VINF_SUCCESS;
2456 }
2457 else
2458 rc = VERR_VDI_NOT_OPENED;
2459 LogFlow(("%s: returned %Vrc\n", __FUNCTION__, rc));
2460 return rc;
2461}
2462
2463
2464VBOXHDDBACKEND g_VmdkBackend =
2465{
2466 /* pfnOpen */
2467 vmdkOpen,
2468 /* pfnCreate */
2469 vmdkCreate,
2470 /* pfnClose */
2471 vmdkClose,
2472 /* pfnRead */
2473 vmdkRead,
2474 /* pfnWrite */
2475 vmdkWrite,
2476 /* pfnFlush */
2477 vmdkFlush,
2478 /* pfnGetImageType */
2479 vmdkGetImageType,
2480 /* pfnGetSize */
2481 vmdkGetSize,
2482 /* pfnGetGeometry */
2483 vmdkGetGeometry,
2484 /* pfnSetGeometry */
2485 vmdkSetGeometry,
2486 /* pfnGetTranslation */
2487 vmdkGetTranslation,
2488 /* pfnSetTranslation */
2489 vmdkSetTranslation,
2490 /* pfnGetOpenFlags */
2491 vmdkGetOpenFlags,
2492 /* pfnSetOpenFlags */
2493 vmdkSetOpenFlags,
2494 /* pfnGetUuid */
2495 vmdkGetUuid,
2496 /* pfnGetUuid */
2497 vmdkSetUuid,
2498 /* pfnGetModificationUuid */
2499 vmdkGetModificationUuid,
2500 /* pfnSetModificationUuid */
2501 vmdkSetModificationUuid,
2502 /* pfnGetParentUuid */
2503 vmdkGetParentUuid,
2504 /* pfnSetParentUuid */
2505 vmdkSetParentUuid
2506};
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