VirtualBox

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

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

Storage: warnings.

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