VirtualBox

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

Last change on this file since 67981 was 66505, checked in by vboxsync, 7 years ago

Storage/VDI,VMDK: Allocate the correct amount of memory when probing

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

© 2023 Oracle
ContactPrivacy policyTerms of Use