VirtualBox

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

Last change on this file since 61600 was 60608, checked in by vboxsync, 9 years ago

Main+Storage/VD: add a capability which marks preferred medium format backends

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