VirtualBox

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

Last change on this file since 58113 was 57399, checked in by vboxsync, 9 years ago

build fix

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