VirtualBox

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

Last change on this file since 48846 was 48743, checked in by vboxsync, 11 years ago

Storage/VD: Add support for different sector sizes (only opening and reading and writing images, not creating them with a sector size other than 512 bytes)

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