VirtualBox

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

Last change on this file since 76678 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use