VirtualBox

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

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

Storage/VD: Add proper versioning of the backend structures instead of just relying on the structure size to make changing callback signatures possible in the future and still being able to reject incompatible plugins

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