VirtualBox

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

Last change on this file since 46712 was 46613, checked in by vboxsync, 12 years ago

Storage: Propagate errors when closing a file but free everything nevertheless (see @bugref{6791})

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 118.5 KB
Line 
1/* $Id: VDI.cpp 46613 2013-06-18 10:27:13Z 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::pfnGetSize */
1774static uint64_t vdiGetSize(void *pBackendData)
1775{
1776 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1777 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1778 uint64_t cbSize;
1779
1780 AssertPtr(pImage);
1781
1782 if (pImage)
1783 cbSize = getImageDiskSize(&pImage->Header);
1784 else
1785 cbSize = 0;
1786
1787 LogFlowFunc(("returns %llu\n", cbSize));
1788 return cbSize;
1789}
1790
1791/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1792static uint64_t vdiGetFileSize(void *pBackendData)
1793{
1794 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1795 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1796 uint64_t cb = 0;
1797
1798 AssertPtr(pImage);
1799
1800 if (pImage)
1801 {
1802 uint64_t cbFile;
1803 if (pImage->pStorage)
1804 {
1805 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1806 if (RT_SUCCESS(rc))
1807 cb += cbFile;
1808 }
1809 }
1810
1811 LogFlowFunc(("returns %lld\n", cb));
1812 return cb;
1813}
1814
1815/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1816static int vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1817{
1818 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1819 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1820 int rc;
1821
1822 AssertPtr(pImage);
1823
1824 if (pImage)
1825 {
1826 if (pImage->PCHSGeometry.cCylinders)
1827 {
1828 *pPCHSGeometry = pImage->PCHSGeometry;
1829 rc = VINF_SUCCESS;
1830 }
1831 else
1832 rc = VERR_VD_GEOMETRY_NOT_SET;
1833 }
1834 else
1835 rc = VERR_VD_NOT_OPENED;
1836
1837 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1838 return rc;
1839}
1840
1841/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1842static int vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1843{
1844 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1845 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1846 int rc;
1847
1848 AssertPtr(pImage);
1849
1850 if (pImage)
1851 {
1852 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1853 {
1854 rc = VERR_VD_IMAGE_READ_ONLY;
1855 goto out;
1856 }
1857
1858 pImage->PCHSGeometry = *pPCHSGeometry;
1859 rc = VINF_SUCCESS;
1860 }
1861 else
1862 rc = VERR_VD_NOT_OPENED;
1863
1864out:
1865 LogFlowFunc(("returns %Rrc\n", rc));
1866 return rc;
1867}
1868
1869/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1870static int vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1871{
1872 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1873 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1874 int rc;
1875
1876 AssertPtr(pImage);
1877
1878 if (pImage)
1879 {
1880 VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
1881 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
1882 if (!pGeometry)
1883 pGeometry = &DummyGeo;
1884
1885 if ( pGeometry->cCylinders > 0
1886 && pGeometry->cHeads > 0
1887 && pGeometry->cSectors > 0)
1888 {
1889 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
1890 pLCHSGeometry->cHeads = pGeometry->cHeads;
1891 pLCHSGeometry->cSectors = pGeometry->cSectors;
1892 rc = VINF_SUCCESS;
1893 }
1894 else
1895 rc = VERR_VD_GEOMETRY_NOT_SET;
1896 }
1897 else
1898 rc = VERR_VD_NOT_OPENED;
1899
1900 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1901 return rc;
1902}
1903
1904/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1905static int vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1906{
1907 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1908 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1909 PVDIDISKGEOMETRY pGeometry;
1910 int rc;
1911
1912 AssertPtr(pImage);
1913
1914 if (pImage)
1915 {
1916 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1917 {
1918 rc = VERR_VD_IMAGE_READ_ONLY;
1919 goto out;
1920 }
1921
1922 pGeometry = getImageLCHSGeometry(&pImage->Header);
1923 if (pGeometry)
1924 {
1925 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
1926 pGeometry->cHeads = pLCHSGeometry->cHeads;
1927 pGeometry->cSectors = pLCHSGeometry->cSectors;
1928 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
1929
1930 /* Update header information in base image file. */
1931 vdiFlushImage(pImage);
1932 }
1933 rc = VINF_SUCCESS;
1934 }
1935 else
1936 rc = VERR_VD_NOT_OPENED;
1937
1938out:
1939 LogFlowFunc(("returns %Rrc\n", rc));
1940 return rc;
1941}
1942
1943/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1944static unsigned vdiGetImageFlags(void *pBackendData)
1945{
1946 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1947 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1948 unsigned uImageFlags;
1949
1950 AssertPtr(pImage);
1951
1952 if (pImage)
1953 uImageFlags = pImage->uImageFlags;
1954 else
1955 uImageFlags = 0;
1956
1957 LogFlowFunc(("returns %#x\n", uImageFlags));
1958 return uImageFlags;
1959}
1960
1961/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1962static unsigned vdiGetOpenFlags(void *pBackendData)
1963{
1964 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1965 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1966 unsigned uOpenFlags;
1967
1968 AssertPtr(pImage);
1969
1970 if (pImage)
1971 uOpenFlags = pImage->uOpenFlags;
1972 else
1973 uOpenFlags = 0;
1974
1975 LogFlowFunc(("returns %#x\n", uOpenFlags));
1976 return uOpenFlags;
1977}
1978
1979/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1980static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1981{
1982 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
1983 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1984 int rc;
1985 const char *pszFilename;
1986
1987 /* Image must be opened and the new flags must be valid. */
1988 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1989 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1990 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD
1991 | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1992 {
1993 rc = VERR_INVALID_PARAMETER;
1994 goto out;
1995 }
1996
1997 /* Implement this operation via reopening the image. */
1998 pszFilename = pImage->pszFilename;
1999 rc = vdiFreeImage(pImage, false);
2000 if (RT_FAILURE(rc))
2001 goto out;
2002 rc = vdiOpenImage(pImage, uOpenFlags);
2003
2004out:
2005 LogFlowFunc(("returns %Rrc\n", rc));
2006 return rc;
2007}
2008
2009/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2010static int vdiGetComment(void *pBackendData, char *pszComment,
2011 size_t cbComment)
2012{
2013 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2014 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2015 int rc = VINF_SUCCESS;
2016
2017 AssertPtr(pImage);
2018
2019 if (pImage)
2020 {
2021 char *pszTmp = getImageComment(&pImage->Header);
2022 /* Make this foolproof even if the image doesn't have the zero
2023 * termination. With some luck the repaired header will be saved. */
2024 size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
2025 if (cb == VDI_IMAGE_COMMENT_SIZE)
2026 {
2027 pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
2028 cb--;
2029 }
2030 if (cb < cbComment)
2031 {
2032 /* memcpy is much better than strncpy. */
2033 memcpy(pszComment, pszTmp, cb + 1);
2034 }
2035 else
2036 rc = VERR_BUFFER_OVERFLOW;
2037 }
2038 else
2039 rc = VERR_VD_NOT_OPENED;
2040
2041 LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
2042 return rc;
2043}
2044
2045/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2046static int vdiSetComment(void *pBackendData, const char *pszComment)
2047{
2048 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2049 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2050 int rc;
2051
2052 AssertPtr(pImage);
2053
2054 if (pImage)
2055 {
2056 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2057 rc = VERR_VD_IMAGE_READ_ONLY;
2058 else
2059 {
2060 size_t cchComment = pszComment ? strlen(pszComment) : 0;
2061 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
2062 {
2063 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
2064 rc = VERR_VD_VDI_COMMENT_TOO_LONG;
2065 goto out;
2066 }
2067
2068 /* we don't support old style images */
2069 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2070 {
2071 /*
2072 * Update the comment field, making sure to zero out all of the previous comment.
2073 */
2074 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
2075 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
2076
2077 /* write out new the header */
2078 rc = vdiUpdateHeader(pImage);
2079 }
2080 else
2081 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2082 }
2083 }
2084 else
2085 rc = VERR_VD_NOT_OPENED;
2086
2087out:
2088 LogFlowFunc(("returns %Rrc\n", rc));
2089 return rc;
2090}
2091
2092/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2093static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
2094{
2095 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2096 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2097 int rc;
2098
2099 AssertPtr(pImage);
2100
2101 if (pImage)
2102 {
2103 *pUuid = *getImageCreationUUID(&pImage->Header);
2104 rc = VINF_SUCCESS;
2105 }
2106 else
2107 rc = VERR_VD_NOT_OPENED;
2108
2109 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2110 return rc;
2111}
2112
2113/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2114static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
2115{
2116 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2117 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2118 int rc = VINF_SUCCESS;
2119
2120 AssertPtr(pImage);
2121
2122 if (pImage)
2123 {
2124 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2125 {
2126 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2127 pImage->Header.u.v1.uuidCreate = *pUuid;
2128 /* Make it possible to clone old VDIs. */
2129 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2130 pImage->Header.u.v0.uuidCreate = *pUuid;
2131 else
2132 {
2133 LogFunc(("Version is not supported!\n"));
2134 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2135 }
2136 }
2137 else
2138 rc = VERR_VD_IMAGE_READ_ONLY;
2139 }
2140 else
2141 rc = VERR_VD_NOT_OPENED;
2142
2143 LogFlowFunc(("returns %Rrc\n", rc));
2144 return rc;
2145}
2146
2147/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2148static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2149{
2150 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2151 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2152 int rc;
2153
2154 AssertPtr(pImage);
2155
2156 if (pImage)
2157 {
2158 *pUuid = *getImageModificationUUID(&pImage->Header);
2159 rc = VINF_SUCCESS;
2160 }
2161 else
2162 rc = VERR_VD_NOT_OPENED;
2163
2164 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2165 return rc;
2166}
2167
2168/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2169static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2170{
2171 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2172 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2173 int rc = VINF_SUCCESS;
2174
2175 AssertPtr(pImage);
2176
2177 if (pImage)
2178 {
2179 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2180 {
2181 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2182 pImage->Header.u.v1.uuidModify = *pUuid;
2183 /* Make it possible to clone old VDIs. */
2184 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2185 pImage->Header.u.v0.uuidModify = *pUuid;
2186 else
2187 {
2188 LogFunc(("Version is not supported!\n"));
2189 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2190 }
2191 }
2192 else
2193 rc = VERR_VD_IMAGE_READ_ONLY;
2194 }
2195 else
2196 rc = VERR_VD_NOT_OPENED;
2197
2198 LogFlowFunc(("returns %Rrc\n", rc));
2199 return rc;
2200}
2201
2202/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2203static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
2204{
2205 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2206 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2207 int rc;
2208
2209 AssertPtr(pImage);
2210
2211 if (pImage)
2212 {
2213 *pUuid = *getImageParentUUID(&pImage->Header);
2214 rc = VINF_SUCCESS;
2215 }
2216 else
2217 rc = VERR_VD_NOT_OPENED;
2218
2219 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2220 return rc;
2221}
2222
2223/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2224static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2225{
2226 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2227 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2228 int rc = VINF_SUCCESS;
2229
2230 AssertPtr(pImage);
2231
2232 if (pImage)
2233 {
2234 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2235 {
2236 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2237 pImage->Header.u.v1.uuidLinkage = *pUuid;
2238 /* Make it possible to clone old VDIs. */
2239 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2240 pImage->Header.u.v0.uuidLinkage = *pUuid;
2241 else
2242 {
2243 LogFunc(("Version is not supported!\n"));
2244 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2245 }
2246 }
2247 else
2248 rc = VERR_VD_IMAGE_READ_ONLY;
2249 }
2250 else
2251 rc = VERR_VD_NOT_OPENED;
2252
2253 LogFlowFunc(("returns %Rrc\n", rc));
2254 return rc;
2255}
2256
2257/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2258static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2259{
2260 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2261 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2262 int rc;
2263
2264 AssertPtr(pImage);
2265
2266 if (pImage)
2267 {
2268 *pUuid = *getImageParentModificationUUID(&pImage->Header);
2269 rc = VINF_SUCCESS;
2270 }
2271 else
2272 rc = VERR_VD_NOT_OPENED;
2273
2274 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2275 return rc;
2276}
2277
2278/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2279static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2280{
2281 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2282 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2283 int rc = VINF_SUCCESS;
2284
2285 AssertPtr(pImage);
2286
2287 if (pImage)
2288 {
2289 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2290 {
2291 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2292 pImage->Header.u.v1.uuidParentModify = *pUuid;
2293 else
2294 {
2295 LogFunc(("Version is not supported!\n"));
2296 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2297 }
2298 }
2299 else
2300 rc = VERR_VD_IMAGE_READ_ONLY;
2301 }
2302 else
2303 rc = VERR_VD_NOT_OPENED;
2304
2305 LogFlowFunc(("returns %Rrc\n", rc));
2306 return rc;
2307}
2308
2309/** @copydoc VBOXHDDBACKEND::pfnDump */
2310static void vdiDump(void *pBackendData)
2311{
2312 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2313
2314 vdIfErrorMessage(pImage->pIfError, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
2315 pImage->pszFilename,
2316 (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
2317 pImage->uOpenFlags,
2318 pImage->pStorage);
2319 vdIfErrorMessage(pImage->pIfError, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
2320 pImage->PreHeader.u32Version,
2321 getImageType(&pImage->Header),
2322 getImageFlags(&pImage->Header),
2323 getImageDiskSize(&pImage->Header));
2324 vdIfErrorMessage(pImage->pIfError, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
2325 getImageBlockSize(&pImage->Header),
2326 getImageExtraBlockSize(&pImage->Header),
2327 getImageBlocks(&pImage->Header),
2328 getImageBlocksAllocated(&pImage->Header));
2329 vdIfErrorMessage(pImage->pIfError, "Header: offBlocks=%u offData=%u\n",
2330 getImageBlocksOffset(&pImage->Header),
2331 getImageDataOffset(&pImage->Header));
2332 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
2333 if (pg)
2334 vdIfErrorMessage(pImage->pIfError, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
2335 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
2336 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
2337 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
2338 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
2339 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
2340 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
2341 vdIfErrorMessage(pImage->pIfError, "Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
2342 pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
2343 vdIfErrorMessage(pImage->pIfError, "Image: uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
2344 pImage->uBlockMask,
2345 pImage->cbTotalBlockData,
2346 pImage->uShiftOffset2Index,
2347 pImage->offStartBlockData);
2348
2349 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
2350 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
2351 {
2352 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2353 {
2354 cBlocksNotFree++;
2355 if (pImage->paBlocks[uBlock] >= cBlocks)
2356 cBadBlocks++;
2357 }
2358 }
2359 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
2360 {
2361 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
2362 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
2363 }
2364 if (cBadBlocks)
2365 {
2366 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u bad blocks found !!\n",
2367 cBadBlocks);
2368 }
2369}
2370
2371/** @copydoc VBOXHDDBACKEND::pfnCompact */
2372static int vdiCompact(void *pBackendData, unsigned uPercentStart,
2373 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2374 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2375{
2376 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2377 int rc = VINF_SUCCESS;
2378 void *pvBuf = NULL, *pvTmp = NULL;
2379 unsigned *paBlocks2 = NULL;
2380
2381 int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2382 void *pvParent = NULL;
2383 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2384 if (pIfParentState)
2385 {
2386 pfnParentRead = pIfParentState->pfnParentRead;
2387 pvParent = pIfParentState->Core.pvUser;
2388 }
2389
2390 PFNVDPROGRESS pfnProgress = NULL;
2391 void *pvUser = NULL;
2392 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2393
2394 PVDINTERFACEQUERYRANGEUSE pIfQueryRangeUse = VDIfQueryRangeUseGet(pVDIfsOperation);
2395
2396 do {
2397 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2398
2399 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2400 rc = VERR_VD_IMAGE_READ_ONLY);
2401
2402 unsigned cBlocks;
2403 unsigned cBlocksToMove = 0;
2404 size_t cbBlock;
2405 cBlocks = getImageBlocks(&pImage->Header);
2406 cbBlock = getImageBlockSize(&pImage->Header);
2407 if (pfnParentRead)
2408 {
2409 pvBuf = RTMemTmpAlloc(cbBlock);
2410 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2411 }
2412 pvTmp = RTMemTmpAlloc(cbBlock);
2413 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
2414
2415 uint64_t cbFile;
2416 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2417 AssertRCBreak(rc);
2418 unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
2419 if (cBlocksAllocated == 0)
2420 {
2421 /* No data blocks in this image, no need to compact. */
2422 rc = VINF_SUCCESS;
2423 break;
2424 }
2425
2426 /* Allocate block array for back resolving. */
2427 paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
2428 AssertBreakStmt(VALID_PTR(paBlocks2), rc = VERR_NO_MEMORY);
2429 /* Fill out back resolving, check/fix allocation errors before
2430 * compacting the image, just to be on the safe side. Update the
2431 * image contents straight away, as this enables cancelling. */
2432 for (unsigned i = 0; i < cBlocksAllocated; i++)
2433 paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
2434 rc = VINF_SUCCESS;
2435 for (unsigned i = 0; i < cBlocks; i++)
2436 {
2437 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2438 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2439 {
2440 if (ptrBlock < cBlocksAllocated)
2441 {
2442 if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
2443 paBlocks2[ptrBlock] = i;
2444 else
2445 {
2446 LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
2447 i, pImage->pszFilename));
2448 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2449 rc = vdiUpdateBlockInfo(pImage, i);
2450 if (RT_FAILURE(rc))
2451 break;
2452 }
2453 }
2454 else
2455 {
2456 LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
2457 i, pImage->pszFilename));
2458 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2459 rc = vdiUpdateBlockInfo(pImage, i);
2460 if (RT_FAILURE(rc))
2461 break;
2462 }
2463 }
2464 }
2465 if (RT_FAILURE(rc))
2466 break;
2467
2468 /* Find redundant information and update the block pointers
2469 * accordingly, creating bubbles. Keep disk up to date, as this
2470 * enables cancelling. */
2471 for (unsigned i = 0; i < cBlocks; i++)
2472 {
2473 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2474 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2475 {
2476 /* Block present in image file, read relevant data. */
2477 uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
2478 + (pImage->offStartData + pImage->offStartBlockData);
2479 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock);
2480 if (RT_FAILURE(rc))
2481 break;
2482
2483 if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
2484 {
2485 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2486 rc = vdiUpdateBlockInfo(pImage, i);
2487 if (RT_FAILURE(rc))
2488 break;
2489 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2490 /* Adjust progress info, one block to be relocated. */
2491 cBlocksToMove++;
2492 }
2493 else if (pfnParentRead)
2494 {
2495 rc = pfnParentRead(pvParent, (uint64_t)i * cbBlock, pvBuf, cbBlock);
2496 if (RT_FAILURE(rc))
2497 break;
2498 if (!memcmp(pvTmp, pvBuf, cbBlock))
2499 {
2500 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2501 rc = vdiUpdateBlockInfo(pImage, i);
2502 if (RT_FAILURE(rc))
2503 break;
2504 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2505 /* Adjust progress info, one block to be relocated. */
2506 cBlocksToMove++;
2507 }
2508 }
2509 }
2510
2511 /* Check if the range is in use if the block is still allocated. */
2512 ptrBlock = pImage->paBlocks[i];
2513 if ( IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock)
2514 && pIfQueryRangeUse)
2515 {
2516 bool fUsed = true;
2517
2518 rc = vdIfQueryRangeUse(pIfQueryRangeUse, (uint64_t)i * cbBlock, cbBlock, &fUsed);
2519 if (RT_FAILURE(rc))
2520 break;
2521 if (!fUsed)
2522 {
2523 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2524 rc = vdiUpdateBlockInfo(pImage, i);
2525 if (RT_FAILURE(rc))
2526 break;
2527 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2528 /* Adjust progress info, one block to be relocated. */
2529 cBlocksToMove++;
2530 }
2531 }
2532
2533 if (pIfProgress && pIfProgress->pfnProgress)
2534 {
2535 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2536 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2537 if (RT_FAILURE(rc))
2538 break;
2539 }
2540 }
2541 if (RT_FAILURE(rc))
2542 break;
2543
2544 /* Fill bubbles with other data (if available). */
2545 unsigned cBlocksMoved = 0;
2546 unsigned uBlockUsedPos = cBlocksAllocated;
2547 for (unsigned i = 0; i < cBlocksAllocated; i++)
2548 {
2549 unsigned uBlock = paBlocks2[i];
2550 if (uBlock == VDI_IMAGE_BLOCK_FREE)
2551 {
2552 unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
2553 while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
2554 {
2555 uBlockUsedPos--;
2556 uBlockData = paBlocks2[uBlockUsedPos];
2557 }
2558 /* Terminate early if there is no block which needs copying. */
2559 if (uBlockUsedPos == i)
2560 break;
2561 uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2562 + (pImage->offStartData + pImage->offStartBlockData);
2563 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2564 pvTmp, cbBlock);
2565 u64Offset = (uint64_t)i * pImage->cbTotalBlockData
2566 + (pImage->offStartData + pImage->offStartBlockData);
2567 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2568 pvTmp, cbBlock);
2569 pImage->paBlocks[uBlockData] = i;
2570 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
2571 rc = vdiUpdateBlockInfo(pImage, uBlockData);
2572 if (RT_FAILURE(rc))
2573 break;
2574 paBlocks2[i] = uBlockData;
2575 paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
2576 cBlocksMoved++;
2577 }
2578
2579 if (pIfProgress && pIfProgress->pfnProgress)
2580 {
2581 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2582 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2583
2584 if (RT_FAILURE(rc))
2585 break;
2586 }
2587 }
2588 if (RT_FAILURE(rc))
2589 break;
2590
2591 /* Update image header. */
2592 setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
2593 vdiUpdateHeader(pImage);
2594
2595 /* Truncate the image to the proper size to finish compacting. */
2596 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2597 (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2598 + pImage->offStartData + pImage->offStartBlockData);
2599 } while (0);
2600
2601 if (paBlocks2)
2602 RTMemTmpFree(paBlocks2);
2603 if (pvTmp)
2604 RTMemTmpFree(pvTmp);
2605 if (pvBuf)
2606 RTMemTmpFree(pvBuf);
2607
2608 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2609 {
2610 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2611 uPercentStart + uPercentSpan);
2612 }
2613
2614 LogFlowFunc(("returns %Rrc\n", rc));
2615 return rc;
2616}
2617
2618
2619/** @copydoc VBOXHDDBACKEND::pfnResize */
2620static int vdiResize(void *pBackendData, uint64_t cbSize,
2621 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2622 unsigned uPercentStart, unsigned uPercentSpan,
2623 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2624 PVDINTERFACE pVDIfsOperation)
2625{
2626 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2627 int rc = VINF_SUCCESS;
2628
2629 PFNVDPROGRESS pfnProgress = NULL;
2630 void *pvUser = NULL;
2631 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2632
2633 /*
2634 * Making the image smaller is not supported at the moment.
2635 * Resizing is also not supported for fixed size images and
2636 * very old images.
2637 */
2638 /** @todo implement making the image smaller, it is the responsibility of
2639 * the user to know what he's doing. */
2640 if ( cbSize < getImageDiskSize(&pImage->Header)
2641 || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2642 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2643 rc = VERR_NOT_SUPPORTED;
2644 else if (cbSize > getImageDiskSize(&pImage->Header))
2645 {
2646 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
2647 uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header); /** < New number of blocks in the image after the resize */
2648 if (cbSize % getImageBlockSize(&pImage->Header))
2649 cBlocksNew++;
2650
2651 uint32_t cBlocksOld = getImageBlocks(&pImage->Header); /** < Number of blocks before the resize. */
2652 uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
2653 uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
2654
2655 if ( pImage->offStartData != offStartDataNew
2656 && cBlocksAllocated > 0)
2657 {
2658 /* Calculate how many sectors need to be relocated. */
2659 uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
2660 unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
2661 if (cbOverlapping % getImageBlockSize(&pImage->Header))
2662 cBlocksReloc++;
2663
2664 /* Since only full blocks can be relocated the new data start is
2665 * determined by moving it block by block. */
2666 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2667 offStartDataNew = pImage->offStartData;
2668
2669 /* Do the relocation. */
2670 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2671
2672 /*
2673 * Get the blocks we need to relocate first, they are appended to the end
2674 * of the image.
2675 */
2676 void *pvBuf = NULL, *pvZero = NULL;
2677 do
2678 {
2679 /* Allocate data buffer. */
2680 pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
2681 if (!pvBuf)
2682 {
2683 rc = VERR_NO_MEMORY;
2684 break;
2685 }
2686
2687 /* Allocate buffer for overwriting with zeroes. */
2688 pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
2689 if (!pvZero)
2690 {
2691 rc = VERR_NO_MEMORY;
2692 break;
2693 }
2694
2695 for (unsigned i = 0; i < cBlocksReloc; i++)
2696 {
2697 /* Search the index in the block table. */
2698 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2699 {
2700 if (!pImage->paBlocks[idxBlock])
2701 {
2702 /* Read data and append to the end of the image. */
2703 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2704 offStartDataNew, pvBuf,
2705 pImage->cbTotalBlockData);
2706 if (RT_FAILURE(rc))
2707 break;
2708
2709 uint64_t offBlockAppend;
2710 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &offBlockAppend);
2711 if (RT_FAILURE(rc))
2712 break;
2713
2714 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2715 offBlockAppend, pvBuf,
2716 pImage->cbTotalBlockData);
2717 if (RT_FAILURE(rc))
2718 break;
2719
2720 /* Zero out the old block area. */
2721 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2722 offStartDataNew, pvZero,
2723 pImage->cbTotalBlockData);
2724 if (RT_FAILURE(rc))
2725 break;
2726
2727 /* Update block counter. */
2728 pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
2729
2730 /*
2731 * Decrease the block number of all other entries in the array.
2732 * They were moved one block to the front.
2733 * Doing it as a separate step iterating over the array again
2734 * because an error while relocating the block might end up
2735 * in a corrupted image otherwise.
2736 */
2737 for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
2738 {
2739 if ( idxBlock2 != idxBlock
2740 && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
2741 pImage->paBlocks[idxBlock2]--;
2742 }
2743
2744 /* Continue with the next block. */
2745 break;
2746 }
2747 }
2748
2749 if (RT_FAILURE(rc))
2750 break;
2751
2752 offStartDataNew += pImage->cbTotalBlockData;
2753 }
2754 } while (0);
2755
2756 if (pvBuf)
2757 RTMemFree(pvBuf);
2758 if (pvZero)
2759 RTMemFree(pvZero);
2760 }
2761
2762 /*
2763 * We need to update the new offsets for the image data in the out of memory
2764 * case too because we relocated the blocks already.
2765 */
2766 pImage->offStartData = offStartDataNew;
2767 setImageDataOffset(&pImage->Header, offStartDataNew);
2768
2769 /*
2770 * Relocation done, expand the block array and update the header with
2771 * the new data.
2772 */
2773 if (RT_SUCCESS(rc))
2774 {
2775 PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
2776 if (paBlocksNew)
2777 {
2778 pImage->paBlocks = paBlocksNew;
2779
2780 /* Mark the new blocks as unallocated. */
2781 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2782 pImage->paBlocks[idxBlock] = VDI_IMAGE_BLOCK_FREE;
2783 }
2784 else
2785 rc = VERR_NO_MEMORY;
2786
2787 /* Write the block array before updating the rest. */
2788 vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, cBlocksNew);
2789 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks,
2790 pImage->paBlocks, cbBlockspaceNew);
2791 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, cBlocksNew);
2792
2793 if (RT_SUCCESS(rc))
2794 {
2795 /* Update size and new block count. */
2796 setImageDiskSize(&pImage->Header, cbSize);
2797 setImageBlocks(&pImage->Header, cBlocksNew);
2798 /* Update geometry. */
2799 pImage->PCHSGeometry = *pPCHSGeometry;
2800 pImage->cbImage = cbSize;
2801
2802 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
2803 if (pGeometry)
2804 {
2805 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
2806 pGeometry->cHeads = pLCHSGeometry->cHeads;
2807 pGeometry->cSectors = pLCHSGeometry->cSectors;
2808 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
2809 }
2810 }
2811 }
2812
2813 /* Update header information in base image file. */
2814 vdiFlushImage(pImage);
2815 }
2816 /* Same size doesn't change the image at all. */
2817
2818 LogFlowFunc(("returns %Rrc\n", rc));
2819 return rc;
2820}
2821
2822/** @copydoc VBOXHDDBACKEND::pfnDiscard */
2823static DECLCALLBACK(int) vdiDiscard(void *pBackendData, PVDIOCTX pIoCtx,
2824 uint64_t uOffset, size_t cbDiscard,
2825 size_t *pcbPreAllocated,
2826 size_t *pcbPostAllocated,
2827 size_t *pcbActuallyDiscarded,
2828 void **ppbmAllocationBitmap,
2829 unsigned fDiscard)
2830{
2831 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2832 unsigned uBlock;
2833 unsigned offDiscard;
2834 int rc = VINF_SUCCESS;
2835 void *pvBlock = NULL;
2836
2837 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
2838 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
2839
2840 AssertPtr(pImage);
2841 Assert(!(uOffset % 512));
2842 Assert(!(cbDiscard % 512));
2843
2844 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2845 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
2846 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
2847 && cbDiscard,
2848 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
2849 uOffset, cbDiscard),
2850 VERR_INVALID_PARAMETER);
2851
2852 do
2853 {
2854 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2855 ("Image is opened readonly\n"),
2856 rc = VERR_VD_IMAGE_READ_ONLY);
2857
2858 AssertMsgBreakStmt(cbDiscard,
2859 ("cbDiscard=%u\n", cbDiscard),
2860 rc = VERR_INVALID_PARAMETER);
2861
2862 /* Calculate starting block number and offset inside it. */
2863 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2864 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
2865
2866 /* Clip range to at most the rest of the block. */
2867 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
2868 Assert(!(cbDiscard % 512));
2869
2870 if (pcbPreAllocated)
2871 *pcbPreAllocated = 0;
2872
2873 if (pcbPostAllocated)
2874 *pcbPostAllocated = 0;
2875
2876 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2877 {
2878 uint8_t *pbBlockData;
2879 size_t cbPreAllocated, cbPostAllocated;
2880
2881 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
2882 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
2883
2884 /* Read the block data. */
2885 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
2886 if (!pvBlock)
2887 {
2888 rc = VERR_NO_MEMORY;
2889 break;
2890 }
2891
2892 if (!cbPreAllocated && !cbPostAllocated)
2893 {
2894 /*
2895 * Discarding a whole block, don't check for allocated sectors.
2896 * It is possible to just remove the whole block which avoids
2897 * one read and checking the whole block for data.
2898 */
2899 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
2900 }
2901 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
2902 {
2903 /* Just zero out the given range. */
2904 memset(pvBlock, 0, cbDiscard);
2905
2906 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
2907 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
2908 u64Offset, pvBlock, cbDiscard, pIoCtx,
2909 NULL, NULL);
2910 RTMemFree(pvBlock);
2911 }
2912 else
2913 {
2914 /*
2915 * Read complete block as metadata, the I/O context has no memory buffer
2916 * and we need to access the content directly anyway.
2917 */
2918 PVDMETAXFER pMetaXfer;
2919 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
2920
2921 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
2922 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
2923 pbBlockData, pImage->cbTotalBlockData,
2924 pIoCtx, &pMetaXfer, NULL, NULL);
2925 if (RT_FAILURE(rc))
2926 {
2927 RTMemFree(pvBlock);
2928 break;
2929 }
2930
2931 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
2932
2933 /* Clear data. */
2934 memset(pbBlockData + offDiscard , 0, cbDiscard);
2935
2936 Assert(!(cbDiscard % 4));
2937 Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
2938 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
2939 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
2940 else
2941 {
2942 /* Block has data, create allocation bitmap. */
2943 *pcbPreAllocated = cbPreAllocated;
2944 *pcbPostAllocated = cbPostAllocated;
2945 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
2946 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
2947 rc = VERR_NO_MEMORY;
2948 else
2949 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
2950
2951 RTMemFree(pvBlock);
2952 }
2953 } /* if: no complete block discarded */
2954 } /* if: Block is allocated. */
2955 /* else: nothing to do. */
2956 } while (0);
2957
2958 if (pcbActuallyDiscarded)
2959 *pcbActuallyDiscarded = cbDiscard;
2960
2961 LogFlowFunc(("returns %Rrc\n", rc));
2962 return rc;
2963}
2964
2965/** @copydoc VBOXHDDBACKEND::pfnRepair */
2966static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
2967 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
2968{
2969 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
2970 int rc;
2971 PVDINTERFACEERROR pIfError;
2972 PVDINTERFACEIOINT pIfIo;
2973 PVDIOSTORAGE pStorage;
2974 uint64_t cbFile;
2975 PVDIIMAGEBLOCKPOINTER paBlocks = NULL;
2976 uint32_t *pu32BlockBitmap = NULL;
2977 VDIPREHEADER PreHdr;
2978 VDIHEADER Hdr;
2979
2980 pIfIo = VDIfIoIntGet(pVDIfsImage);
2981 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
2982
2983 pIfError = VDIfErrorGet(pVDIfsDisk);
2984
2985 do
2986 {
2987 bool fRepairHdr = false;
2988 bool fRepairBlockArray = false;
2989
2990 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
2991 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
2992 ? VD_OPEN_FLAGS_READONLY
2993 : 0,
2994 false /* fCreate */),
2995 &pStorage);
2996 if (RT_FAILURE(rc))
2997 {
2998 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to open image \"%s\"", pszFilename);
2999 break;
3000 }
3001
3002 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
3003 if (RT_FAILURE(rc))
3004 {
3005 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to query image size");
3006 break;
3007 }
3008
3009 /* Read pre-header. */
3010 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr));
3011 if (RT_FAILURE(rc))
3012 {
3013 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: Error reading pre-header in '%s'"), pszFilename);
3014 break;
3015 }
3016 vdiConvPreHeaderEndianess(VDIECONV_F2H, &PreHdr, &PreHdr);
3017 rc = vdiValidatePreHeader(&PreHdr);
3018 if (RT_FAILURE(rc))
3019 {
3020 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3021 N_("VDI: invalid pre-header in '%s'"), pszFilename);
3022 break;
3023 }
3024
3025 /* Read header. */
3026 Hdr.uVersion = PreHdr.u32Version;
3027 switch (GET_MAJOR_HEADER_VERSION(&Hdr))
3028 {
3029 case 0:
3030 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3031 &Hdr.u.v0, sizeof(Hdr.u.v0));
3032 if (RT_FAILURE(rc))
3033 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"),
3034 pszFilename);
3035 vdiConvHeaderEndianessV0(VDIECONV_F2H, &Hdr.u.v0, &Hdr.u.v0);
3036 break;
3037 case 1:
3038 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3039 &Hdr.u.v1, sizeof(Hdr.u.v1));
3040 if (RT_FAILURE(rc))
3041 {
3042 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"),
3043 pszFilename);
3044 }
3045 vdiConvHeaderEndianessV1(VDIECONV_F2H, &Hdr.u.v1, &Hdr.u.v1);
3046 if (Hdr.u.v1.cbHeader >= sizeof(Hdr.u.v1plus))
3047 {
3048 /* Read the VDI 1.1+ header completely. */
3049 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3050 &Hdr.u.v1plus, sizeof(Hdr.u.v1plus));
3051 if (RT_FAILURE(rc))
3052 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"),
3053 pszFilename);
3054 vdiConvHeaderEndianessV1p(VDIECONV_F2H, &Hdr.u.v1plus, &Hdr.u.v1plus);
3055 }
3056 break;
3057 default:
3058 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3059 N_("VDI: unsupported major version %u in '%s'"),
3060 GET_MAJOR_HEADER_VERSION(&Hdr), pszFilename);
3061 break;
3062 }
3063
3064 if (RT_SUCCESS(rc))
3065 {
3066 rc = vdiValidateHeader(&Hdr);
3067 if (RT_FAILURE(rc))
3068 {
3069 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3070 N_("VDI: invalid header in '%s'"), pszFilename);
3071 break;
3072 }
3073 }
3074
3075 /* Setup image parameters by header. */
3076 uint64_t offStartBlocks, offStartData;
3077 size_t cbTotalBlockData;
3078
3079 offStartBlocks = getImageBlocksOffset(&Hdr);
3080 offStartData = getImageDataOffset(&Hdr);
3081 cbTotalBlockData = getImageExtraBlockSize(&Hdr) + getImageBlockSize(&Hdr);
3082
3083 /* Allocate memory for blocks array. */
3084 paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&Hdr));
3085 if (!paBlocks)
3086 {
3087 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3088 "Failed to allocate memory for block array");
3089 break;
3090 }
3091
3092 /* Read blocks array. */
3093 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3094 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
3095 if (RT_FAILURE(rc))
3096 {
3097 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3098 "Failed to read block array (at %llu), %Rrc",
3099 offStartBlocks, rc);
3100 break;
3101 }
3102 vdiConvBlocksEndianess(VDIECONV_F2H, paBlocks, getImageBlocks(&Hdr));
3103
3104 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(getImageBlocks(&Hdr) / 8, 4));
3105 if (!pu32BlockBitmap)
3106 {
3107 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3108 "Failed to allocate memory for block bitmap");
3109 break;
3110 }
3111
3112 for (uint32_t i = 0; i < getImageBlocks(&Hdr); i++)
3113 {
3114 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(paBlocks[i]))
3115 {
3116 uint64_t offBlock = (uint64_t)paBlocks[i] * cbTotalBlockData
3117 + offStartData;
3118
3119 /*
3120 * Check that the offsets are valid (inside of the image) and
3121 * that there are no double references.
3122 */
3123 if (offBlock + cbTotalBlockData > cbFile)
3124 {
3125 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
3126 i, offBlock);
3127 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3128 fRepairBlockArray = true;
3129 }
3130 else if (ASMBitTestAndSet(pu32BlockBitmap, paBlocks[i]))
3131 {
3132 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
3133 i);
3134 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3135 fRepairBlockArray = true;
3136 }
3137 }
3138 }
3139
3140 /* Write repaired structures now. */
3141 if (!fRepairBlockArray)
3142 vdIfErrorMessage(pIfError, "VDI image is in a consistent state, no repair required\n");
3143 else if (!(fFlags & VD_REPAIR_DRY_RUN))
3144 {
3145 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
3146
3147 vdiConvBlocksEndianess(VDIECONV_H2F, paBlocks, getImageBlocks(&Hdr));
3148 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3149 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER));
3150 if (RT_FAILURE(rc))
3151 {
3152 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3153 "Could not write repaired block allocation table (at %llu), %Rrc",
3154 offStartBlocks, rc);
3155 break;
3156 }
3157 }
3158
3159 vdIfErrorMessage(pIfError, "Corrupted VDI image repaired successfully\n");
3160 } while(0);
3161
3162 if (paBlocks)
3163 RTMemFree(paBlocks);
3164
3165 if (pu32BlockBitmap)
3166 RTMemFree(pu32BlockBitmap);
3167
3168 if (pStorage)
3169 {
3170 int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
3171 if (RT_SUCCESS(rc))
3172 rc = rc2; /* Propagate error code only if repairing was successful. */
3173 }
3174
3175 LogFlowFunc(("returns %Rrc\n", rc));
3176 return rc;
3177}
3178
3179VBOXHDDBACKEND g_VDIBackend =
3180{
3181 /* pszBackendName */
3182 "VDI",
3183 /* cbSize */
3184 sizeof(VBOXHDDBACKEND),
3185 /* uBackendCaps */
3186 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
3187 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_DISCARD,
3188 /* paFileExtensions */
3189 s_aVdiFileExtensions,
3190 /* paConfigInfo */
3191 NULL,
3192 /* hPlugin */
3193 NIL_RTLDRMOD,
3194 /* pfnCheckIfValid */
3195 vdiCheckIfValid,
3196 /* pfnOpen */
3197 vdiOpen,
3198 /* pfnCreate */
3199 vdiCreate,
3200 /* pfnRename */
3201 vdiRename,
3202 /* pfnClose */
3203 vdiClose,
3204 /* pfnRead */
3205 vdiRead,
3206 /* pfnWrite */
3207 vdiWrite,
3208 /* pfnFlush */
3209 vdiFlush,
3210 /* pfnDiscard */
3211 vdiDiscard,
3212 /* pfnGetVersion */
3213 vdiGetVersion,
3214 /* pfnGetSize */
3215 vdiGetSize,
3216 /* pfnGetFileSize */
3217 vdiGetFileSize,
3218 /* pfnGetPCHSGeometry */
3219 vdiGetPCHSGeometry,
3220 /* pfnSetPCHSGeometry */
3221 vdiSetPCHSGeometry,
3222 /* pfnGetLCHSGeometry */
3223 vdiGetLCHSGeometry,
3224 /* pfnSetLCHSGeometry */
3225 vdiSetLCHSGeometry,
3226 /* pfnGetImageFlags */
3227 vdiGetImageFlags,
3228 /* pfnGetOpenFlags */
3229 vdiGetOpenFlags,
3230 /* pfnSetOpenFlags */
3231 vdiSetOpenFlags,
3232 /* pfnGetComment */
3233 vdiGetComment,
3234 /* pfnSetComment */
3235 vdiSetComment,
3236 /* pfnGetUuid */
3237 vdiGetUuid,
3238 /* pfnSetUuid */
3239 vdiSetUuid,
3240 /* pfnGetModificationUuid */
3241 vdiGetModificationUuid,
3242 /* pfnSetModificationUuid */
3243 vdiSetModificationUuid,
3244 /* pfnGetParentUuid */
3245 vdiGetParentUuid,
3246 /* pfnSetParentUuid */
3247 vdiSetParentUuid,
3248 /* pfnGetParentModificationUuid */
3249 vdiGetParentModificationUuid,
3250 /* pfnSetParentModificationUuid */
3251 vdiSetParentModificationUuid,
3252 /* pfnDump */
3253 vdiDump,
3254 /* pfnGetTimeStamp */
3255 NULL,
3256 /* pfnGetParentTimeStamp */
3257 NULL,
3258 /* pfnSetParentTimeStamp */
3259 NULL,
3260 /* pfnGetParentFilename */
3261 NULL,
3262 /* pfnSetParentFilename */
3263 NULL,
3264 /* pfnComposeLocation */
3265 genericFileComposeLocation,
3266 /* pfnComposeName */
3267 genericFileComposeName,
3268 /* pfnCompact */
3269 vdiCompact,
3270 /* pfnResize */
3271 vdiResize,
3272 /* pfnRepair */
3273 vdiRepair
3274};
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