VirtualBox

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

Last change on this file since 77606 was 77606, checked in by vboxsync, 6 years ago

Added AllocationBlockSize property to VDI backend, and ability to set properties with VBoxManage --property key=value

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette