VirtualBox

source: vbox/trunk/src/VBox/Storage/VDI.cpp@ 62740

Last change on this file since 62740 was 62740, checked in by vboxsync, 8 years ago

VDI*: Disabling MSC warnings for the v1plus UUIDs alignment 'problem'. Should be rather harmless since UUIDs aren't really two uint64_t's.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.0 KB
Line 
1/* $Id: VDI.cpp 62740 2016-07-30 12:29:40Z vboxsync $ */
2/** @file
3 * Virtual Disk Image (VDI), Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_VDI
23#include <VBox/vd-plugin.h>
24#include "VDICore.h"
25#include <VBox/err.h>
26
27#include <VBox/log.h>
28#include <iprt/alloc.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
33
34#include "VDBackends.h"
35
36#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
37
38/** Macros for endianess conversion. */
39#define SET_ENDIAN_U32(conv, u32) (conv == VDIECONV_H2F ? RT_H2LE_U32(u32) : RT_LE2H_U32(u32))
40#define SET_ENDIAN_U64(conv, u64) (conv == VDIECONV_H2F ? RT_H2LE_U64(u64) : RT_LE2H_U64(u64))
41
42
43/*********************************************************************************************************************************
44* Static Variables *
45*********************************************************************************************************************************/
46
47/** NULL-terminated array of supported file extensions. */
48static const VDFILEEXTENSION s_aVdiFileExtensions[] =
49{
50 {"vdi", VDTYPE_HDD},
51 {NULL, VDTYPE_INVALID}
52};
53
54
55/*********************************************************************************************************************************
56* Internal Functions *
57*********************************************************************************************************************************/
58static unsigned getPowerOfTwo(unsigned uNumber);
59static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
60static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
61static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
62 const char *pszComment, uint64_t cbDisk,
63 uint32_t cbBlock, uint32_t cbBlockExtra);
64static int vdiValidateHeader(PVDIHEADER pHeader);
65static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
66static int vdiUpdateHeader(PVDIIMAGEDESC pImage);
67static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
68static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
69static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx,
70 bool fUpdateHdr);
71
72/**
73 * Internal: Convert the PreHeader fields to the appropriate endianess.
74 * @param enmConv Direction of the conversion.
75 * @param pPreHdrConv Where to store the converted pre header.
76 * @param pPreHdr PreHeader pointer.
77 */
78static void vdiConvPreHeaderEndianess(VDIECONV enmConv, PVDIPREHEADER pPreHdrConv,
79 PVDIPREHEADER pPreHdr)
80{
81 memcpy(pPreHdrConv->szFileInfo, pPreHdr->szFileInfo, sizeof(pPreHdr->szFileInfo));
82 pPreHdrConv->u32Signature = SET_ENDIAN_U32(enmConv, pPreHdr->u32Signature);
83 pPreHdrConv->u32Version = SET_ENDIAN_U32(enmConv, pPreHdr->u32Version);
84}
85
86/**
87 * Internal: Convert the VDIDISKGEOMETRY fields to the appropriate endianess.
88 * @param enmConv Direction of the conversion.
89 * @param pDiskGeoConv Where to store the converted geometry.
90 * @param pDiskGeo Pointer to the disk geometry to convert.
91 */
92static void vdiConvGeometryEndianess(VDIECONV enmConv, PVDIDISKGEOMETRY pDiskGeoConv,
93 PVDIDISKGEOMETRY pDiskGeo)
94{
95 pDiskGeoConv->cCylinders = SET_ENDIAN_U32(enmConv, pDiskGeo->cCylinders);
96 pDiskGeoConv->cHeads = SET_ENDIAN_U32(enmConv, pDiskGeo->cHeads);
97 pDiskGeoConv->cSectors = SET_ENDIAN_U32(enmConv, pDiskGeo->cSectors);
98 pDiskGeoConv->cbSector = SET_ENDIAN_U32(enmConv, pDiskGeo->cbSector);
99}
100
101/**
102 * Internal: Convert the Header - version 0 fields to the appropriate endianess.
103 * @param enmConv Direction of the conversion.
104 * @param pHdrConv Where to store the converted header.
105 * @param pHdr Pointer to the version 0 header.
106 */
107static void vdiConvHeaderEndianessV0(VDIECONV enmConv, PVDIHEADER0 pHdrConv,
108 PVDIHEADER0 pHdr)
109{
110 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
111 pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
112 pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
113 vdiConvGeometryEndianess(enmConv, &pHdrConv->LegacyGeometry, &pHdr->LegacyGeometry);
114 pHdrConv->cbDisk = SET_ENDIAN_U64(enmConv, pHdr->cbDisk);
115 pHdrConv->cbBlock = SET_ENDIAN_U32(enmConv, pHdr->cbBlock);
116 pHdrConv->cBlocks = SET_ENDIAN_U32(enmConv, pHdr->cBlocks);
117 pHdrConv->cBlocksAllocated = SET_ENDIAN_U32(enmConv, pHdr->cBlocksAllocated);
118 /* Don't convert the RTUUID fields. */
119 pHdrConv->uuidCreate = pHdr->uuidCreate;
120 pHdrConv->uuidModify = pHdr->uuidModify;
121 pHdrConv->uuidLinkage = pHdr->uuidLinkage;
122}
123
124/**
125 * Internal: Set the Header - version 1 fields to the appropriate endianess.
126 * @param enmConv Direction of the conversion.
127 * @param pHdrConv Where to store the converted header.
128 * @param pHdr Version 1 Header pointer.
129 */
130static void vdiConvHeaderEndianessV1(VDIECONV enmConv, PVDIHEADER1 pHdrConv,
131 PVDIHEADER1 pHdr)
132{
133 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
134 pHdrConv->cbHeader = SET_ENDIAN_U32(enmConv, pHdr->cbHeader);
135 pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
136 pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
137 pHdrConv->offBlocks = SET_ENDIAN_U32(enmConv, pHdr->offBlocks);
138 pHdrConv->offData = SET_ENDIAN_U32(enmConv, pHdr->offData);
139 vdiConvGeometryEndianess(enmConv, &pHdrConv->LegacyGeometry, &pHdr->LegacyGeometry);
140 pHdrConv->u32Dummy = SET_ENDIAN_U32(enmConv, pHdr->u32Dummy);
141 pHdrConv->cbDisk = SET_ENDIAN_U64(enmConv, pHdr->cbDisk);
142 pHdrConv->cbBlock = SET_ENDIAN_U32(enmConv, pHdr->cbBlock);
143 pHdrConv->cbBlockExtra = SET_ENDIAN_U32(enmConv, pHdr->cbBlockExtra);
144 pHdrConv->cBlocks = SET_ENDIAN_U32(enmConv, pHdr->cBlocks);
145 pHdrConv->cBlocksAllocated = SET_ENDIAN_U32(enmConv, pHdr->cBlocksAllocated);
146 /* Don't convert the RTUUID fields. */
147 pHdrConv->uuidCreate = pHdr->uuidCreate;
148 pHdrConv->uuidModify = pHdr->uuidModify;
149 pHdrConv->uuidLinkage = pHdr->uuidLinkage;
150 pHdrConv->uuidParentModify = pHdr->uuidParentModify;
151}
152
153/**
154 * Internal: Set the Header - version 1plus fields to the appropriate endianess.
155 * @param enmConv Direction of the conversion.
156 * @param pHdrConv Where to store the converted header.
157 * @param pHdr Version 1+ Header pointer.
158 */
159static void vdiConvHeaderEndianessV1p(VDIECONV enmConv, PVDIHEADER1PLUS pHdrConv,
160 PVDIHEADER1PLUS pHdr)
161{
162 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
163 pHdrConv->cbHeader = SET_ENDIAN_U32(enmConv, pHdr->cbHeader);
164 pHdrConv->u32Type = SET_ENDIAN_U32(enmConv, pHdr->u32Type);
165 pHdrConv->fFlags = SET_ENDIAN_U32(enmConv, pHdr->fFlags);
166 pHdrConv->offBlocks = SET_ENDIAN_U32(enmConv, pHdr->offBlocks);
167 pHdrConv->offData = SET_ENDIAN_U32(enmConv, pHdr->offData);
168 vdiConvGeometryEndianess(enmConv, &pHdrConv->LegacyGeometry, &pHdr->LegacyGeometry);
169 pHdrConv->u32Dummy = SET_ENDIAN_U32(enmConv, pHdr->u32Dummy);
170 pHdrConv->cbDisk = SET_ENDIAN_U64(enmConv, pHdr->cbDisk);
171 pHdrConv->cbBlock = SET_ENDIAN_U32(enmConv, pHdr->cbBlock);
172 pHdrConv->cbBlockExtra = SET_ENDIAN_U32(enmConv, pHdr->cbBlockExtra);
173 pHdrConv->cBlocks = SET_ENDIAN_U32(enmConv, pHdr->cBlocks);
174 pHdrConv->cBlocksAllocated = SET_ENDIAN_U32(enmConv, pHdr->cBlocksAllocated);
175 /* Don't convert the RTUUID fields. */
176 pHdrConv->uuidCreate = pHdr->uuidCreate;
177 pHdrConv->uuidModify = pHdr->uuidModify;
178 pHdrConv->uuidLinkage = pHdr->uuidLinkage;
179 pHdrConv->uuidParentModify = pHdr->uuidParentModify;
180 vdiConvGeometryEndianess(enmConv, &pHdrConv->LCHSGeometry, &pHdr->LCHSGeometry);
181}
182
183/**
184 * Internal: Set the appropriate endianess on all the Blocks pointed.
185 * @param enmConv Direction of the conversion.
186 * @param paBlocks Pointer to the block array.
187 * @param cEntries Number of entries in the block array.
188 *
189 * @note Unlike the other conversion functions this method does an in place conversion
190 * to avoid temporary memory allocations when writing the block array.
191 */
192static void vdiConvBlocksEndianess(VDIECONV enmConv, PVDIIMAGEBLOCKPOINTER paBlocks,
193 unsigned cEntries)
194{
195 for (unsigned i = 0; i < cEntries; i++)
196 paBlocks[i] = SET_ENDIAN_U32(enmConv, paBlocks[i]);
197}
198
199/**
200 * Internal: Flush the image file to disk.
201 */
202static void vdiFlushImage(PVDIIMAGEDESC pImage)
203{
204 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
205 {
206 /* Save header. */
207 int rc = vdiUpdateHeader(pImage);
208 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Rrc\n",
209 pImage->pszFilename, rc));
210 vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
211 }
212}
213
214/**
215 * Internal: Free all allocated space for representing an image, and optionally
216 * delete the image from disk.
217 */
218static int vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
219{
220 int rc = VINF_SUCCESS;
221
222 /* Freeing a never allocated image (e.g. because the open failed) is
223 * not signalled as an error. After all nothing bad happens. */
224 if (pImage)
225 {
226 if (pImage->pStorage)
227 {
228 /* No point updating the file that is deleted anyway. */
229 if (!fDelete)
230 vdiFlushImage(pImage);
231
232 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
233 pImage->pStorage = NULL;
234 }
235
236 if (pImage->paBlocks)
237 {
238 RTMemFree(pImage->paBlocks);
239 pImage->paBlocks = NULL;
240 }
241
242 if (pImage->paBlocksRev)
243 {
244 RTMemFree(pImage->paBlocksRev);
245 pImage->paBlocksRev = NULL;
246 }
247
248 if (fDelete && pImage->pszFilename)
249 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
250 }
251
252 LogFlowFunc(("returns %Rrc\n", rc));
253 return rc;
254}
255
256/**
257 * internal: return power of 2 or 0 if num error.
258 */
259static unsigned getPowerOfTwo(unsigned uNumber)
260{
261 if (uNumber == 0)
262 return 0;
263 unsigned uPower2 = 0;
264 while ((uNumber & 1) == 0)
265 {
266 uNumber >>= 1;
267 uPower2++;
268 }
269 return uNumber == 1 ? uPower2 : 0;
270}
271
272/**
273 * Internal: Init VDI preheader.
274 */
275static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
276{
277 pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
278 pPreHdr->u32Version = VDI_IMAGE_VERSION;
279 memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
280 strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo)-1);
281}
282
283/**
284 * Internal: check VDI preheader.
285 */
286static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
287{
288 if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
289 return VERR_VD_VDI_INVALID_HEADER;
290
291 if ( VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
292 && pPreHdr->u32Version != 0x00000002) /* old version. */
293 return VERR_VD_VDI_UNSUPPORTED_VERSION;
294
295 return VINF_SUCCESS;
296}
297
298/**
299 * Internal: translate VD image flags to VDI image type enum.
300 */
301static VDIIMAGETYPE vdiTranslateImageFlags2VDI(unsigned uImageFlags)
302{
303 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
304 return VDI_IMAGE_TYPE_FIXED;
305 else if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
306 return VDI_IMAGE_TYPE_DIFF;
307 else
308 return VDI_IMAGE_TYPE_NORMAL;
309}
310
311/**
312 * Internal: translate VDI image type enum to VD image type enum.
313 */
314static unsigned vdiTranslateVDI2ImageFlags(VDIIMAGETYPE enmType)
315{
316 switch (enmType)
317 {
318 case VDI_IMAGE_TYPE_NORMAL:
319 return VD_IMAGE_FLAGS_NONE;
320 case VDI_IMAGE_TYPE_FIXED:
321 return VD_IMAGE_FLAGS_FIXED;
322 case VDI_IMAGE_TYPE_DIFF:
323 return VD_IMAGE_FLAGS_DIFF;
324 default:
325 AssertMsgFailed(("invalid VDIIMAGETYPE enmType=%d\n", (int)enmType));
326 return VD_IMAGE_FLAGS_NONE;
327 }
328}
329
330/**
331 * Internal: Init VDI header. Always use latest header version.
332 * @param pHeader Assumes it was initially initialized to all zeros.
333 */
334static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
335 const char *pszComment, uint64_t cbDisk,
336 uint32_t cbBlock, uint32_t cbBlockExtra,
337 uint32_t cbDataAlign)
338{
339 pHeader->uVersion = VDI_IMAGE_VERSION;
340 pHeader->u.v1plus.cbHeader = sizeof(VDIHEADER1PLUS);
341 pHeader->u.v1plus.u32Type = (uint32_t)vdiTranslateImageFlags2VDI(uImageFlags);
342 pHeader->u.v1plus.fFlags = (uImageFlags & VD_VDI_IMAGE_FLAGS_ZERO_EXPAND) ? 1 : 0;
343#ifdef VBOX_STRICT
344 char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
345 Assert(!memcmp(pHeader->u.v1plus.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
346#endif
347 pHeader->u.v1plus.szComment[0] = '\0';
348 if (pszComment)
349 {
350 AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1plus.szComment),
351 ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
352 strncat(pHeader->u.v1plus.szComment, pszComment, sizeof(pHeader->u.v1plus.szComment)-1);
353 }
354
355 /* Mark the legacy geometry not-calculated. */
356 pHeader->u.v1plus.LegacyGeometry.cCylinders = 0;
357 pHeader->u.v1plus.LegacyGeometry.cHeads = 0;
358 pHeader->u.v1plus.LegacyGeometry.cSectors = 0;
359 pHeader->u.v1plus.LegacyGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
360 pHeader->u.v1plus.u32Dummy = 0; /* used to be the translation value */
361
362 pHeader->u.v1plus.cbDisk = cbDisk;
363 pHeader->u.v1plus.cbBlock = cbBlock;
364 pHeader->u.v1plus.cBlocks = (uint32_t)(cbDisk / cbBlock);
365 if (cbDisk % cbBlock)
366 pHeader->u.v1plus.cBlocks++;
367 pHeader->u.v1plus.cbBlockExtra = cbBlockExtra;
368 pHeader->u.v1plus.cBlocksAllocated = 0;
369
370 /* Init offsets. */
371 pHeader->u.v1plus.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1PLUS), cbDataAlign);
372 pHeader->u.v1plus.offData = RT_ALIGN_32(pHeader->u.v1plus.offBlocks + (pHeader->u.v1plus.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), cbDataAlign);
373
374 /* Init uuids. */
375#ifdef _MSC_VER
376# pragma warning(disable:4366) /* (harmless "misalignment") */
377#endif
378 RTUuidCreate(&pHeader->u.v1plus.uuidCreate);
379 RTUuidClear(&pHeader->u.v1plus.uuidModify);
380 RTUuidClear(&pHeader->u.v1plus.uuidLinkage);
381 RTUuidClear(&pHeader->u.v1plus.uuidParentModify);
382#ifdef _MSC_VER
383# pragma warning(default:4366)
384#endif
385
386 /* Mark LCHS geometry not-calculated. */
387 pHeader->u.v1plus.LCHSGeometry.cCylinders = 0;
388 pHeader->u.v1plus.LCHSGeometry.cHeads = 0;
389 pHeader->u.v1plus.LCHSGeometry.cSectors = 0;
390 pHeader->u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
391}
392
393/**
394 * Internal: Check VDI header.
395 */
396static int vdiValidateHeader(PVDIHEADER pHeader)
397{
398 /* Check version-dependent header parameters. */
399 switch (GET_MAJOR_HEADER_VERSION(pHeader))
400 {
401 case 0:
402 {
403 /* Old header version. */
404 break;
405 }
406 case 1:
407 {
408 /* Current header version. */
409
410 if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
411 {
412 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
413 pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
414 return VERR_VD_VDI_INVALID_HEADER;
415 }
416
417 if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
418 {
419 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
420 getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
421 return VERR_VD_VDI_INVALID_HEADER;
422 }
423
424 if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
425 {
426 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
427 getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
428 return VERR_VD_VDI_INVALID_HEADER;
429 }
430
431 break;
432 }
433 default:
434 /* Unsupported. */
435 return VERR_VD_VDI_UNSUPPORTED_VERSION;
436 }
437
438 /* Check common header parameters. */
439
440 bool fFailed = false;
441
442 if ( getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
443 || getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
444 {
445 LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
446 fFailed = true;
447 }
448
449 if (getImageFlags(pHeader) & ~VD_VDI_IMAGE_FLAGS_MASK)
450 {
451 LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
452 fFailed = true;
453 }
454
455 if ( getImageLCHSGeometry(pHeader)
456 && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
457 {
458 LogRel(("VDI: wrong sector size (%d != %d)\n",
459 (getImageLCHSGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
460 fFailed = true;
461 }
462
463 if ( getImageDiskSize(pHeader) == 0
464 || getImageBlockSize(pHeader) == 0
465 || getImageBlocks(pHeader) == 0
466 || getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
467 {
468 LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
469 getImageDiskSize(pHeader), getImageBlockSize(pHeader),
470 getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
471 fFailed = true;
472 }
473
474 if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
475 {
476 LogRel(("VDI: too many blocks allocated (%d > %d)\n"
477 " blocksize=%d disksize=%lld\n",
478 getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
479 getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
480 fFailed = true;
481 }
482
483 if ( getImageExtraBlockSize(pHeader) != 0
484 && getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
485 {
486 LogRel(("VDI: wrong extra size (%d, %d)\n",
487 getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
488 fFailed = true;
489 }
490
491 if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
492 {
493 LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
494 getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
495 fFailed = true;
496 }
497
498 if (RTUuidIsNull(getImageCreationUUID(pHeader)))
499 {
500 LogRel(("VDI: uuid of creator is 0\n"));
501 fFailed = true;
502 }
503
504 if (RTUuidIsNull(getImageModificationUUID(pHeader)))
505 {
506 LogRel(("VDI: uuid of modifier is 0\n"));
507 fFailed = true;
508 }
509
510 return fFailed ? VERR_VD_VDI_INVALID_HEADER : VINF_SUCCESS;
511}
512
513/**
514 * Internal: Set up VDIIMAGEDESC structure by image header.
515 */
516static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
517{
518 pImage->uImageFlags = getImageFlags(&pImage->Header);
519 pImage->uImageFlags |= vdiTranslateVDI2ImageFlags(getImageType(&pImage->Header));
520 pImage->offStartBlocks = getImageBlocksOffset(&pImage->Header);
521 pImage->offStartData = getImageDataOffset(&pImage->Header);
522 pImage->uBlockMask = getImageBlockSize(&pImage->Header) - 1;
523 pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
524 pImage->offStartBlockData = getImageExtraBlockSize(&pImage->Header);
525 pImage->cbTotalBlockData = pImage->offStartBlockData
526 + getImageBlockSize(&pImage->Header);
527}
528
529/**
530 * Internal: Create VDI image file.
531 */
532static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
533 unsigned uImageFlags, const char *pszComment,
534 PCVDGEOMETRY pPCHSGeometry,
535 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
536 unsigned uOpenFlags, PFNVDPROGRESS pfnProgress,
537 void *pvUser, unsigned uPercentStart,
538 unsigned uPercentSpan, PVDINTERFACECONFIG pIfCfg)
539{
540 int rc;
541 uint64_t cbTotal;
542 uint32_t cbDataAlign = VDI_DATA_ALIGN;
543
544 AssertPtr(pPCHSGeometry);
545 AssertPtr(pLCHSGeometry);
546
547 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
548 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
549 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
550
551 /* Special check for comment length. */
552 if ( VALID_PTR(pszComment)
553 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
554 {
555 rc = vdIfError(pImage->pIfError, VERR_VD_VDI_COMMENT_TOO_LONG, RT_SRC_POS,
556 N_("VDI: comment is too long for '%s'"), pImage->pszFilename);
557 goto out;
558 }
559
560 if (pIfCfg)
561 {
562 rc = VDCFGQueryU32Def(pIfCfg, "DataAlignment", &cbDataAlign, VDI_DATA_ALIGN);
563 if (RT_FAILURE(rc))
564 {
565 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
566 N_("VDI: Getting data alignment for '%s' failed (%Rrc)"), pImage->pszFilename, rc);
567 goto out;
568 }
569 }
570
571 vdiInitPreHeader(&pImage->PreHeader);
572 vdiInitHeader(&pImage->Header, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0,
573 cbDataAlign);
574 /* Save PCHS geometry. Not much work, and makes the flow of information
575 * quite a bit clearer - relying on the higher level isn't obvious. */
576 pImage->PCHSGeometry = *pPCHSGeometry;
577 /* Set LCHS geometry (legacy geometry is ignored for the current 1.1+). */
578 pImage->Header.u.v1plus.LCHSGeometry.cCylinders = pLCHSGeometry->cCylinders;
579 pImage->Header.u.v1plus.LCHSGeometry.cHeads = pLCHSGeometry->cHeads;
580 pImage->Header.u.v1plus.LCHSGeometry.cSectors = pLCHSGeometry->cSectors;
581 pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
582
583 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
584 if (!pImage->paBlocks)
585 {
586 rc = VERR_NO_MEMORY;
587 goto out;
588 }
589
590 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
591 {
592 /* for growing images mark all blocks in paBlocks as free. */
593 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
594 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
595 }
596 else
597 {
598 /* for fixed images mark all blocks in paBlocks as allocated */
599 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
600 pImage->paBlocks[i] = i;
601 pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
602 }
603
604 /* Setup image parameters. */
605 vdiSetupImageDesc(pImage);
606
607 /* Create image file. */
608 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
609 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
610 true /* fCreate */),
611 &pImage->pStorage);
612 if (RT_FAILURE(rc))
613 {
614 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: cannot create image '%s'"),
615 pImage->pszFilename);
616 goto out;
617 }
618
619 cbTotal = pImage->offStartData
620 + (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
621
622 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
623 {
624 /* Check the free space on the disk and leave early if there is not
625 * sufficient space available. */
626 int64_t cbFree = 0;
627 rc = vdIfIoIntFileGetFreeSpace(pImage->pIfIo, pImage->pszFilename, &cbFree);
628 if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbTotal))
629 {
630 rc = vdIfError(pImage->pIfError, VERR_DISK_FULL, RT_SRC_POS,
631 N_("VDI: disk would overflow creating image '%s'"), pImage->pszFilename);
632 goto out;
633 }
634 }
635
636 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
637 {
638 /*
639 * Allocate & commit whole file if fixed image, it must be more
640 * effective than expanding file by write operations.
641 */
642 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pImage->pStorage, cbTotal, 0 /* fFlags */,
643 pfnProgress, pvUser, uPercentStart, uPercentSpan);
644 pImage->cbImage = cbTotal;
645 }
646 else
647 {
648 /* Set file size to hold header and blocks array. */
649 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->offStartData);
650 pImage->cbImage = pImage->offStartData;
651 }
652 if (RT_FAILURE(rc))
653 {
654 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: setting image size failed for '%s'"),
655 pImage->pszFilename);
656 goto out;
657 }
658
659 /* Use specified image uuid */
660 *getImageCreationUUID(&pImage->Header) = *pUuid;
661
662 /* Generate image last-modify uuid */
663 RTUuidCreate(getImageModificationUUID(&pImage->Header));
664
665 /* Write pre-header. */
666 VDIPREHEADER PreHeader;
667 vdiConvPreHeaderEndianess(VDIECONV_H2F, &PreHeader, &pImage->PreHeader);
668 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
669 &PreHeader, sizeof(PreHeader));
670 if (RT_FAILURE(rc))
671 {
672 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"),
673 pImage->pszFilename);
674 goto out;
675 }
676
677 /* Write header. */
678 VDIHEADER1PLUS Hdr;
679 vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
680 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
681 &Hdr, sizeof(Hdr));
682 if (RT_FAILURE(rc))
683 {
684 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"),
685 pImage->pszFilename);
686 goto out;
687 }
688
689 vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, getImageBlocks(&pImage->Header));
690 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks, pImage->paBlocks,
691 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER));
692 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, getImageBlocks(&pImage->Header));
693 if (RT_FAILURE(rc))
694 {
695 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: writing block pointers failed for '%s'"),
696 pImage->pszFilename);
697 goto out;
698 }
699
700out:
701 if (RT_SUCCESS(rc) && pfnProgress)
702 pfnProgress(pvUser, uPercentStart + uPercentSpan);
703
704 if (RT_FAILURE(rc))
705 vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
706 return rc;
707}
708
709/**
710 * Internal: Open a VDI image.
711 */
712static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
713{
714 int rc;
715
716 pImage->uOpenFlags = uOpenFlags;
717
718 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
719 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
720 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
721
722 /*
723 * Open the image.
724 */
725 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
726 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
727 &pImage->pStorage);
728 if (RT_FAILURE(rc))
729 {
730 /* Do NOT signal an appropriate error here, as the VD layer has the
731 * choice of retrying the open if it failed. */
732 goto out;
733 }
734
735 /* Get file size. */
736 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage,
737 &pImage->cbImage);
738 if (RT_FAILURE(rc))
739 {
740 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error getting the image size in '%s'"), pImage->pszFilename);
741 rc = VERR_VD_VDI_INVALID_HEADER;
742 goto out;
743 }
744
745 /* Read pre-header. */
746 VDIPREHEADER PreHeader;
747 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
748 &PreHeader, sizeof(PreHeader));
749 if (RT_FAILURE(rc))
750 {
751 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
752 rc = VERR_VD_VDI_INVALID_HEADER;
753 goto out;
754 }
755 vdiConvPreHeaderEndianess(VDIECONV_F2H, &pImage->PreHeader, &PreHeader);
756 rc = vdiValidatePreHeader(&pImage->PreHeader);
757 if (RT_FAILURE(rc))
758 {
759 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: invalid pre-header in '%s'"), pImage->pszFilename);
760 goto out;
761 }
762
763 /* Read header. */
764 pImage->Header.uVersion = pImage->PreHeader.u32Version;
765 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
766 {
767 case 0:
768 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
769 &pImage->Header.u.v0, sizeof(pImage->Header.u.v0));
770 if (RT_FAILURE(rc))
771 {
772 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
773 goto out;
774 }
775 vdiConvHeaderEndianessV0(VDIECONV_F2H, &pImage->Header.u.v0, &pImage->Header.u.v0);
776 break;
777 case 1:
778 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
779 &pImage->Header.u.v1, sizeof(pImage->Header.u.v1));
780 if (RT_FAILURE(rc))
781 {
782 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
783 goto out;
784 }
785 vdiConvHeaderEndianessV1(VDIECONV_F2H, &pImage->Header.u.v1, &pImage->Header.u.v1);
786 /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write mode.
787 * Conversion is harmless, as any VirtualBox version supporting VDI
788 * 1.1 doesn't touch fields it doesn't know about. */
789 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
790 && GET_MINOR_HEADER_VERSION(&pImage->Header) == 1
791 && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
792 {
793 pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
794 /* Mark LCHS geometry not-calculated. */
795 pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
796 pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
797 pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
798 pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
799 }
800 else if (pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
801 {
802 /* Read the actual VDI 1.1+ header completely. */
803 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, sizeof(pImage->PreHeader),
804 &pImage->Header.u.v1plus,
805 sizeof(pImage->Header.u.v1plus));
806 if (RT_FAILURE(rc))
807 {
808 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
809 goto out;
810 }
811 vdiConvHeaderEndianessV1p(VDIECONV_F2H, &pImage->Header.u.v1plus, &pImage->Header.u.v1plus);
812 }
813 break;
814 default:
815 rc = vdIfError(pImage->pIfError, VERR_VD_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: unsupported major version %u in '%s'"), GET_MAJOR_HEADER_VERSION(&pImage->Header), pImage->pszFilename);
816 goto out;
817 }
818
819 rc = vdiValidateHeader(&pImage->Header);
820 if (RT_FAILURE(rc))
821 {
822 rc = vdIfError(pImage->pIfError, VERR_VD_VDI_INVALID_HEADER, RT_SRC_POS, N_("VDI: invalid header in '%s'"), pImage->pszFilename);
823 goto out;
824 }
825
826 /* Setup image parameters by header. */
827 vdiSetupImageDesc(pImage);
828
829 /* Allocate memory for blocks array. */
830 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
831 if (!pImage->paBlocks)
832 {
833 rc = VERR_NO_MEMORY;
834 goto out;
835 }
836
837 /* Read blocks array. */
838 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks, pImage->paBlocks,
839 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER));
840 if (RT_FAILURE(rc))
841 {
842 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: Error reading the block table in '%s'"), pImage->pszFilename);
843 goto out;
844 }
845 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, getImageBlocks(&pImage->Header));
846
847 if (uOpenFlags & VD_OPEN_FLAGS_DISCARD)
848 {
849 /*
850 * Create the back resolving table for discards.
851 * any error or inconsistency results in a fail because this might
852 * get us into trouble later on.
853 */
854 pImage->paBlocksRev = (unsigned *)RTMemAllocZ(sizeof(unsigned) * getImageBlocks(&pImage->Header));
855 if (pImage->paBlocksRev)
856 {
857 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
858 unsigned cBlocks = getImageBlocks(&pImage->Header);
859
860 for (unsigned i = 0; i < cBlocks; i++)
861 pImage->paBlocksRev[i] = VDI_IMAGE_BLOCK_FREE;
862
863 for (unsigned i = 0; i < cBlocks; i++)
864 {
865 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
866 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
867 {
868 if (ptrBlock < cBlocksAllocated)
869 {
870 if (pImage->paBlocksRev[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
871 pImage->paBlocksRev[ptrBlock] = i;
872 else
873 {
874 rc = VERR_VD_VDI_INVALID_HEADER;
875 break;
876 }
877 }
878 else
879 {
880 rc = VERR_VD_VDI_INVALID_HEADER;
881 break;
882 }
883 }
884 }
885 }
886 else
887 rc = VERR_NO_MEMORY;
888 }
889
890out:
891 if (RT_FAILURE(rc))
892 vdiFreeImage(pImage, false);
893 return rc;
894}
895
896/**
897 * Internal: Save header to file.
898 */
899static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
900{
901 int rc;
902 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
903 {
904 case 0:
905 {
906 VDIHEADER0 Hdr;
907 vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
908 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
909 &Hdr, sizeof(Hdr));
910 break;
911 }
912 case 1:
913 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
914 {
915 VDIHEADER1 Hdr;
916 vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
917 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
918 &Hdr, sizeof(Hdr));
919 }
920 else
921 {
922 VDIHEADER1PLUS Hdr;
923 vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
924 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
925 &Hdr, sizeof(Hdr));
926 }
927 break;
928 default:
929 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
930 break;
931 }
932 AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
933 return rc;
934}
935
936/**
937 * Internal: Save header to file - async version.
938 */
939static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
940{
941 int rc;
942 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
943 {
944 case 0:
945 {
946 VDIHEADER0 Hdr;
947 vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
948 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
949 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
950 pIoCtx, NULL, NULL);
951 break;
952 }
953 case 1:
954 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
955 {
956 VDIHEADER1 Hdr;
957 vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
958 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
959 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
960 pIoCtx, NULL, NULL);
961 }
962 else
963 {
964 VDIHEADER1PLUS Hdr;
965 vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
966 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
967 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
968 pIoCtx, NULL, NULL);
969 }
970 break;
971 default:
972 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
973 break;
974 }
975 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
976 ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
977 return rc;
978}
979
980/**
981 * Internal: Save block pointer to file, save header to file.
982 */
983static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
984{
985 /* Update image header. */
986 int rc = vdiUpdateHeader(pImage);
987 if (RT_SUCCESS(rc))
988 {
989 /* write only one block pointer. */
990 VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
991 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
992 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
993 &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER));
994 AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
995 uBlock, pImage->pszFilename, rc));
996 }
997 return rc;
998}
999
1000/**
1001 * Internal: Save block pointer to file, save header to file - async version.
1002 */
1003static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
1004 PVDIOCTX pIoCtx, bool fUpdateHdr)
1005{
1006 int rc = VINF_SUCCESS;
1007
1008 /* Update image header. */
1009 if (fUpdateHdr)
1010 rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
1011
1012 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1013 {
1014 /* write only one block pointer. */
1015 VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
1016 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1017 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
1018 &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER),
1019 pIoCtx, NULL, NULL);
1020 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
1021 ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
1022 uBlock, pImage->pszFilename, rc));
1023 }
1024 return rc;
1025}
1026
1027/**
1028 * Internal: Flush the image file to disk - async version.
1029 */
1030static int vdiFlushImageIoCtx(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
1031{
1032 int rc = VINF_SUCCESS;
1033
1034 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1035 {
1036 /* Save header. */
1037 rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
1038 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
1039 ("vdiUpdateHeaderAsync() failed, filename=\"%s\", rc=%Rrc\n",
1040 pImage->pszFilename, rc));
1041 rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
1042 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
1043 ("Flushing data to disk failed rc=%Rrc\n", rc));
1044 }
1045
1046 return rc;
1047}
1048
1049/**
1050 * Completion callback for meta/userdata reads or writes.
1051 *
1052 * @return VBox status code.
1053 * VINF_SUCCESS if everything was successful and the transfer can continue.
1054 * VERR_VD_ASYNC_IO_IN_PROGRESS if there is another data transfer pending.
1055 * @param pBackendData The opaque backend data.
1056 * @param pIoCtx I/O context associated with this request.
1057 * @param pvUser Opaque user data passed during a read/write request.
1058 * @param rcReq Status code for the completed request.
1059 */
1060static DECLCALLBACK(int) vdiDiscardBlockAsyncUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
1061{
1062 int rc = VINF_SUCCESS;
1063 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1064 PVDIBLOCKDISCARDASYNC pDiscardAsync = (PVDIBLOCKDISCARDASYNC)pvUser;
1065
1066 switch (pDiscardAsync->enmState)
1067 {
1068 case VDIBLOCKDISCARDSTATE_READ_BLOCK:
1069 {
1070 PVDMETAXFER pMetaXfer;
1071 uint64_t u64Offset = (uint64_t)pDiscardAsync->idxLastBlock * pImage->cbTotalBlockData + pImage->offStartData;
1072 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
1073 pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
1074 &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
1075 if (RT_FAILURE(rc))
1076 break;
1077
1078 /* Release immediately and go to next step. */
1079 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
1080 pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_WRITE_BLOCK;
1081 }
1082 case VDIBLOCKDISCARDSTATE_WRITE_BLOCK:
1083 {
1084 /* Block read complete. Write to the new location (discarded block). */
1085 uint64_t u64Offset = (uint64_t)pDiscardAsync->ptrBlockDiscard * pImage->cbTotalBlockData + pImage->offStartData;
1086 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
1087 pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
1088 vdiDiscardBlockAsyncUpdate, pDiscardAsync);
1089
1090 pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA;
1091 if (RT_FAILURE(rc))
1092 break;
1093 }
1094 case VDIBLOCKDISCARDSTATE_UPDATE_METADATA:
1095 {
1096 int rc2;
1097
1098 /* Block write complete. Update metadata. */
1099 pImage->paBlocksRev[pDiscardAsync->idxLastBlock] = VDI_IMAGE_BLOCK_FREE;
1100 pImage->paBlocks[pDiscardAsync->uBlock] = VDI_IMAGE_BLOCK_ZERO;
1101
1102 if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
1103 {
1104 pImage->paBlocks[pDiscardAsync->uBlockLast] = pDiscardAsync->ptrBlockDiscard;
1105 pImage->paBlocksRev[pDiscardAsync->ptrBlockDiscard] = pDiscardAsync->uBlockLast;
1106
1107 rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlockLast, pIoCtx, false /* fUpdateHdr */);
1108 if ( RT_FAILURE(rc)
1109 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1110 break;
1111 }
1112
1113 setImageBlocksAllocated(&pImage->Header, pDiscardAsync->idxLastBlock);
1114 rc = vdiUpdateBlockInfoAsync(pImage, pDiscardAsync->uBlock, pIoCtx, true /* fUpdateHdr */);
1115 if ( RT_FAILURE(rc)
1116 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1117 break;
1118
1119 pImage->cbImage -= pImage->cbTotalBlockData;
1120 LogFlowFunc(("Set new size %llu\n", pImage->cbImage));
1121 rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbImage);
1122 if (RT_FAILURE(rc2))
1123 rc = rc2;
1124
1125 /* Free discard state. */
1126 RTMemFree(pDiscardAsync->pvBlock);
1127 RTMemFree(pDiscardAsync);
1128 break;
1129 }
1130 default:
1131 AssertMsgFailed(("Invalid state %d\n", pDiscardAsync->enmState));
1132 }
1133
1134 if (rc == VERR_VD_NOT_ENOUGH_METADATA)
1135 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1136
1137 return rc;
1138}
1139
1140/**
1141 * Internal: Discard a whole block from the image filling the created hole with
1142 * data from another block - async I/O version.
1143 *
1144 * @returns VBox status code.
1145 * @param pImage VDI image instance data.
1146 * @param pIoCtx I/O context associated with this request.
1147 * @param uBlock The block to discard.
1148 * @param pvBlock Memory to use for the I/O.
1149 */
1150static int vdiDiscardBlockAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx,
1151 unsigned uBlock, void *pvBlock)
1152{
1153 int rc = VINF_SUCCESS;
1154 PVDIBLOCKDISCARDASYNC pDiscardAsync = NULL;
1155
1156 LogFlowFunc(("pImage=%#p uBlock=%u pvBlock=%#p\n",
1157 pImage, uBlock, pvBlock));
1158
1159 pDiscardAsync = (PVDIBLOCKDISCARDASYNC)RTMemAllocZ(sizeof(VDIBLOCKDISCARDASYNC));
1160 if (RT_UNLIKELY(!pDiscardAsync))
1161 return VERR_NO_MEMORY;
1162
1163 /* Init block discard state. */
1164 pDiscardAsync->uBlock = uBlock;
1165 pDiscardAsync->pvBlock = pvBlock;
1166 pDiscardAsync->ptrBlockDiscard = pImage->paBlocks[uBlock];
1167 pDiscardAsync->idxLastBlock = getImageBlocksAllocated(&pImage->Header) - 1;
1168 pDiscardAsync->uBlockLast = pImage->paBlocksRev[pDiscardAsync->idxLastBlock];
1169
1170 /*
1171 * The block is empty, remove it.
1172 * Read the last block of the image first.
1173 */
1174 if (pDiscardAsync->idxLastBlock != pDiscardAsync->ptrBlockDiscard)
1175 {
1176 LogFlowFunc(("Moving block [%u]=%u into [%u]=%u\n",
1177 pDiscardAsync->uBlockLast, pDiscardAsync->idxLastBlock,
1178 uBlock, pImage->paBlocks[uBlock]));
1179 pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_READ_BLOCK;
1180 }
1181 else
1182 {
1183 pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA; /* Start immediately to shrink the image. */
1184 LogFlowFunc(("Discard last block [%u]=%u\n", uBlock, pImage->paBlocks[uBlock]));
1185 }
1186
1187 /* Call the update callback directly. */
1188 rc = vdiDiscardBlockAsyncUpdate(pImage, pIoCtx, pDiscardAsync, VINF_SUCCESS);
1189
1190 LogFlowFunc(("returns rc=%Rrc\n", rc));
1191 return rc;
1192}
1193
1194/**
1195 * Internal: Creates a allocation bitmap from the given data.
1196 * Sectors which contain only 0 are marked as unallocated and sectors with
1197 * other data as allocated.
1198 *
1199 * @returns Pointer to the allocation bitmap or NULL on failure.
1200 * @param pvData The data to create the allocation bitmap for.
1201 * @param cbData Number of bytes in the buffer.
1202 */
1203static void *vdiAllocationBitmapCreate(void *pvData, size_t cbData)
1204{
1205 Assert(cbData <= UINT32_MAX / 8);
1206 uint32_t cSectors = (uint32_t)(cbData / 512);
1207 uint32_t uSectorCur = 0;
1208 void *pbmAllocationBitmap = NULL;
1209
1210 Assert(!(cbData % 512));
1211 Assert(!(cSectors % 8));
1212
1213 pbmAllocationBitmap = RTMemAllocZ(cSectors / 8);
1214 if (!pbmAllocationBitmap)
1215 return NULL;
1216
1217 while (uSectorCur < cSectors)
1218 {
1219 int idxSet = ASMBitFirstSet((uint8_t *)pvData + uSectorCur * 512, (uint32_t)cbData * 8);
1220
1221 if (idxSet != -1)
1222 {
1223 unsigned idxSectorAlloc = idxSet / 8 / 512;
1224 ASMBitSet(pbmAllocationBitmap, uSectorCur + idxSectorAlloc);
1225
1226 uSectorCur += idxSectorAlloc + 1;
1227 cbData -= (idxSectorAlloc + 1) * 512;
1228 }
1229 else
1230 break;
1231 }
1232
1233 return pbmAllocationBitmap;
1234}
1235
1236
1237/**
1238 * Updates the state of the async cluster allocation.
1239 *
1240 * @returns VBox status code.
1241 * @param pBackendData The opaque backend data.
1242 * @param pIoCtx I/O context associated with this request.
1243 * @param pvUser Opaque user data passed during a read/write request.
1244 * @param rcReq Status code for the completed request.
1245 */
1246static DECLCALLBACK(int) vdiBlockAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
1247{
1248 int rc = VINF_SUCCESS;
1249 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1250 PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)pvUser;
1251
1252 if (RT_SUCCESS(rcReq))
1253 {
1254 pImage->cbImage += pImage->cbTotalBlockData;
1255 pImage->paBlocks[pBlockAlloc->uBlock] = pBlockAlloc->cBlocksAllocated;
1256
1257 if (pImage->paBlocksRev)
1258 pImage->paBlocksRev[pBlockAlloc->cBlocksAllocated] = pBlockAlloc->uBlock;
1259
1260 setImageBlocksAllocated(&pImage->Header, pBlockAlloc->cBlocksAllocated + 1);
1261 rc = vdiUpdateBlockInfoAsync(pImage, pBlockAlloc->uBlock, pIoCtx,
1262 true /* fUpdateHdr */);
1263 }
1264 /* else: I/O error don't update the block table. */
1265
1266 RTMemFree(pBlockAlloc);
1267 return rc;
1268}
1269
1270/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1271static DECLCALLBACK(int) vdiCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1272 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1273{
1274 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
1275 int rc = VINF_SUCCESS;
1276 PVDIIMAGEDESC pImage;
1277
1278 if ( !VALID_PTR(pszFilename)
1279 || !*pszFilename)
1280 {
1281 rc = VERR_INVALID_PARAMETER;
1282 goto out;
1283 }
1284
1285 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
1286 if (!pImage)
1287 {
1288 rc = VERR_NO_MEMORY;
1289 goto out;
1290 }
1291 pImage->pszFilename = pszFilename;
1292 pImage->pStorage = NULL;
1293 pImage->paBlocks = NULL;
1294 pImage->pVDIfsDisk = pVDIfsDisk;
1295 pImage->pVDIfsImage = pVDIfsImage;
1296
1297 rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1298 vdiFreeImage(pImage, false);
1299 RTMemFree(pImage);
1300
1301 if (RT_SUCCESS(rc))
1302 *penmType = VDTYPE_HDD;
1303
1304out:
1305 LogFlowFunc(("returns %Rrc\n", rc));
1306 return rc;
1307}
1308
1309/** @copydoc VBOXHDDBACKEND::pfnOpen */
1310static DECLCALLBACK(int) vdiOpen(const char *pszFilename, unsigned uOpenFlags,
1311 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1312 VDTYPE enmType, void **ppBackendData)
1313{
1314 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1315 int rc;
1316 PVDIIMAGEDESC pImage;
1317
1318 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
1319
1320 /* Check open flags. All valid flags are supported. */
1321 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1322 {
1323 rc = VERR_INVALID_PARAMETER;
1324 goto out;
1325 }
1326
1327 /* Check remaining arguments. */
1328 if ( !VALID_PTR(pszFilename)
1329 || !*pszFilename)
1330 {
1331 rc = VERR_INVALID_PARAMETER;
1332 goto out;
1333 }
1334
1335 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
1336 if (!pImage)
1337 {
1338 rc = VERR_NO_MEMORY;
1339 goto out;
1340 }
1341 pImage->pszFilename = pszFilename;
1342 pImage->pStorage = NULL;
1343 pImage->paBlocks = NULL;
1344 pImage->pVDIfsDisk = pVDIfsDisk;
1345 pImage->pVDIfsImage = pVDIfsImage;
1346
1347 rc = vdiOpenImage(pImage, uOpenFlags);
1348 if (RT_SUCCESS(rc))
1349 *ppBackendData = pImage;
1350 else
1351 RTMemFree(pImage);
1352
1353out:
1354 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1355 return rc;
1356}
1357
1358/** @copydoc VBOXHDDBACKEND::pfnCreate */
1359static DECLCALLBACK(int) vdiCreate(const char *pszFilename, uint64_t cbSize,
1360 unsigned uImageFlags, const char *pszComment,
1361 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1362 PCRTUUID pUuid, unsigned uOpenFlags,
1363 unsigned uPercentStart, unsigned uPercentSpan,
1364 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1365 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1366 void **ppBackendData)
1367{
1368 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p\n",
1369 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1370 int rc;
1371 PVDIIMAGEDESC pImage;
1372
1373 PFNVDPROGRESS pfnProgress = NULL;
1374 void *pvUser = NULL;
1375 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1376 if (pIfProgress)
1377 {
1378 pfnProgress = pIfProgress->pfnProgress;
1379 pvUser = pIfProgress->Core.pvUser;
1380 }
1381
1382 PVDINTERFACECONFIG pIfCfg = VDIfConfigGet(pVDIfsOperation);
1383
1384 /* Check the image flags. */
1385 if ((uImageFlags & ~VD_VDI_IMAGE_FLAGS_MASK) != 0)
1386 {
1387 rc = VERR_VD_INVALID_TYPE;
1388 goto out;
1389 }
1390
1391 /* Check the VD container type. */
1392 if (enmType != VDTYPE_HDD)
1393 {
1394 rc = VERR_VD_INVALID_TYPE;
1395 goto out;
1396 }
1397
1398 /* Check open flags. All valid flags are supported. */
1399 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1400 {
1401 rc = VERR_INVALID_PARAMETER;
1402 goto out;
1403 }
1404
1405 /* Check size. Maximum 4PB-3M. No tricks with adjusting the 1M block size
1406 * so far, which would extend the size. */
1407 if ( !cbSize
1408 || cbSize >= _1P * 4 - _1M * 3)
1409 {
1410 rc = VERR_VD_INVALID_SIZE;
1411 goto out;
1412 }
1413
1414 /* Check remaining arguments. */
1415 if ( !VALID_PTR(pszFilename)
1416 || !*pszFilename
1417 || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
1418 || !VALID_PTR(pPCHSGeometry)
1419 || !VALID_PTR(pLCHSGeometry))
1420 {
1421 rc = VERR_INVALID_PARAMETER;
1422 goto out;
1423 }
1424
1425 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
1426 if (!pImage)
1427 {
1428 rc = VERR_NO_MEMORY;
1429 goto out;
1430 }
1431 pImage->pszFilename = pszFilename;
1432 pImage->pStorage = NULL;
1433 pImage->paBlocks = NULL;
1434 pImage->pVDIfsDisk = pVDIfsDisk;
1435 pImage->pVDIfsImage = pVDIfsImage;
1436
1437 rc = vdiCreateImage(pImage, cbSize, uImageFlags, pszComment,
1438 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1439 pfnProgress, pvUser, uPercentStart, uPercentSpan,
1440 pIfCfg);
1441 if (RT_SUCCESS(rc))
1442 {
1443 /* So far the image is opened in read/write mode. Make sure the
1444 * image is opened in read-only mode if the caller requested that. */
1445 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1446 {
1447 vdiFreeImage(pImage, false);
1448 rc = vdiOpenImage(pImage, uOpenFlags);
1449 if (RT_FAILURE(rc))
1450 {
1451 RTMemFree(pImage);
1452 goto out;
1453 }
1454 }
1455 *ppBackendData = pImage;
1456 }
1457 else
1458 RTMemFree(pImage);
1459
1460out:
1461 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1462 return rc;
1463}
1464
1465/** @copydoc VBOXHDDBACKEND::pfnRename */
1466static DECLCALLBACK(int) vdiRename(void *pBackendData, const char *pszFilename)
1467{
1468 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1469
1470 int rc = VINF_SUCCESS;
1471 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1472
1473 /* Check arguments. */
1474 if ( !pImage
1475 || !pszFilename
1476 || !*pszFilename)
1477 {
1478 rc = VERR_INVALID_PARAMETER;
1479 goto out;
1480 }
1481
1482 /* Close the image. */
1483 rc = vdiFreeImage(pImage, false);
1484 if (RT_SUCCESS(rc))
1485 {
1486 /* Rename the file. */
1487 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1488 if (RT_FAILURE(rc))
1489 {
1490 /* The move failed, try to reopen the original image. */
1491 int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
1492 if (RT_FAILURE(rc2))
1493 rc = rc2;
1494
1495 goto out;
1496 }
1497
1498 /* Update pImage with the new information. */
1499 pImage->pszFilename = pszFilename;
1500
1501 /* Open the new image. */
1502 rc = vdiOpenImage(pImage, pImage->uOpenFlags);
1503 if (RT_FAILURE(rc))
1504 goto out;
1505 }
1506
1507out:
1508 LogFlowFunc(("returns %Rrc\n", rc));
1509 return rc;
1510}
1511
1512/** @copydoc VBOXHDDBACKEND::pfnClose */
1513static DECLCALLBACK(int) vdiClose(void *pBackendData, bool fDelete)
1514{
1515 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1516 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1517 int rc;
1518
1519 rc = vdiFreeImage(pImage, fDelete);
1520 RTMemFree(pImage);
1521
1522 LogFlowFunc(("returns %Rrc\n", rc));
1523 return rc;
1524}
1525
1526static DECLCALLBACK(int) vdiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1527 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1528{
1529 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1530 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1531 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1532 unsigned uBlock;
1533 unsigned offRead;
1534 int rc;
1535
1536 AssertPtr(pImage);
1537 Assert(!(uOffset % 512));
1538 Assert(!(cbToRead % 512));
1539
1540 if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
1541 || !VALID_PTR(pIoCtx)
1542 || !cbToRead)
1543 {
1544 rc = VERR_INVALID_PARAMETER;
1545 goto out;
1546 }
1547
1548 /* Calculate starting block number and offset inside it. */
1549 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
1550 offRead = (unsigned)uOffset & pImage->uBlockMask;
1551
1552 /* Clip read range to at most the rest of the block. */
1553 cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
1554 Assert(!(cbToRead % 512));
1555
1556 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1557 rc = VERR_VD_BLOCK_FREE;
1558 else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1559 {
1560 size_t cbSet;
1561
1562 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
1563 Assert(cbSet == cbToRead);
1564
1565 rc = VINF_SUCCESS;
1566 }
1567 else
1568 {
1569 /* Block present in image file, read relevant data. */
1570 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
1571 + (pImage->offStartData + pImage->offStartBlockData + offRead);
1572
1573 if (u64Offset + cbToRead <= pImage->cbImage)
1574 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, u64Offset,
1575 pIoCtx, cbToRead);
1576 else
1577 {
1578 LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
1579 u64Offset, pImage->pszFilename, pImage->cbImage));
1580 vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
1581 rc = VERR_VD_READ_OUT_OF_RANGE;
1582 }
1583 }
1584
1585 if (pcbActuallyRead)
1586 *pcbActuallyRead = cbToRead;
1587
1588out:
1589 LogFlowFunc(("returns %Rrc\n", rc));
1590 return rc;
1591}
1592
1593static DECLCALLBACK(int) vdiWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1594 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1595 size_t *pcbPostRead, unsigned fWrite)
1596{
1597 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1598 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1599 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1600 unsigned uBlock;
1601 unsigned offWrite;
1602 int rc = VINF_SUCCESS;
1603
1604 AssertPtr(pImage);
1605 Assert(!(uOffset % 512));
1606 Assert(!(cbToWrite % 512));
1607
1608 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1609 {
1610 rc = VERR_VD_IMAGE_READ_ONLY;
1611 goto out;
1612 }
1613
1614 if (!VALID_PTR(pIoCtx) || !cbToWrite)
1615 {
1616 rc = VERR_INVALID_PARAMETER;
1617 goto out;
1618 }
1619
1620 /* No size check here, will do that later. For dynamic images which are
1621 * not multiples of the block size in length, this would prevent writing to
1622 * the last block. */
1623
1624 /* Calculate starting block number and offset inside it. */
1625 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
1626 offWrite = (unsigned)uOffset & pImage->uBlockMask;
1627
1628 /* Clip write range to at most the rest of the block. */
1629 cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
1630 Assert(!(cbToWrite % 512));
1631
1632 do
1633 {
1634 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1635 {
1636 /* Block is either free or zero. */
1637 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
1638 && ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1639 || cbToWrite == getImageBlockSize(&pImage->Header)))
1640 {
1641 /* If the destination block is unallocated at this point, it's
1642 * either a zero block or a block which hasn't been used so far
1643 * (which also means that it's a zero block. Don't need to write
1644 * anything to this block if the data consists of just zeroes. */
1645 if (vdIfIoIntIoCtxIsZero(pImage->pIfIo, pIoCtx, cbToWrite, true))
1646 {
1647 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1648 *pcbPreRead = 0;
1649 *pcbPostRead = 0;
1650 break;
1651 }
1652 }
1653
1654 if ( cbToWrite == getImageBlockSize(&pImage->Header)
1655 && !(fWrite & VD_WRITE_NO_ALLOC))
1656 {
1657 /* Full block write to previously unallocated block.
1658 * Allocate block and write data. */
1659 Assert(!offWrite);
1660 PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)RTMemAllocZ(sizeof(VDIASYNCBLOCKALLOC));
1661 if (!pBlockAlloc)
1662 {
1663 rc = VERR_NO_MEMORY;
1664 break;
1665 }
1666
1667 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1668 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
1669 + (pImage->offStartData + pImage->offStartBlockData);
1670
1671 pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
1672 pBlockAlloc->uBlock = uBlock;
1673
1674 *pcbPreRead = 0;
1675 *pcbPostRead = 0;
1676
1677 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1678 u64Offset, pIoCtx, cbToWrite,
1679 vdiBlockAllocUpdate, pBlockAlloc);
1680 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1681 break;
1682 else if (RT_FAILURE(rc))
1683 {
1684 RTMemFree(pBlockAlloc);
1685 break;
1686 }
1687
1688 rc = vdiBlockAllocUpdate(pImage, pIoCtx, pBlockAlloc, rc);
1689 }
1690 else
1691 {
1692 /* Trying to do a partial write to an unallocated block. Don't do
1693 * anything except letting the upper layer know what to do. */
1694 *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
1695 *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
1696 rc = VERR_VD_BLOCK_FREE;
1697 }
1698 }
1699 else
1700 {
1701 /* Block present in image file, write relevant data. */
1702 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
1703 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
1704 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1705 u64Offset, pIoCtx, cbToWrite, NULL, NULL);
1706 }
1707 } while (0);
1708
1709 if (pcbWriteProcess)
1710 *pcbWriteProcess = cbToWrite;
1711
1712out:
1713 LogFlowFunc(("returns %Rrc\n", rc));
1714 return rc;
1715}
1716
1717static DECLCALLBACK(int) vdiFlush(void *pBackendData, PVDIOCTX pIoCtx)
1718{
1719 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1720 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1721 int rc = VINF_SUCCESS;
1722
1723 Assert(pImage);
1724
1725 rc = vdiFlushImageIoCtx(pImage, pIoCtx);
1726 LogFlowFunc(("returns %Rrc\n", rc));
1727 return rc;
1728}
1729
1730/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1731static DECLCALLBACK(unsigned) vdiGetVersion(void *pBackendData)
1732{
1733 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1734 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1735 unsigned uVersion;
1736
1737 AssertPtr(pImage);
1738
1739 if (pImage)
1740 uVersion = pImage->PreHeader.u32Version;
1741 else
1742 uVersion = 0;
1743
1744 LogFlowFunc(("returns %#x\n", uVersion));
1745 return uVersion;
1746}
1747
1748/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
1749static DECLCALLBACK(uint32_t) vdiGetSectorSize(void *pBackendData)
1750{
1751 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1752 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1753 uint64_t cbSector = 0;
1754
1755 AssertPtr(pImage);
1756
1757 if (pImage && pImage->pStorage)
1758 cbSector = 512;
1759
1760 LogFlowFunc(("returns %zu\n", cbSector));
1761 return cbSector;
1762}
1763
1764/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1765static DECLCALLBACK(uint64_t) vdiGetSize(void *pBackendData)
1766{
1767 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1768 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1769 uint64_t cbSize;
1770
1771 AssertPtr(pImage);
1772
1773 if (pImage)
1774 cbSize = getImageDiskSize(&pImage->Header);
1775 else
1776 cbSize = 0;
1777
1778 LogFlowFunc(("returns %llu\n", cbSize));
1779 return cbSize;
1780}
1781
1782/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1783static DECLCALLBACK(uint64_t) vdiGetFileSize(void *pBackendData)
1784{
1785 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1786 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1787 uint64_t cb = 0;
1788
1789 AssertPtr(pImage);
1790
1791 if (pImage)
1792 {
1793 uint64_t cbFile;
1794 if (pImage->pStorage)
1795 {
1796 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1797 if (RT_SUCCESS(rc))
1798 cb += cbFile;
1799 }
1800 }
1801
1802 LogFlowFunc(("returns %lld\n", cb));
1803 return cb;
1804}
1805
1806/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1807static DECLCALLBACK(int) vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1808{
1809 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1810 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1811 int rc;
1812
1813 AssertPtr(pImage);
1814
1815 if (pImage)
1816 {
1817 if (pImage->PCHSGeometry.cCylinders)
1818 {
1819 *pPCHSGeometry = pImage->PCHSGeometry;
1820 rc = VINF_SUCCESS;
1821 }
1822 else
1823 rc = VERR_VD_GEOMETRY_NOT_SET;
1824 }
1825 else
1826 rc = VERR_VD_NOT_OPENED;
1827
1828 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1829 return rc;
1830}
1831
1832/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1833static DECLCALLBACK(int) vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1834{
1835 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1836 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1837 int rc;
1838
1839 AssertPtr(pImage);
1840
1841 if (pImage)
1842 {
1843 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1844 {
1845 rc = VERR_VD_IMAGE_READ_ONLY;
1846 goto out;
1847 }
1848
1849 pImage->PCHSGeometry = *pPCHSGeometry;
1850 rc = VINF_SUCCESS;
1851 }
1852 else
1853 rc = VERR_VD_NOT_OPENED;
1854
1855out:
1856 LogFlowFunc(("returns %Rrc\n", rc));
1857 return rc;
1858}
1859
1860/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1861static DECLCALLBACK(int) vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1862{
1863 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1864 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1865 int rc;
1866
1867 AssertPtr(pImage);
1868
1869 if (pImage)
1870 {
1871 VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
1872 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
1873 if (!pGeometry)
1874 pGeometry = &DummyGeo;
1875
1876 if ( pGeometry->cCylinders > 0
1877 && pGeometry->cHeads > 0
1878 && pGeometry->cSectors > 0)
1879 {
1880 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
1881 pLCHSGeometry->cHeads = pGeometry->cHeads;
1882 pLCHSGeometry->cSectors = pGeometry->cSectors;
1883 rc = VINF_SUCCESS;
1884 }
1885 else
1886 rc = VERR_VD_GEOMETRY_NOT_SET;
1887 }
1888 else
1889 rc = VERR_VD_NOT_OPENED;
1890
1891 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1892 return rc;
1893}
1894
1895/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1896static DECLCALLBACK(int) vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1897{
1898 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1899 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1900 PVDIDISKGEOMETRY pGeometry;
1901 int rc;
1902
1903 AssertPtr(pImage);
1904
1905 if (pImage)
1906 {
1907 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1908 {
1909 rc = VERR_VD_IMAGE_READ_ONLY;
1910 goto out;
1911 }
1912
1913 pGeometry = getImageLCHSGeometry(&pImage->Header);
1914 if (pGeometry)
1915 {
1916 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
1917 pGeometry->cHeads = pLCHSGeometry->cHeads;
1918 pGeometry->cSectors = pLCHSGeometry->cSectors;
1919 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
1920
1921 /* Update header information in base image file. */
1922 vdiFlushImage(pImage);
1923 }
1924 rc = VINF_SUCCESS;
1925 }
1926 else
1927 rc = VERR_VD_NOT_OPENED;
1928
1929out:
1930 LogFlowFunc(("returns %Rrc\n", rc));
1931 return rc;
1932}
1933
1934/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1935static DECLCALLBACK(unsigned) vdiGetImageFlags(void *pBackendData)
1936{
1937 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1938 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1939 unsigned uImageFlags;
1940
1941 AssertPtr(pImage);
1942
1943 if (pImage)
1944 uImageFlags = pImage->uImageFlags;
1945 else
1946 uImageFlags = 0;
1947
1948 LogFlowFunc(("returns %#x\n", uImageFlags));
1949 return uImageFlags;
1950}
1951
1952/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1953static DECLCALLBACK(unsigned) vdiGetOpenFlags(void *pBackendData)
1954{
1955 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1956 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1957 unsigned uOpenFlags;
1958
1959 AssertPtr(pImage);
1960
1961 if (pImage)
1962 uOpenFlags = pImage->uOpenFlags;
1963 else
1964 uOpenFlags = 0;
1965
1966 LogFlowFunc(("returns %#x\n", uOpenFlags));
1967 return uOpenFlags;
1968}
1969
1970/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1971static DECLCALLBACK(int) vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1972{
1973 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
1974 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1975 int rc;
1976 const char *pszFilename;
1977
1978 /* Image must be opened and the new flags must be valid. */
1979 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1980 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1981 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD
1982 | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1983 {
1984 rc = VERR_INVALID_PARAMETER;
1985 goto out;
1986 }
1987
1988 /* Implement this operation via reopening the image. */
1989 pszFilename = pImage->pszFilename;
1990 rc = vdiFreeImage(pImage, false);
1991 if (RT_FAILURE(rc))
1992 goto out;
1993 rc = vdiOpenImage(pImage, uOpenFlags);
1994
1995out:
1996 LogFlowFunc(("returns %Rrc\n", rc));
1997 return rc;
1998}
1999
2000/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2001static DECLCALLBACK(int) vdiGetComment(void *pBackendData, char *pszComment,
2002 size_t cbComment)
2003{
2004 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2005 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2006 int rc = VINF_SUCCESS;
2007
2008 AssertPtr(pImage);
2009
2010 if (pImage)
2011 {
2012 char *pszTmp = getImageComment(&pImage->Header);
2013 /* Make this foolproof even if the image doesn't have the zero
2014 * termination. With some luck the repaired header will be saved. */
2015 size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
2016 if (cb == VDI_IMAGE_COMMENT_SIZE)
2017 {
2018 pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
2019 cb--;
2020 }
2021 if (cb < cbComment)
2022 {
2023 /* memcpy is much better than strncpy. */
2024 memcpy(pszComment, pszTmp, cb + 1);
2025 }
2026 else
2027 rc = VERR_BUFFER_OVERFLOW;
2028 }
2029 else
2030 rc = VERR_VD_NOT_OPENED;
2031
2032 LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
2033 return rc;
2034}
2035
2036/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2037static DECLCALLBACK(int) vdiSetComment(void *pBackendData, const char *pszComment)
2038{
2039 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2040 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2041 int rc;
2042
2043 AssertPtr(pImage);
2044
2045 if (pImage)
2046 {
2047 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2048 rc = VERR_VD_IMAGE_READ_ONLY;
2049 else
2050 {
2051 size_t cchComment = pszComment ? strlen(pszComment) : 0;
2052 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
2053 {
2054 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
2055 rc = VERR_VD_VDI_COMMENT_TOO_LONG;
2056 goto out;
2057 }
2058
2059 /* we don't support old style images */
2060 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2061 {
2062 /*
2063 * Update the comment field, making sure to zero out all of the previous comment.
2064 */
2065 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
2066 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
2067
2068 /* write out new the header */
2069 rc = vdiUpdateHeader(pImage);
2070 }
2071 else
2072 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2073 }
2074 }
2075 else
2076 rc = VERR_VD_NOT_OPENED;
2077
2078out:
2079 LogFlowFunc(("returns %Rrc\n", rc));
2080 return rc;
2081}
2082
2083/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2084static DECLCALLBACK(int) vdiGetUuid(void *pBackendData, PRTUUID pUuid)
2085{
2086 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2087 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2088 int rc;
2089
2090 AssertPtr(pImage);
2091
2092 if (pImage)
2093 {
2094 *pUuid = *getImageCreationUUID(&pImage->Header);
2095 rc = VINF_SUCCESS;
2096 }
2097 else
2098 rc = VERR_VD_NOT_OPENED;
2099
2100 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2101 return rc;
2102}
2103
2104/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2105static DECLCALLBACK(int) vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
2106{
2107 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2108 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2109 int rc = VINF_SUCCESS;
2110
2111 AssertPtr(pImage);
2112
2113 if (pImage)
2114 {
2115 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2116 {
2117 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2118 pImage->Header.u.v1.uuidCreate = *pUuid;
2119 /* Make it possible to clone old VDIs. */
2120 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2121 pImage->Header.u.v0.uuidCreate = *pUuid;
2122 else
2123 {
2124 LogFunc(("Version is not supported!\n"));
2125 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2126 }
2127 }
2128 else
2129 rc = VERR_VD_IMAGE_READ_ONLY;
2130 }
2131 else
2132 rc = VERR_VD_NOT_OPENED;
2133
2134 LogFlowFunc(("returns %Rrc\n", rc));
2135 return rc;
2136}
2137
2138/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2139static DECLCALLBACK(int) vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2140{
2141 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2142 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2143 int rc;
2144
2145 AssertPtr(pImage);
2146
2147 if (pImage)
2148 {
2149 *pUuid = *getImageModificationUUID(&pImage->Header);
2150 rc = VINF_SUCCESS;
2151 }
2152 else
2153 rc = VERR_VD_NOT_OPENED;
2154
2155 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2156 return rc;
2157}
2158
2159/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2160static DECLCALLBACK(int) vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2161{
2162 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2163 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2164 int rc = VINF_SUCCESS;
2165
2166 AssertPtr(pImage);
2167
2168 if (pImage)
2169 {
2170 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2171 {
2172 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2173 pImage->Header.u.v1.uuidModify = *pUuid;
2174 /* Make it possible to clone old VDIs. */
2175 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2176 pImage->Header.u.v0.uuidModify = *pUuid;
2177 else
2178 {
2179 LogFunc(("Version is not supported!\n"));
2180 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2181 }
2182 }
2183 else
2184 rc = VERR_VD_IMAGE_READ_ONLY;
2185 }
2186 else
2187 rc = VERR_VD_NOT_OPENED;
2188
2189 LogFlowFunc(("returns %Rrc\n", rc));
2190 return rc;
2191}
2192
2193/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2194static DECLCALLBACK(int) vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
2195{
2196 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2197 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2198 int rc;
2199
2200 AssertPtr(pImage);
2201
2202 if (pImage)
2203 {
2204 *pUuid = *getImageParentUUID(&pImage->Header);
2205 rc = VINF_SUCCESS;
2206 }
2207 else
2208 rc = VERR_VD_NOT_OPENED;
2209
2210 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2211 return rc;
2212}
2213
2214/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2215static DECLCALLBACK(int) vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2216{
2217 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2218 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2219 int rc = VINF_SUCCESS;
2220
2221 AssertPtr(pImage);
2222
2223 if (pImage)
2224 {
2225 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2226 {
2227 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2228 pImage->Header.u.v1.uuidLinkage = *pUuid;
2229 /* Make it possible to clone old VDIs. */
2230 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2231 pImage->Header.u.v0.uuidLinkage = *pUuid;
2232 else
2233 {
2234 LogFunc(("Version is not supported!\n"));
2235 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2236 }
2237 }
2238 else
2239 rc = VERR_VD_IMAGE_READ_ONLY;
2240 }
2241 else
2242 rc = VERR_VD_NOT_OPENED;
2243
2244 LogFlowFunc(("returns %Rrc\n", rc));
2245 return rc;
2246}
2247
2248/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2249static DECLCALLBACK(int) vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2250{
2251 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2252 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2253 int rc;
2254
2255 AssertPtr(pImage);
2256
2257 if (pImage)
2258 {
2259 *pUuid = *getImageParentModificationUUID(&pImage->Header);
2260 rc = VINF_SUCCESS;
2261 }
2262 else
2263 rc = VERR_VD_NOT_OPENED;
2264
2265 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2266 return rc;
2267}
2268
2269/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2270static DECLCALLBACK(int) vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2271{
2272 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2273 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2274 int rc = VINF_SUCCESS;
2275
2276 AssertPtr(pImage);
2277
2278 if (pImage)
2279 {
2280 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2281 {
2282 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2283 pImage->Header.u.v1.uuidParentModify = *pUuid;
2284 else
2285 {
2286 LogFunc(("Version is not supported!\n"));
2287 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2288 }
2289 }
2290 else
2291 rc = VERR_VD_IMAGE_READ_ONLY;
2292 }
2293 else
2294 rc = VERR_VD_NOT_OPENED;
2295
2296 LogFlowFunc(("returns %Rrc\n", rc));
2297 return rc;
2298}
2299
2300/** @copydoc VBOXHDDBACKEND::pfnDump */
2301static DECLCALLBACK(void) vdiDump(void *pBackendData)
2302{
2303 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2304
2305 vdIfErrorMessage(pImage->pIfError, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
2306 pImage->pszFilename,
2307 (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
2308 pImage->uOpenFlags,
2309 pImage->pStorage);
2310 vdIfErrorMessage(pImage->pIfError, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
2311 pImage->PreHeader.u32Version,
2312 getImageType(&pImage->Header),
2313 getImageFlags(&pImage->Header),
2314 getImageDiskSize(&pImage->Header));
2315 vdIfErrorMessage(pImage->pIfError, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
2316 getImageBlockSize(&pImage->Header),
2317 getImageExtraBlockSize(&pImage->Header),
2318 getImageBlocks(&pImage->Header),
2319 getImageBlocksAllocated(&pImage->Header));
2320 vdIfErrorMessage(pImage->pIfError, "Header: offBlocks=%u offData=%u\n",
2321 getImageBlocksOffset(&pImage->Header),
2322 getImageDataOffset(&pImage->Header));
2323 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
2324 if (pg)
2325 vdIfErrorMessage(pImage->pIfError, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
2326 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
2327 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
2328 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
2329 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
2330 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
2331 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
2332 vdIfErrorMessage(pImage->pIfError, "Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
2333 pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
2334 vdIfErrorMessage(pImage->pIfError, "Image: uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
2335 pImage->uBlockMask,
2336 pImage->cbTotalBlockData,
2337 pImage->uShiftOffset2Index,
2338 pImage->offStartBlockData);
2339
2340 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
2341 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
2342 {
2343 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2344 {
2345 cBlocksNotFree++;
2346 if (pImage->paBlocks[uBlock] >= cBlocks)
2347 cBadBlocks++;
2348 }
2349 }
2350 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
2351 {
2352 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
2353 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
2354 }
2355 if (cBadBlocks)
2356 {
2357 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u bad blocks found !!\n",
2358 cBadBlocks);
2359 }
2360}
2361
2362/** @copydoc VBOXHDDBACKEND::pfnCompact */
2363static DECLCALLBACK(int) vdiCompact(void *pBackendData, unsigned uPercentStart,
2364 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2365 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2366{
2367 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2368 int rc = VINF_SUCCESS;
2369 void *pvBuf = NULL, *pvTmp = NULL;
2370 unsigned *paBlocks2 = NULL;
2371
2372 DECLCALLBACKMEMBER(int, pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2373 void *pvParent = NULL;
2374 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2375 if (pIfParentState)
2376 {
2377 pfnParentRead = pIfParentState->pfnParentRead;
2378 pvParent = pIfParentState->Core.pvUser;
2379 }
2380
2381 PFNVDPROGRESS pfnProgress = NULL;
2382 void *pvUser = NULL;
2383 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2384
2385 PVDINTERFACEQUERYRANGEUSE pIfQueryRangeUse = VDIfQueryRangeUseGet(pVDIfsOperation);
2386
2387 do {
2388 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2389
2390 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2391 rc = VERR_VD_IMAGE_READ_ONLY);
2392
2393 unsigned cBlocks;
2394 unsigned cBlocksToMove = 0;
2395 size_t cbBlock;
2396 cBlocks = getImageBlocks(&pImage->Header);
2397 cbBlock = getImageBlockSize(&pImage->Header);
2398 if (pfnParentRead)
2399 {
2400 pvBuf = RTMemTmpAlloc(cbBlock);
2401 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
2402 }
2403 pvTmp = RTMemTmpAlloc(cbBlock);
2404 AssertBreakStmt(pvTmp, rc = VERR_NO_MEMORY);
2405
2406 uint64_t cbFile;
2407 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2408 AssertRCBreak(rc);
2409 unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
2410 if (cBlocksAllocated == 0)
2411 {
2412 /* No data blocks in this image, no need to compact. */
2413 rc = VINF_SUCCESS;
2414 break;
2415 }
2416
2417 /* Allocate block array for back resolving. */
2418 paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
2419 AssertBreakStmt(paBlocks2, rc = VERR_NO_MEMORY);
2420 /* Fill out back resolving, check/fix allocation errors before
2421 * compacting the image, just to be on the safe side. Update the
2422 * image contents straight away, as this enables cancelling. */
2423 for (unsigned i = 0; i < cBlocksAllocated; i++)
2424 paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
2425 rc = VINF_SUCCESS;
2426 for (unsigned i = 0; i < cBlocks; i++)
2427 {
2428 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2429 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2430 {
2431 if (ptrBlock < cBlocksAllocated)
2432 {
2433 if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
2434 paBlocks2[ptrBlock] = i;
2435 else
2436 {
2437 LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
2438 i, pImage->pszFilename));
2439 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2440 rc = vdiUpdateBlockInfo(pImage, i);
2441 if (RT_FAILURE(rc))
2442 break;
2443 }
2444 }
2445 else
2446 {
2447 LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
2448 i, pImage->pszFilename));
2449 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2450 rc = vdiUpdateBlockInfo(pImage, i);
2451 if (RT_FAILURE(rc))
2452 break;
2453 }
2454 }
2455 }
2456 if (RT_FAILURE(rc))
2457 break;
2458
2459 /* Find redundant information and update the block pointers
2460 * accordingly, creating bubbles. Keep disk up to date, as this
2461 * enables cancelling. */
2462 for (unsigned i = 0; i < cBlocks; i++)
2463 {
2464 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2465 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2466 {
2467 /* Block present in image file, read relevant data. */
2468 uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
2469 + (pImage->offStartData + pImage->offStartBlockData);
2470 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock);
2471 if (RT_FAILURE(rc))
2472 break;
2473
2474 if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
2475 {
2476 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2477 rc = vdiUpdateBlockInfo(pImage, i);
2478 if (RT_FAILURE(rc))
2479 break;
2480 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2481 /* Adjust progress info, one block to be relocated. */
2482 cBlocksToMove++;
2483 }
2484 else if (pfnParentRead)
2485 {
2486 rc = pfnParentRead(pvParent, (uint64_t)i * cbBlock, pvBuf, cbBlock);
2487 if (RT_FAILURE(rc))
2488 break;
2489 if (!memcmp(pvTmp, pvBuf, cbBlock))
2490 {
2491 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2492 rc = vdiUpdateBlockInfo(pImage, i);
2493 if (RT_FAILURE(rc))
2494 break;
2495 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2496 /* Adjust progress info, one block to be relocated. */
2497 cBlocksToMove++;
2498 }
2499 }
2500 }
2501
2502 /* Check if the range is in use if the block is still allocated. */
2503 ptrBlock = pImage->paBlocks[i];
2504 if ( IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock)
2505 && pIfQueryRangeUse)
2506 {
2507 bool fUsed = true;
2508
2509 rc = vdIfQueryRangeUse(pIfQueryRangeUse, (uint64_t)i * cbBlock, cbBlock, &fUsed);
2510 if (RT_FAILURE(rc))
2511 break;
2512 if (!fUsed)
2513 {
2514 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2515 rc = vdiUpdateBlockInfo(pImage, i);
2516 if (RT_FAILURE(rc))
2517 break;
2518 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2519 /* Adjust progress info, one block to be relocated. */
2520 cBlocksToMove++;
2521 }
2522 }
2523
2524 if (pIfProgress && pIfProgress->pfnProgress)
2525 {
2526 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2527 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2528 if (RT_FAILURE(rc))
2529 break;
2530 }
2531 }
2532 if (RT_FAILURE(rc))
2533 break;
2534
2535 /* Fill bubbles with other data (if available). */
2536 unsigned cBlocksMoved = 0;
2537 unsigned uBlockUsedPos = cBlocksAllocated;
2538 for (unsigned i = 0; i < cBlocksAllocated; i++)
2539 {
2540 unsigned uBlock = paBlocks2[i];
2541 if (uBlock == VDI_IMAGE_BLOCK_FREE)
2542 {
2543 unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
2544 while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
2545 {
2546 uBlockUsedPos--;
2547 uBlockData = paBlocks2[uBlockUsedPos];
2548 }
2549 /* Terminate early if there is no block which needs copying. */
2550 if (uBlockUsedPos == i)
2551 break;
2552 uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2553 + (pImage->offStartData + pImage->offStartBlockData);
2554 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2555 pvTmp, cbBlock);
2556 u64Offset = (uint64_t)i * pImage->cbTotalBlockData
2557 + (pImage->offStartData + pImage->offStartBlockData);
2558 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2559 pvTmp, cbBlock);
2560 pImage->paBlocks[uBlockData] = i;
2561 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
2562 rc = vdiUpdateBlockInfo(pImage, uBlockData);
2563 if (RT_FAILURE(rc))
2564 break;
2565 paBlocks2[i] = uBlockData;
2566 paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
2567 cBlocksMoved++;
2568 }
2569
2570 if (pIfProgress && pIfProgress->pfnProgress)
2571 {
2572 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2573 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2574
2575 if (RT_FAILURE(rc))
2576 break;
2577 }
2578 }
2579 if (RT_FAILURE(rc))
2580 break;
2581
2582 /* Update image header. */
2583 setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
2584 vdiUpdateHeader(pImage);
2585
2586 /* Truncate the image to the proper size to finish compacting. */
2587 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2588 (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2589 + pImage->offStartData + pImage->offStartBlockData);
2590 } while (0);
2591
2592 if (paBlocks2)
2593 RTMemTmpFree(paBlocks2);
2594 if (pvTmp)
2595 RTMemTmpFree(pvTmp);
2596 if (pvBuf)
2597 RTMemTmpFree(pvBuf);
2598
2599 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2600 {
2601 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2602 uPercentStart + uPercentSpan);
2603 }
2604
2605 LogFlowFunc(("returns %Rrc\n", rc));
2606 return rc;
2607}
2608
2609
2610/** @copydoc VBOXHDDBACKEND::pfnResize */
2611static DECLCALLBACK(int) vdiResize(void *pBackendData, uint64_t cbSize,
2612 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2613 unsigned uPercentStart, unsigned uPercentSpan,
2614 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2615 PVDINTERFACE pVDIfsOperation)
2616{
2617 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2618 int rc = VINF_SUCCESS;
2619
2620 PFNVDPROGRESS pfnProgress = NULL;
2621 void *pvUser = NULL;
2622 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2623
2624 /*
2625 * Making the image smaller is not supported at the moment.
2626 * Resizing is also not supported for fixed size images and
2627 * very old images.
2628 */
2629 /** @todo implement making the image smaller, it is the responsibility of
2630 * the user to know what he's doing. */
2631 if ( cbSize < getImageDiskSize(&pImage->Header)
2632 || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2633 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2634 rc = VERR_NOT_SUPPORTED;
2635 else if (cbSize > getImageDiskSize(&pImage->Header))
2636 {
2637 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
2638 uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header); /** < New number of blocks in the image after the resize */
2639 if (cbSize % getImageBlockSize(&pImage->Header))
2640 cBlocksNew++;
2641
2642 uint32_t cBlocksOld = getImageBlocks(&pImage->Header); /** < Number of blocks before the resize. */
2643 uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
2644 uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
2645
2646 if ( pImage->offStartData < offStartDataNew
2647 && cBlocksAllocated > 0)
2648 {
2649 /* Calculate how many sectors need to be relocated. */
2650 uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
2651 unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
2652 if (cbOverlapping % getImageBlockSize(&pImage->Header))
2653 cBlocksReloc++;
2654
2655 /* Since only full blocks can be relocated the new data start is
2656 * determined by moving it block by block. */
2657 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2658 offStartDataNew = pImage->offStartData;
2659
2660 /* Do the relocation. */
2661 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2662
2663 /*
2664 * Get the blocks we need to relocate first, they are appended to the end
2665 * of the image.
2666 */
2667 void *pvBuf = NULL, *pvZero = NULL;
2668 do
2669 {
2670 /* Allocate data buffer. */
2671 pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
2672 if (!pvBuf)
2673 {
2674 rc = VERR_NO_MEMORY;
2675 break;
2676 }
2677
2678 /* Allocate buffer for overwriting with zeroes. */
2679 pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
2680 if (!pvZero)
2681 {
2682 rc = VERR_NO_MEMORY;
2683 break;
2684 }
2685
2686 for (unsigned i = 0; i < cBlocksReloc; i++)
2687 {
2688 /* Search the index in the block table. */
2689 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2690 {
2691 if (!pImage->paBlocks[idxBlock])
2692 {
2693 /* Read data and append to the end of the image. */
2694 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2695 offStartDataNew, pvBuf,
2696 pImage->cbTotalBlockData);
2697 if (RT_FAILURE(rc))
2698 break;
2699
2700 uint64_t offBlockAppend;
2701 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &offBlockAppend);
2702 if (RT_FAILURE(rc))
2703 break;
2704
2705 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2706 offBlockAppend, pvBuf,
2707 pImage->cbTotalBlockData);
2708 if (RT_FAILURE(rc))
2709 break;
2710
2711 /* Zero out the old block area. */
2712 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2713 offStartDataNew, pvZero,
2714 pImage->cbTotalBlockData);
2715 if (RT_FAILURE(rc))
2716 break;
2717
2718 /* Update block counter. */
2719 pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
2720
2721 /*
2722 * Decrease the block number of all other entries in the array.
2723 * They were moved one block to the front.
2724 * Doing it as a separate step iterating over the array again
2725 * because an error while relocating the block might end up
2726 * in a corrupted image otherwise.
2727 */
2728 for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
2729 {
2730 if ( idxBlock2 != idxBlock
2731 && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
2732 pImage->paBlocks[idxBlock2]--;
2733 }
2734
2735 /* Continue with the next block. */
2736 break;
2737 }
2738 }
2739
2740 if (RT_FAILURE(rc))
2741 break;
2742
2743 offStartDataNew += pImage->cbTotalBlockData;
2744 }
2745 } while (0);
2746
2747 if (pvBuf)
2748 RTMemFree(pvBuf);
2749 if (pvZero)
2750 RTMemFree(pvZero);
2751 }
2752
2753 /*
2754 * We need to update the new offsets for the image data in the out of memory
2755 * case too because we relocated the blocks already.
2756 */
2757 pImage->offStartData = offStartDataNew;
2758 setImageDataOffset(&pImage->Header, offStartDataNew);
2759
2760 /*
2761 * Relocation done, expand the block array and update the header with
2762 * the new data.
2763 */
2764 if (RT_SUCCESS(rc))
2765 {
2766 PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
2767 if (paBlocksNew)
2768 {
2769 pImage->paBlocks = paBlocksNew;
2770
2771 /* Mark the new blocks as unallocated. */
2772 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2773 pImage->paBlocks[idxBlock] = VDI_IMAGE_BLOCK_FREE;
2774 }
2775 else
2776 rc = VERR_NO_MEMORY;
2777
2778 /* Write the block array before updating the rest. */
2779 vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, cBlocksNew);
2780 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks,
2781 pImage->paBlocks, cbBlockspaceNew);
2782 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, cBlocksNew);
2783
2784 if (RT_SUCCESS(rc))
2785 {
2786 /* Update size and new block count. */
2787 setImageDiskSize(&pImage->Header, cbSize);
2788 setImageBlocks(&pImage->Header, cBlocksNew);
2789 /* Update geometry. */
2790 pImage->PCHSGeometry = *pPCHSGeometry;
2791 pImage->cbImage = cbSize;
2792
2793 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
2794 if (pGeometry)
2795 {
2796 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
2797 pGeometry->cHeads = pLCHSGeometry->cHeads;
2798 pGeometry->cSectors = pLCHSGeometry->cSectors;
2799 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
2800 }
2801 }
2802 }
2803
2804 /* Update header information in base image file. */
2805 vdiFlushImage(pImage);
2806 }
2807 /* Same size doesn't change the image at all. */
2808
2809 LogFlowFunc(("returns %Rrc\n", rc));
2810 return rc;
2811}
2812
2813/** @copydoc VBOXHDDBACKEND::pfnDiscard */
2814static DECLCALLBACK(int) vdiDiscard(void *pBackendData, PVDIOCTX pIoCtx,
2815 uint64_t uOffset, size_t cbDiscard,
2816 size_t *pcbPreAllocated,
2817 size_t *pcbPostAllocated,
2818 size_t *pcbActuallyDiscarded,
2819 void **ppbmAllocationBitmap,
2820 unsigned fDiscard)
2821{
2822 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2823 unsigned uBlock;
2824 unsigned offDiscard;
2825 int rc = VINF_SUCCESS;
2826 void *pvBlock = NULL;
2827
2828 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
2829 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
2830
2831 AssertPtr(pImage);
2832 Assert(!(uOffset % 512));
2833 Assert(!(cbDiscard % 512));
2834
2835 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2836 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
2837 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
2838 && cbDiscard,
2839 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
2840 uOffset, cbDiscard),
2841 VERR_INVALID_PARAMETER);
2842
2843 do
2844 {
2845 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2846 ("Image is opened readonly\n"),
2847 rc = VERR_VD_IMAGE_READ_ONLY);
2848
2849 AssertMsgBreakStmt(cbDiscard,
2850 ("cbDiscard=%u\n", cbDiscard),
2851 rc = VERR_INVALID_PARAMETER);
2852
2853 /* Calculate starting block number and offset inside it. */
2854 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2855 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
2856
2857 /* Clip range to at most the rest of the block. */
2858 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
2859 Assert(!(cbDiscard % 512));
2860
2861 if (pcbPreAllocated)
2862 *pcbPreAllocated = 0;
2863
2864 if (pcbPostAllocated)
2865 *pcbPostAllocated = 0;
2866
2867 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2868 {
2869 uint8_t *pbBlockData;
2870 size_t cbPreAllocated, cbPostAllocated;
2871
2872 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
2873 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
2874
2875 /* Read the block data. */
2876 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
2877 if (!pvBlock)
2878 {
2879 rc = VERR_NO_MEMORY;
2880 break;
2881 }
2882
2883 if (!cbPreAllocated && !cbPostAllocated)
2884 {
2885 /*
2886 * Discarding a whole block, don't check for allocated sectors.
2887 * It is possible to just remove the whole block which avoids
2888 * one read and checking the whole block for data.
2889 */
2890 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
2891 }
2892 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
2893 {
2894 /* Just zero out the given range. */
2895 memset(pvBlock, 0, cbDiscard);
2896
2897 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
2898 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
2899 u64Offset, pvBlock, cbDiscard, pIoCtx,
2900 NULL, NULL);
2901 RTMemFree(pvBlock);
2902 }
2903 else
2904 {
2905 /*
2906 * Read complete block as metadata, the I/O context has no memory buffer
2907 * and we need to access the content directly anyway.
2908 */
2909 PVDMETAXFER pMetaXfer;
2910 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
2911
2912 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
2913 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
2914 pbBlockData, pImage->cbTotalBlockData,
2915 pIoCtx, &pMetaXfer, NULL, NULL);
2916 if (RT_FAILURE(rc))
2917 {
2918 RTMemFree(pvBlock);
2919 break;
2920 }
2921
2922 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
2923
2924 /* Clear data. */
2925 memset(pbBlockData + offDiscard , 0, cbDiscard);
2926
2927 Assert(!(cbDiscard % 4));
2928 Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
2929 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
2930 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
2931 else
2932 {
2933 /* Block has data, create allocation bitmap. */
2934 *pcbPreAllocated = cbPreAllocated;
2935 *pcbPostAllocated = cbPostAllocated;
2936 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
2937 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
2938 rc = VERR_NO_MEMORY;
2939 else
2940 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
2941
2942 RTMemFree(pvBlock);
2943 }
2944 } /* if: no complete block discarded */
2945 } /* if: Block is allocated. */
2946 /* else: nothing to do. */
2947 } while (0);
2948
2949 if (pcbActuallyDiscarded)
2950 *pcbActuallyDiscarded = cbDiscard;
2951
2952 LogFlowFunc(("returns %Rrc\n", rc));
2953 return rc;
2954}
2955
2956/** @copydoc VBOXHDDBACKEND::pfnRepair */
2957static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
2958 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
2959{
2960 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
2961 int rc;
2962 PVDINTERFACEERROR pIfError;
2963 PVDINTERFACEIOINT pIfIo;
2964 PVDIOSTORAGE pStorage;
2965 uint64_t cbFile;
2966 PVDIIMAGEBLOCKPOINTER paBlocks = NULL;
2967 uint32_t *pu32BlockBitmap = NULL;
2968 VDIPREHEADER PreHdr;
2969 VDIHEADER Hdr;
2970
2971 pIfIo = VDIfIoIntGet(pVDIfsImage);
2972 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
2973
2974 pIfError = VDIfErrorGet(pVDIfsDisk);
2975
2976 do
2977 {
2978 bool fRepairHdr = false;
2979 bool fRepairBlockArray = false;
2980
2981 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
2982 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
2983 ? VD_OPEN_FLAGS_READONLY
2984 : 0,
2985 false /* fCreate */),
2986 &pStorage);
2987 if (RT_FAILURE(rc))
2988 {
2989 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to open image \"%s\"", pszFilename);
2990 break;
2991 }
2992
2993 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
2994 if (RT_FAILURE(rc))
2995 {
2996 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to query image size");
2997 break;
2998 }
2999
3000 /* Read pre-header. */
3001 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr));
3002 if (RT_FAILURE(rc))
3003 {
3004 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: Error reading pre-header in '%s'"), pszFilename);
3005 break;
3006 }
3007 vdiConvPreHeaderEndianess(VDIECONV_F2H, &PreHdr, &PreHdr);
3008 rc = vdiValidatePreHeader(&PreHdr);
3009 if (RT_FAILURE(rc))
3010 {
3011 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3012 N_("VDI: invalid pre-header in '%s'"), pszFilename);
3013 break;
3014 }
3015
3016 /* Read header. */
3017 Hdr.uVersion = PreHdr.u32Version;
3018 switch (GET_MAJOR_HEADER_VERSION(&Hdr))
3019 {
3020 case 0:
3021 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3022 &Hdr.u.v0, sizeof(Hdr.u.v0));
3023 if (RT_FAILURE(rc))
3024 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"),
3025 pszFilename);
3026 vdiConvHeaderEndianessV0(VDIECONV_F2H, &Hdr.u.v0, &Hdr.u.v0);
3027 break;
3028 case 1:
3029 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3030 &Hdr.u.v1, sizeof(Hdr.u.v1));
3031 if (RT_FAILURE(rc))
3032 {
3033 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"),
3034 pszFilename);
3035 }
3036 vdiConvHeaderEndianessV1(VDIECONV_F2H, &Hdr.u.v1, &Hdr.u.v1);
3037 if (Hdr.u.v1.cbHeader >= sizeof(Hdr.u.v1plus))
3038 {
3039 /* Read the VDI 1.1+ header completely. */
3040 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3041 &Hdr.u.v1plus, sizeof(Hdr.u.v1plus));
3042 if (RT_FAILURE(rc))
3043 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"),
3044 pszFilename);
3045 vdiConvHeaderEndianessV1p(VDIECONV_F2H, &Hdr.u.v1plus, &Hdr.u.v1plus);
3046 }
3047 break;
3048 default:
3049 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3050 N_("VDI: unsupported major version %u in '%s'"),
3051 GET_MAJOR_HEADER_VERSION(&Hdr), pszFilename);
3052 break;
3053 }
3054
3055 if (RT_SUCCESS(rc))
3056 {
3057 rc = vdiValidateHeader(&Hdr);
3058 if (RT_FAILURE(rc))
3059 {
3060 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3061 N_("VDI: invalid header in '%s'"), pszFilename);
3062 break;
3063 }
3064 }
3065
3066 /* Setup image parameters by header. */
3067 uint64_t offStartBlocks, offStartData;
3068 size_t cbTotalBlockData;
3069
3070 offStartBlocks = getImageBlocksOffset(&Hdr);
3071 offStartData = getImageDataOffset(&Hdr);
3072 cbTotalBlockData = getImageExtraBlockSize(&Hdr) + getImageBlockSize(&Hdr);
3073
3074 /* Allocate memory for blocks array. */
3075 paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&Hdr));
3076 if (!paBlocks)
3077 {
3078 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3079 "Failed to allocate memory for block array");
3080 break;
3081 }
3082
3083 /* Read blocks array. */
3084 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3085 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
3086 if (RT_FAILURE(rc))
3087 {
3088 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3089 "Failed to read block array (at %llu), %Rrc",
3090 offStartBlocks, rc);
3091 break;
3092 }
3093 vdiConvBlocksEndianess(VDIECONV_F2H, paBlocks, getImageBlocks(&Hdr));
3094
3095 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(getImageBlocks(&Hdr) / 8, 4));
3096 if (!pu32BlockBitmap)
3097 {
3098 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3099 "Failed to allocate memory for block bitmap");
3100 break;
3101 }
3102
3103 for (uint32_t i = 0; i < getImageBlocks(&Hdr); i++)
3104 {
3105 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(paBlocks[i]))
3106 {
3107 uint64_t offBlock = (uint64_t)paBlocks[i] * cbTotalBlockData
3108 + offStartData;
3109
3110 /*
3111 * Check that the offsets are valid (inside of the image) and
3112 * that there are no double references.
3113 */
3114 if (offBlock + cbTotalBlockData > cbFile)
3115 {
3116 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
3117 i, offBlock);
3118 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3119 fRepairBlockArray = true;
3120 }
3121 else if (ASMBitTestAndSet(pu32BlockBitmap, paBlocks[i]))
3122 {
3123 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
3124 i);
3125 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3126 fRepairBlockArray = true;
3127 }
3128 }
3129 }
3130
3131 /* Write repaired structures now. */
3132 if (!fRepairBlockArray)
3133 vdIfErrorMessage(pIfError, "VDI image is in a consistent state, no repair required\n");
3134 else if (!(fFlags & VD_REPAIR_DRY_RUN))
3135 {
3136 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
3137
3138 vdiConvBlocksEndianess(VDIECONV_H2F, paBlocks, getImageBlocks(&Hdr));
3139 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3140 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
3141 if (RT_FAILURE(rc))
3142 {
3143 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3144 "Could not write repaired block allocation table (at %llu), %Rrc",
3145 offStartBlocks, rc);
3146 break;
3147 }
3148 }
3149
3150 vdIfErrorMessage(pIfError, "Corrupted VDI image repaired successfully\n");
3151 } while(0);
3152
3153 if (paBlocks)
3154 RTMemFree(paBlocks);
3155
3156 if (pu32BlockBitmap)
3157 RTMemFree(pu32BlockBitmap);
3158
3159 if (pStorage)
3160 {
3161 int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
3162 if (RT_SUCCESS(rc))
3163 rc = rc2; /* Propagate error code only if repairing was successful. */
3164 }
3165
3166 LogFlowFunc(("returns %Rrc\n", rc));
3167 return rc;
3168}
3169
3170const VBOXHDDBACKEND g_VDIBackend =
3171{
3172 /* pszBackendName */
3173 "VDI",
3174 /* cbSize */
3175 sizeof(VBOXHDDBACKEND),
3176 /* uBackendCaps */
3177 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
3178 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_DISCARD
3179 | VD_CAP_PREFERRED,
3180 /* paFileExtensions */
3181 s_aVdiFileExtensions,
3182 /* paConfigInfo */
3183 NULL,
3184 /* pfnCheckIfValid */
3185 vdiCheckIfValid,
3186 /* pfnOpen */
3187 vdiOpen,
3188 /* pfnCreate */
3189 vdiCreate,
3190 /* pfnRename */
3191 vdiRename,
3192 /* pfnClose */
3193 vdiClose,
3194 /* pfnRead */
3195 vdiRead,
3196 /* pfnWrite */
3197 vdiWrite,
3198 /* pfnFlush */
3199 vdiFlush,
3200 /* pfnDiscard */
3201 vdiDiscard,
3202 /* pfnGetVersion */
3203 vdiGetVersion,
3204 /* pfnGetSectorSize */
3205 vdiGetSectorSize,
3206 /* pfnGetSize */
3207 vdiGetSize,
3208 /* pfnGetFileSize */
3209 vdiGetFileSize,
3210 /* pfnGetPCHSGeometry */
3211 vdiGetPCHSGeometry,
3212 /* pfnSetPCHSGeometry */
3213 vdiSetPCHSGeometry,
3214 /* pfnGetLCHSGeometry */
3215 vdiGetLCHSGeometry,
3216 /* pfnSetLCHSGeometry */
3217 vdiSetLCHSGeometry,
3218 /* pfnGetImageFlags */
3219 vdiGetImageFlags,
3220 /* pfnGetOpenFlags */
3221 vdiGetOpenFlags,
3222 /* pfnSetOpenFlags */
3223 vdiSetOpenFlags,
3224 /* pfnGetComment */
3225 vdiGetComment,
3226 /* pfnSetComment */
3227 vdiSetComment,
3228 /* pfnGetUuid */
3229 vdiGetUuid,
3230 /* pfnSetUuid */
3231 vdiSetUuid,
3232 /* pfnGetModificationUuid */
3233 vdiGetModificationUuid,
3234 /* pfnSetModificationUuid */
3235 vdiSetModificationUuid,
3236 /* pfnGetParentUuid */
3237 vdiGetParentUuid,
3238 /* pfnSetParentUuid */
3239 vdiSetParentUuid,
3240 /* pfnGetParentModificationUuid */
3241 vdiGetParentModificationUuid,
3242 /* pfnSetParentModificationUuid */
3243 vdiSetParentModificationUuid,
3244 /* pfnDump */
3245 vdiDump,
3246 /* pfnGetTimestamp */
3247 NULL,
3248 /* pfnGetParentTimestamp */
3249 NULL,
3250 /* pfnSetParentTimestamp */
3251 NULL,
3252 /* pfnGetParentFilename */
3253 NULL,
3254 /* pfnSetParentFilename */
3255 NULL,
3256 /* pfnComposeLocation */
3257 genericFileComposeLocation,
3258 /* pfnComposeName */
3259 genericFileComposeName,
3260 /* pfnCompact */
3261 vdiCompact,
3262 /* pfnResize */
3263 vdiResize,
3264 /* pfnRepair */
3265 vdiRepair,
3266 /* pfnTraverseMetadata */
3267 NULL
3268};
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