VirtualBox

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

Last change on this file since 67954 was 66505, checked in by vboxsync, 8 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
RevLine 
[40033]1/* $Id: VDI.cpp 66505 2017-04-11 09:31:25Z vboxsync $ */
[1]2/** @file
[1565]3 * Virtual Disk Image (VDI), Core Code.
[1]4 */
5
6/*
[60608]7 * Copyright (C) 2006-2016 Oracle Corporation
[1]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
[5999]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.
[1]16 */
17
[57358]18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[7159]22#define LOG_GROUP LOG_GROUP_VD_VDI
[33567]23#include <VBox/vd-plugin.h>
[1565]24#include "VDICore.h"
[1]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>
[150]31#include <iprt/string.h>
[745]32#include <iprt/asm.h>
[1]33
[50988]34#include "VDBackends.h"
35
[7159]36#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
[1]37
[40936]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
[11421]42
[57358]43/*********************************************************************************************************************************
44* Static Variables *
45*********************************************************************************************************************************/
46
[11421]47/** NULL-terminated array of supported file extensions. */
[33524]48static const VDFILEEXTENSION s_aVdiFileExtensions[] =
[11421]49{
[33524]50 {"vdi", VDTYPE_HDD},
51 {NULL, VDTYPE_INVALID}
[11421]52};
53
[57358]54
55/*********************************************************************************************************************************
56* Internal Functions *
57*********************************************************************************************************************************/
[1]58static unsigned getPowerOfTwo(unsigned uNumber);
59static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
[139]60static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
61static int vdiValidateHeader(PVDIHEADER pHeader);
[1]62static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
[139]63static int vdiUpdateHeader(PVDIIMAGEDESC pImage);
64static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
[28065]65static int vdiUpdateHeaderAsync(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx);
[38876]66static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock, PVDIOCTX pIoCtx,
67 bool fUpdateHdr);
[139]68
[7159]69/**
[40936]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{
[40953]78 memcpy(pPreHdrConv->szFileInfo, pPreHdr->szFileInfo, sizeof(pPreHdr->szFileInfo));
[40936]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{
[46612]107 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
[40936]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{
[46612]130 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
[40936]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{
[46612]159 memmove(pHdrConv->szComment, pHdr->szComment, sizeof(pHdr->szComment));
[40936]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/**
[32536]197 * Internal: Flush the image file to disk.
198 */
199static void vdiFlushImage(PVDIIMAGEDESC pImage)
[28383]200{
[32536]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));
[38469]207 vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
[32536]208 }
[28383]209}
[22966]210
[32536]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;
[28383]218
[32536]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
[46613]229 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
[32536]230 pImage->pStorage = NULL;
231 }
232
233 if (pImage->paBlocks)
234 {
235 RTMemFree(pImage->paBlocks);
236 pImage->paBlocks = NULL;
237 }
238
[38621]239 if (pImage->paBlocksRev)
240 {
241 RTMemFree(pImage->paBlocksRev);
242 pImage->paBlocksRev = NULL;
243 }
244
[32536]245 if (fDelete && pImage->pszFilename)
[38469]246 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
[32536]247 }
248
249 LogFlowFunc(("returns %Rrc\n", rc));
250 return rc;
251}
252
[1]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/**
[7159]270 * Internal: Init VDI preheader.
[1]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));
[32878]277 strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo)-1);
[1]278}
279
280/**
[7159]281 * Internal: check VDI preheader.
[1]282 */
283static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
284{
285 if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
[15673]286 return VERR_VD_VDI_INVALID_HEADER;
[1]287
[6363]288 if ( VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
[1]289 && pPreHdr->u32Version != 0x00000002) /* old version. */
[15366]290 return VERR_VD_VDI_UNSUPPORTED_VERSION;
[1]291
292 return VINF_SUCCESS;
293}
294
295/**
[17970]296 * Internal: translate VD image flags to VDI image type enum.
[13223]297 */
[17970]298static VDIIMAGETYPE vdiTranslateImageFlags2VDI(unsigned uImageFlags)
[13223]299{
[17970]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;
[13223]306}
307
308/**
309 * Internal: translate VDI image type enum to VD image type enum.
310 */
[17970]311static unsigned vdiTranslateVDI2ImageFlags(VDIIMAGETYPE enmType)
[13223]312{
313 switch (enmType)
314 {
315 case VDI_IMAGE_TYPE_NORMAL:
[17970]316 return VD_IMAGE_FLAGS_NONE;
[13223]317 case VDI_IMAGE_TYPE_FIXED:
[17970]318 return VD_IMAGE_FLAGS_FIXED;
[13223]319 case VDI_IMAGE_TYPE_DIFF:
[17970]320 return VD_IMAGE_FLAGS_DIFF;
[13223]321 default:
322 AssertMsgFailed(("invalid VDIIMAGETYPE enmType=%d\n", (int)enmType));
[17970]323 return VD_IMAGE_FLAGS_NONE;
[13223]324 }
325}
326
327/**
[7159]328 * Internal: Init VDI header. Always use latest header version.
[64272]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.
[1]338 */
[17970]339static void vdiInitHeader(PVDIHEADER pHeader, uint32_t uImageFlags,
340 const char *pszComment, uint64_t cbDisk,
[43602]341 uint32_t cbBlock, uint32_t cbBlockExtra,
342 uint32_t cbDataAlign)
[1]343{
344 pHeader->uVersion = VDI_IMAGE_VERSION;
[33081]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;
[1]348#ifdef VBOX_STRICT
349 char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
[33081]350 Assert(!memcmp(pHeader->u.v1plus.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
[1]351#endif
[33081]352 pHeader->u.v1plus.szComment[0] = '\0';
[1]353 if (pszComment)
354 {
[33081]355 AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1plus.szComment),
[1]356 ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
[33081]357 strncat(pHeader->u.v1plus.szComment, pszComment, sizeof(pHeader->u.v1plus.szComment)-1);
[1]358 }
359
[6363]360 /* Mark the legacy geometry not-calculated. */
[33081]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 */
[1]366
[33081]367 pHeader->u.v1plus.cbDisk = cbDisk;
368 pHeader->u.v1plus.cbBlock = cbBlock;
369 pHeader->u.v1plus.cBlocks = (uint32_t)(cbDisk / cbBlock);
[1]370 if (cbDisk % cbBlock)
[33081]371 pHeader->u.v1plus.cBlocks++;
372 pHeader->u.v1plus.cbBlockExtra = cbBlockExtra;
373 pHeader->u.v1plus.cBlocksAllocated = 0;
[1]374
375 /* Init offsets. */
[43602]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);
[1]378
379 /* Init uuids. */
[62740]380#ifdef _MSC_VER
381# pragma warning(disable:4366) /* (harmless "misalignment") */
382#endif
[33081]383 RTUuidCreate(&pHeader->u.v1plus.uuidCreate);
384 RTUuidClear(&pHeader->u.v1plus.uuidModify);
385 RTUuidClear(&pHeader->u.v1plus.uuidLinkage);
386 RTUuidClear(&pHeader->u.v1plus.uuidParentModify);
[62740]387#ifdef _MSC_VER
388# pragma warning(default:4366)
389#endif
[6363]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;
[1]396}
397
398/**
[7159]399 * Internal: Check VDI header.
[1]400 */
401static int vdiValidateHeader(PVDIHEADER pHeader)
402{
[33540]403 /* Check version-dependent header parameters. */
[1]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))
[124]416 {
417 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
418 pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
[15366]419 return VERR_VD_VDI_INVALID_HEADER;
[124]420 }
[1]421
422 if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
[124]423 {
424 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
425 getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
[15366]426 return VERR_VD_VDI_INVALID_HEADER;
[124]427 }
[1]428
429 if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
[124]430 {
431 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
432 getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
[15366]433 return VERR_VD_VDI_INVALID_HEADER;
[124]434 }
[1]435
436 break;
437 }
438 default:
439 /* Unsupported. */
[15366]440 return VERR_VD_VDI_UNSUPPORTED_VERSION;
[1]441 }
442
443 /* Check common header parameters. */
444
[150]445 bool fFailed = false;
[139]446
[1]447 if ( getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
448 || getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
[139]449 {
450 LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
[150]451 fFailed = true;
[139]452 }
[1]453
[7159]454 if (getImageFlags(pHeader) & ~VD_VDI_IMAGE_FLAGS_MASK)
[139]455 {
456 LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
[150]457 fFailed = true;
[139]458 }
[1]459
[6363]460 if ( getImageLCHSGeometry(pHeader)
461 && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
[124]462 {
[139]463 LogRel(("VDI: wrong sector size (%d != %d)\n",
[6291]464 (getImageLCHSGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
[150]465 fFailed = true;
[124]466 }
[1]467
468 if ( getImageDiskSize(pHeader) == 0
469 || getImageBlockSize(pHeader) == 0
470 || getImageBlocks(pHeader) == 0
[124]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))));
[150]476 fFailed = true;
[124]477 }
[1]478
[124]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)));
[150]485 fFailed = true;
[124]486 }
487
[1]488 if ( getImageExtraBlockSize(pHeader) != 0
489 && getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
[124]490 {
491 LogRel(("VDI: wrong extra size (%d, %d)\n",
492 getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
[150]493 fFailed = true;
[124]494 }
[1]495
[139]496 if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
[124]497 {
498 LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
[139]499 getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
[150]500 fFailed = true;
[124]501 }
[1]502
503 if (RTUuidIsNull(getImageCreationUUID(pHeader)))
[124]504 {
505 LogRel(("VDI: uuid of creator is 0\n"));
[150]506 fFailed = true;
[124]507 }
[139]508
[1]509 if (RTUuidIsNull(getImageModificationUUID(pHeader)))
[124]510 {
[33595]511 LogRel(("VDI: uuid of modifier is 0\n"));
[150]512 fFailed = true;
[124]513 }
[1]514
[15366]515 return fFailed ? VERR_VD_VDI_INVALID_HEADER : VINF_SUCCESS;
[1]516}
517
518/**
[7159]519 * Internal: Set up VDIIMAGEDESC structure by image header.
[1]520 */
521static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
522{
[7159]523 pImage->uImageFlags = getImageFlags(&pImage->Header);
[17970]524 pImage->uImageFlags |= vdiTranslateVDI2ImageFlags(getImageType(&pImage->Header));
[139]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);
[7231]530 pImage->cbTotalBlockData = pImage->offStartBlockData
531 + getImageBlockSize(&pImage->Header);
[1]532}
533
534/**
[63809]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
[63887]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 }
[63809]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/**
[7159]683 * Internal: Create VDI image file.
[1]684 */
[17970]685static int vdiCreateImage(PVDIIMAGEDESC pImage, uint64_t cbSize,
686 unsigned uImageFlags, const char *pszComment,
[63809]687 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
688 PCRTUUID pUuid, unsigned uOpenFlags,
689 PVDINTERFACEPROGRESS pIfProgress, unsigned uPercentStart,
[43602]690 unsigned uPercentSpan, PVDINTERFACECONFIG pIfCfg)
[1]691{
[63809]692 int rc = VINF_SUCCESS;
[43602]693 uint32_t cbDataAlign = VDI_DATA_ALIGN;
[1]694
[38469]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
[1]702 /* Special check for comment length. */
[7159]703 if ( VALID_PTR(pszComment)
704 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
[38469]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);
[1]707
[43602]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,
[57007]713 N_("VDI: Getting data alignment for '%s' failed (%Rrc)"), pImage->pszFilename, rc);
[43602]714 }
715
[63809]716 if (RT_SUCCESS(rc))
[1]717 {
[63809]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 }
[1]730 }
731
[63809]732 if (RT_SUCCESS(rc))
[66486]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
[63809]746 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
[66486]747 }
[1]748
[11266]749 if (RT_FAILURE(rc))
[63809]750 vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
751 return rc;
752}
[6481]753
[63809]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))
[7159]766 {
[63809]767 /* Read pre-header. */
768 VDIPREHEADER PreHeader;
769 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
770 &PreHeader, sizeof(PreHeader));
771 if (RT_SUCCESS(rc))
[1]772 {
[63809]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);
[64784]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));
[63809]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);
[1]863 }
[63809]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 }
[7159]869 }
870 else
871 {
[63809]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;
[7159]874 }
[1]875
[63809]876 return rc;
877}
[11353]878
[63809]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;
[1]888
[63809]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)
[7159]895 {
[63809]896 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
897 unsigned cBlocks = getImageBlocks(&pImage->Header);
[1]898
[63809]899 for (unsigned i = 0; i < cBlocks; i++)
900 pImage->paBlocksRev[i] = VDI_IMAGE_BLOCK_FREE;
[1]901
[63809]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 }
[7159]924 }
[63809]925 else
926 rc = VERR_NO_MEMORY;
[1]927
928 return rc;
929}
930
931/**
[7159]932 * Internal: Open a VDI image.
[1]933 */
[7159]934static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
[1]935{
[7159]936 pImage->uOpenFlags = uOpenFlags;
[1]937
[38469]938 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
939 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
940 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
[11435]941
[1]942 /*
943 * Open the image.
944 */
[63809]945 int rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
946 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */),
947 &pImage->pStorage);
948 if (RT_SUCCESS(rc))
[1]949 {
[63809]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))
[7159]956 {
[63809]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))
[7159]961 {
[63809]962 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, getImageBlocks(&pImage->Header));
[139]963
[63809]964 if (uOpenFlags & VD_OPEN_FLAGS_DISCARD)
965 rc = vdiImageBackResolvTblCreate(pImage);
[38621]966 }
[63809]967 else
968 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: Error reading the block table in '%s'"), pImage->pszFilename);
[38621]969 }
[63809]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);;
[38621]973 }
974 }
[63809]975 /* else: Do NOT signal an appropriate error here, as the VD layer has the
976 * choice of retrying the open if it failed. */
[38621]977
[66486]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
[7159]993 vdiFreeImage(pImage, false);
[1]994 return rc;
995}
996
997/**
[7159]998 * Internal: Save header to file.
[1]999 */
1000static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
1001{
[7159]1002 int rc;
1003 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
[1]1004 {
[7159]1005 case 0:
[40936]1006 {
1007 VDIHEADER0 Hdr;
1008 vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
[38469]1009 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
[44233]1010 &Hdr, sizeof(Hdr));
[7159]1011 break;
[40936]1012 }
[7159]1013 case 1:
1014 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
[40936]1015 {
1016 VDIHEADER1 Hdr;
1017 vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
[38469]1018 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
[44233]1019 &Hdr, sizeof(Hdr));
[40936]1020 }
[7159]1021 else
[40936]1022 {
1023 VDIHEADER1PLUS Hdr;
1024 vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
[38469]1025 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VDIPREHEADER),
[44233]1026 &Hdr, sizeof(Hdr));
[40936]1027 }
[7159]1028 break;
1029 default:
[15366]1030 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[7159]1031 break;
[1]1032 }
[11284]1033 AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
[1]1034 return rc;
1035}
1036
1037/**
[28065]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:
[40936]1046 {
1047 VDIHEADER0 Hdr;
1048 vdiConvHeaderEndianessV0(VDIECONV_H2F, &Hdr, &pImage->Header.u.v0);
[44233]1049 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1050 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
1051 pIoCtx, NULL, NULL);
[28065]1052 break;
[40936]1053 }
[28065]1054 case 1:
1055 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
[40936]1056 {
1057 VDIHEADER1 Hdr;
1058 vdiConvHeaderEndianessV1(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1);
[44233]1059 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1060 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
1061 pIoCtx, NULL, NULL);
[40936]1062 }
[28065]1063 else
[40936]1064 {
1065 VDIHEADER1PLUS Hdr;
1066 vdiConvHeaderEndianessV1p(VDIECONV_H2F, &Hdr, &pImage->Header.u.v1plus);
[44233]1067 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1068 sizeof(VDIPREHEADER), &Hdr, sizeof(Hdr),
1069 pIoCtx, NULL, NULL);
[40936]1070 }
[28065]1071 break;
1072 default:
1073 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
1074 break;
1075 }
[30555]1076 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
1077 ("vdiUpdateHeader failed, filename=\"%s\" rc=%Rrc\n", pImage->pszFilename, rc));
[28065]1078 return rc;
1079}
1080
1081/**
[7159]1082 * Internal: Save block pointer to file, save header to file.
[1]1083 */
1084static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
1085{
1086 /* Update image header. */
1087 int rc = vdiUpdateHeader(pImage);
[11266]1088 if (RT_SUCCESS(rc))
[1]1089 {
1090 /* write only one block pointer. */
[40936]1091 VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
[38469]1092 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
1093 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
[44233]1094 &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER));
[11284]1095 AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Rrc\n",
[7159]1096 uBlock, pImage->pszFilename, rc));
[1]1097 }
1098 return rc;
1099}
1100
1101/**
[28065]1102 * Internal: Save block pointer to file, save header to file - async version.
1103 */
1104static int vdiUpdateBlockInfoAsync(PVDIIMAGEDESC pImage, unsigned uBlock,
[38876]1105 PVDIOCTX pIoCtx, bool fUpdateHdr)
[28065]1106{
[38876]1107 int rc = VINF_SUCCESS;
1108
[28065]1109 /* Update image header. */
[38876]1110 if (fUpdateHdr)
1111 rc = vdiUpdateHeaderAsync(pImage, pIoCtx);
1112
[30555]1113 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
[28065]1114 {
1115 /* write only one block pointer. */
[40936]1116 VDIIMAGEBLOCKPOINTER ptrBlock = RT_H2LE_U32(pImage->paBlocks[uBlock]);
[44233]1117 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1118 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
1119 &ptrBlock, sizeof(VDIIMAGEBLOCKPOINTER),
1120 pIoCtx, NULL, NULL);
[30555]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));
[28065]1124 }
1125 return rc;
1126}
1127
1128/**
[28383]1129 * Internal: Flush the image file to disk - async version.
1130 */
[44252]1131static int vdiFlushImageIoCtx(PVDIIMAGEDESC pImage, PVDIOCTX pIoCtx)
[28383]1132{
[30555]1133 int rc = VINF_SUCCESS;
1134
[28383]1135 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1136 {
1137 /* Save header. */
[30555]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));
[44233]1142 rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
[30555]1143 AssertMsg(RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS,
1144 ("Flushing data to disk failed rc=%Rrc\n", rc));
[28383]1145 }
[30555]1146
1147 return rc;
[28383]1148}
1149
[38657]1150/**
[38876]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{
[62742]1163 RT_NOREF1(rcReq);
[38876]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;
[44233]1174 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
1175 pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
1176 &pMetaXfer, vdiDiscardBlockAsyncUpdate, pDiscardAsync);
[38876]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 }
[65644]1184 /* fall thru */
[38876]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;
[44233]1189 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
1190 pDiscardAsync->pvBlock, pImage->cbTotalBlockData, pIoCtx,
1191 vdiDiscardBlockAsyncUpdate, pDiscardAsync);
[38876]1192
1193 pDiscardAsync->enmState = VDIBLOCKDISCARDSTATE_UPDATE_METADATA;
1194 if (RT_FAILURE(rc))
1195 break;
1196 }
[65644]1197 /* fall thru */
[38876]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
[40713]1223 pImage->cbImage -= pImage->cbTotalBlockData;
1224 LogFlowFunc(("Set new size %llu\n", pImage->cbImage));
1225 rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbImage);
[38876]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/**
[38657]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{
[48851]1309 Assert(cbData <= UINT32_MAX / 8);
1310 uint32_t cSectors = (uint32_t)(cbData / 512);
1311 uint32_t uSectorCur = 0;
[38657]1312 void *pbmAllocationBitmap = NULL;
[1]1313
[38657]1314 Assert(!(cbData % 512));
1315 Assert(!(cSectors % 8));
1316
1317 pbmAllocationBitmap = RTMemAllocZ(cSectors / 8);
[40713]1318 if (!pbmAllocationBitmap)
1319 return NULL;
[38657]1320
1321 while (uSectorCur < cSectors)
1322 {
[48851]1323 int idxSet = ASMBitFirstSet((uint8_t *)pvData + uSectorCur * 512, (uint32_t)cbData * 8);
[38657]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
[40713]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 */
[44252]1350static DECLCALLBACK(int) vdiBlockAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
[40713]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
[63802]1374/** @copydoc VDIMAGEBACKEND::pfnProbe */
1375static DECLCALLBACK(int) vdiProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1376 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
[7159]1377{
1378 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
1379 int rc = VINF_SUCCESS;
[1]1380
[63809]1381 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
[1]1382
[66505]1383 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(RT_UOFFSETOF(VDIIMAGEDESC, RegionList.aRegions[1]));
[63809]1384 if (RT_LIKELY(pImage))
[1]1385 {
[63809]1386 pImage->pszFilename = pszFilename;
1387 pImage->pStorage = NULL;
1388 pImage->paBlocks = NULL;
1389 pImage->pVDIfsDisk = pVDIfsDisk;
1390 pImage->pVDIfsImage = pVDIfsImage;
[1]1391
[63809]1392 rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
1393 vdiFreeImage(pImage, false);
1394 RTMemFree(pImage);
[1]1395
[63809]1396 if (RT_SUCCESS(rc))
1397 *penmType = VDTYPE_HDD;
1398 }
1399 else
1400 rc = VERR_NO_MEMORY;
[33524]1401
[11284]1402 LogFlowFunc(("returns %Rrc\n", rc));
[1]1403 return rc;
1404}
1405
[63785]1406/** @copydoc VDIMAGEBACKEND::pfnOpen */
[57388]1407static DECLCALLBACK(int) vdiOpen(const char *pszFilename, unsigned uOpenFlags,
1408 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1409 VDTYPE enmType, void **ppBackendData)
[1]1410{
[63809]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));
[1]1415 int rc;
1416
[7159]1417 /* Check open flags. All valid flags are supported. */
[63809]1418 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1419 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
[1]1420
[66486]1421 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(RT_UOFFSETOF(VDIIMAGEDESC, RegionList.aRegions[1]));
[63809]1422 if (RT_LIKELY(pImage))
[1]1423 {
[63809]1424 pImage->pszFilename = pszFilename;
1425 pImage->pStorage = NULL;
1426 pImage->paBlocks = NULL;
1427 pImage->pVDIfsDisk = pVDIfsDisk;
1428 pImage->pVDIfsImage = pVDIfsImage;
[1]1429
[63809]1430 rc = vdiOpenImage(pImage, uOpenFlags);
1431 if (RT_SUCCESS(rc))
1432 *ppBackendData = pImage;
1433 else
1434 RTMemFree(pImage);
[1]1435 }
[40739]1436 else
[63809]1437 rc = VERR_NO_MEMORY;
[1]1438
[11284]1439 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
[1]1440 return rc;
1441}
1442
[63785]1443/** @copydoc VDIMAGEBACKEND::pfnCreate */
[57388]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)
[1]1452{
[54430]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));
[710]1455 int rc;
[1]1456
[63809]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;
[37552]1461
[63809]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
[64711]1466 || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
1467 || (cbSize % 512))
[63809]1468 return VERR_VD_INVALID_SIZE;
[43602]1469
[7159]1470 /* Check open flags. All valid flags are supported. */
[63809]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);
[1]1476
[66486]1477 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(RT_UOFFSETOF(VDIIMAGEDESC, RegionList.aRegions[1]));
[63809]1478 if (RT_LIKELY(pImage))
[18567]1479 {
[63809]1480 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1481 PVDINTERFACECONFIG pIfCfg = VDIfConfigGet(pVDIfsOperation);
[18567]1482
[63809]1483 pImage->pszFilename = pszFilename;
1484 pImage->pStorage = NULL;
1485 pImage->paBlocks = NULL;
1486 pImage->pVDIfsDisk = pVDIfsDisk;
1487 pImage->pVDIfsImage = pVDIfsImage;
[1]1488
[63809]1489 rc = vdiCreateImage(pImage, cbSize, uImageFlags, pszComment,
1490 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1491 pIfProgress, uPercentStart, uPercentSpan, pIfCfg);
1492 if (RT_SUCCESS(rc))
[1]1493 {
[63809]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)
[32536]1497 {
[63809]1498 vdiFreeImage(pImage, false);
1499 rc = vdiOpenImage(pImage, uOpenFlags);
[32536]1500 }
[63809]1501
1502 if (RT_SUCCESS(rc))
1503 *ppBackendData = pImage;
[1]1504 }
[63809]1505
1506 if (RT_FAILURE(rc))
1507 RTMemFree(pImage);
[1]1508 }
[13427]1509 else
[63809]1510 rc = VERR_NO_MEMORY;
[1]1511
[11284]1512 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
[1]1513 return rc;
1514}
1515
[63785]1516/** @copydoc VDIMAGEBACKEND::pfnRename */
[57388]1517static DECLCALLBACK(int) vdiRename(void *pBackendData, const char *pszFilename)
[1]1518{
[7159]1519 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
[7689]1520 int rc = VINF_SUCCESS;
1521 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1522
1523 /* Check arguments. */
[63809]1524 AssertReturn((pImage && pszFilename && *pszFilename), VERR_INVALID_PARAMETER);
[7689]1525
1526 /* Close the image. */
[46613]1527 rc = vdiFreeImage(pImage, false);
1528 if (RT_SUCCESS(rc))
[11266]1529 {
[46613]1530 /* Rename the file. */
1531 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
[63809]1532 if (RT_SUCCESS(rc))
[46613]1533 {
[63809]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 {
[46613]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 }
[7689]1548
[11284]1549 LogFlowFunc(("returns %Rrc\n", rc));
[1]1550 return rc;
1551}
1552
[63785]1553/** @copydoc VDIMAGEBACKEND::pfnClose */
[57388]1554static DECLCALLBACK(int) vdiClose(void *pBackendData, bool fDelete)
[1]1555{
[7159]1556 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1557 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1558
[63809]1559 int rc = vdiFreeImage(pImage, fDelete);
[32536]1560 RTMemFree(pImage);
[1]1561
[11284]1562 LogFlowFunc(("returns %Rrc\n", rc));
[1]1563 return rc;
1564}
1565
[57388]1566static DECLCALLBACK(int) vdiRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1567 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
[1]1568{
[44252]1569 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1570 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
[7159]1571 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1572 unsigned uBlock;
1573 unsigned offRead;
[63809]1574 int rc = VINF_SUCCESS;
[1]1575
[14269]1576 AssertPtr(pImage);
[7231]1577 Assert(!(uOffset % 512));
1578 Assert(!(cbToRead % 512));
[63809]1579 AssertReturn((VALID_PTR(pIoCtx) && cbToRead), VERR_INVALID_PARAMETER);
1580 AssertReturn(uOffset + cbToRead <= getImageDiskSize(&pImage->Header), VERR_INVALID_PARAMETER);
[1]1581
[7159]1582 /* Calculate starting block number and offset inside it. */
1583 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
1584 offRead = (unsigned)uOffset & pImage->uBlockMask;
[1]1585
[7159]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));
[1]1589
[7159]1590 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
[15366]1591 rc = VERR_VD_BLOCK_FREE;
[7159]1592 else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
[1]1593 {
[44252]1594 size_t cbSet;
1595
1596 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
1597 Assert(cbSet == cbToRead);
[1]1598 }
[7159]1599 else
[1]1600 {
[7231]1601 /* Block present in image file, read relevant data. */
1602 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
[7159]1603 + (pImage->offStartData + pImage->offStartBlockData + offRead);
[40713]1604
1605 if (u64Offset + cbToRead <= pImage->cbImage)
[44252]1606 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, u64Offset,
1607 pIoCtx, cbToRead);
[40713]1608 else
1609 {
1610 LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
1611 u64Offset, pImage->pszFilename, pImage->cbImage));
[44252]1612 vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
[40713]1613 rc = VERR_VD_READ_OUT_OF_RANGE;
1614 }
[1]1615 }
1616
[14269]1617 if (pcbActuallyRead)
[7159]1618 *pcbActuallyRead = cbToRead;
[1]1619
[11284]1620 LogFlowFunc(("returns %Rrc\n", rc));
[1]1621 return rc;
1622}
1623
[57388]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)
[1]1627{
[44252]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));
[7159]1630 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1631 unsigned uBlock;
1632 unsigned offWrite;
1633 int rc = VINF_SUCCESS;
[1]1634
[14269]1635 AssertPtr(pImage);
[7231]1636 Assert(!(uOffset % 512));
1637 Assert(!(cbToWrite % 512));
[63809]1638 AssertReturn((VALID_PTR(pIoCtx) && cbToWrite), VERR_INVALID_PARAMETER);
[1]1639
[63809]1640 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]1641 {
[63809]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. */
[1]1645
[63809]1646 /* Calculate starting block number and offset inside it. */
1647 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
1648 offWrite = (unsigned)uOffset & pImage->uBlockMask;
[1]1649
[63809]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));
[1]1653
[63809]1654 do
[1]1655 {
[63809]1656 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
[1]1657 {
[63809]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)))
[15786]1662 {
[63809]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 }
[15786]1674 }
[1]1675
[63809]1676 if ( cbToWrite == getImageBlockSize(&pImage->Header)
1677 && !(fWrite & VD_WRITE_NO_ALLOC))
[44252]1678 {
[63809]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 }
[44252]1688
[63809]1689 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1690 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
1691 + (pImage->offStartData + pImage->offStartBlockData);
[38621]1692
[63809]1693 pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
1694 pBlockAlloc->uBlock = uBlock;
[38621]1695
[63809]1696 *pcbPreRead = 0;
1697 *pcbPostRead = 0;
[1]1698
[63809]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
[44252]1713 {
[63809]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;
[44252]1719 }
[15786]1720 }
1721 else
1722 {
[63809]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);
[15786]1728 }
[63809]1729 } while (0);
[15785]1730
[63809]1731 if (pcbWriteProcess)
1732 *pcbWriteProcess = cbToWrite;
1733 }
1734 else
1735 rc = VERR_VD_IMAGE_READ_ONLY;
[1]1736
[11284]1737 LogFlowFunc(("returns %Rrc\n", rc));
[1]1738 return rc;
1739}
1740
[57388]1741static DECLCALLBACK(int) vdiFlush(void *pBackendData, PVDIOCTX pIoCtx)
[1]1742{
[7159]1743 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1744 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1745 int rc = VINF_SUCCESS;
[1]1746
[10539]1747 Assert(pImage);
1748
[44252]1749 rc = vdiFlushImageIoCtx(pImage, pIoCtx);
[11284]1750 LogFlowFunc(("returns %Rrc\n", rc));
[1]1751 return rc;
1752}
1753
[63785]1754/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
[57388]1755static DECLCALLBACK(unsigned) vdiGetVersion(void *pBackendData)
[1]1756{
[7159]1757 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1758 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1759
[63809]1760 AssertPtrReturn(pImage, 0);
[1]1761
[63809]1762 LogFlowFunc(("returns %#x\n", pImage->PreHeader.u32Version));
1763 return pImage->PreHeader.u32Version;
[1]1764}
1765
[63785]1766/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
[57388]1767static DECLCALLBACK(uint64_t) vdiGetFileSize(void *pBackendData)
[1]1768{
[7159]1769 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1770 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1771 uint64_t cb = 0;
[1]1772
[63809]1773 AssertPtrReturn(pImage, 0);
[1]1774
[63809]1775 if (pImage->pStorage)
[1]1776 {
[7159]1777 uint64_t cbFile;
[63809]1778 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1779 if (RT_SUCCESS(rc))
1780 cb += cbFile;
[1]1781 }
1782
[7159]1783 LogFlowFunc(("returns %lld\n", cb));
1784 return cb;
[1]1785}
1786
[63785]1787/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
[57388]1788static DECLCALLBACK(int) vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
[1]1789{
[7159]1790 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1791 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[63809]1792 int rc = VINF_SUCCESS;
[1]1793
[63809]1794 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]1795
[63809]1796 if (pImage->PCHSGeometry.cCylinders)
1797 *pPCHSGeometry = pImage->PCHSGeometry;
[1]1798 else
[63809]1799 rc = VERR_VD_GEOMETRY_NOT_SET;
[1]1800
[11284]1801 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
[7159]1802 return rc;
[1]1803}
1804
[63785]1805/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
[57388]1806static DECLCALLBACK(int) vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
[1]1807{
[63809]1808 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1809 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
[7159]1810 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[63809]1811 int rc = VINF_SUCCESS;
[1]1812
[63809]1813 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]1814
[63809]1815 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1816 rc = VERR_VD_IMAGE_READ_ONLY;
1817 else
[7159]1818 pImage->PCHSGeometry = *pPCHSGeometry;
[1]1819
[11284]1820 LogFlowFunc(("returns %Rrc\n", rc));
[7159]1821 return rc;
[1]1822}
1823
[63785]1824/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
[57388]1825static DECLCALLBACK(int) vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
[1]1826{
[7159]1827 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1828 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1829
[63809]1830 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]1831
[63809]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)
[1]1841 {
[63809]1842 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
1843 pLCHSGeometry->cHeads = pGeometry->cHeads;
1844 pLCHSGeometry->cSectors = pGeometry->cSectors;
[1]1845 }
[7159]1846 else
[63809]1847 rc = VERR_VD_GEOMETRY_NOT_SET;
[1]1848
[11284]1849 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
[7159]1850 return rc;
[1]1851}
1852
[63785]1853/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
[57388]1854static DECLCALLBACK(int) vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
[1]1855{
[63809]1856 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
1857 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
[7159]1858 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1859 PVDIDISKGEOMETRY pGeometry;
[63809]1860 int rc = VINF_SUCCESS;
[1]1861
[63809]1862 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[7159]1863
[63809]1864 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]1865 {
[7159]1866 pGeometry = getImageLCHSGeometry(&pImage->Header);
[6363]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;
[1]1873
[6363]1874 /* Update header information in base image file. */
[7159]1875 vdiFlushImage(pImage);
[6363]1876 }
[1]1877 }
[7159]1878 else
[63809]1879 rc = VERR_VD_IMAGE_READ_ONLY;
[1]1880
[11284]1881 LogFlowFunc(("returns %Rrc\n", rc));
[7159]1882 return rc;
[1]1883}
1884
[66486]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
[63785]1909/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
[57388]1910static DECLCALLBACK(unsigned) vdiGetImageFlags(void *pBackendData)
[1]1911{
[7159]1912 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1913 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1914
[63809]1915 AssertPtrReturn(pImage, 0);
[1]1916
[63809]1917 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
1918 return pImage->uImageFlags;
[1]1919}
1920
[63785]1921/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
[57388]1922static DECLCALLBACK(unsigned) vdiGetOpenFlags(void *pBackendData)
[1]1923{
[7159]1924 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1925 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1926
[63809]1927 AssertPtrReturn(pImage, 0);
[1]1928
[63809]1929 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
1930 return pImage->uOpenFlags;
[1]1931}
1932
[63785]1933/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
[57388]1934static DECLCALLBACK(int) vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
[1]1935{
[13580]1936 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
[7159]1937 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1938 int rc;
1939 const char *pszFilename;
[1]1940
[33182]1941 /* Image must be opened and the new flags must be valid. */
[44232]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)))
[63809]1946 rc = VERR_INVALID_PARAMETER;
1947 else
[1]1948 {
[63809]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);
[1]1954 }
1955
[11284]1956 LogFlowFunc(("returns %Rrc\n", rc));
[7159]1957 return rc;
[1]1958}
1959
[63785]1960/** @copydoc VDIMAGEBACKEND::pfnGetComment */
[57388]1961static DECLCALLBACK(int) vdiGetComment(void *pBackendData, char *pszComment,
1962 size_t cbComment)
[1]1963{
[7159]1964 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1965 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]1966
[63809]1967 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]1968
[63809]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)
[1]1975 {
[63809]1976 pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
1977 cb--;
[1]1978 }
[63809]1979 if (cb < cbComment)
1980 {
1981 /* memcpy is much better than strncpy. */
1982 memcpy(pszComment, pszTmp, cb + 1);
1983 }
[7159]1984 else
[63809]1985 rc = VERR_BUFFER_OVERFLOW;
[1]1986
[11284]1987 LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
[7159]1988 return rc;
[1]1989}
1990
[64272]1991/** @copydoc VDIMAGEBACKEND::pfnSetComment */
[57388]1992static DECLCALLBACK(int) vdiSetComment(void *pBackendData, const char *pszComment)
[1]1993{
[7159]1994 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1995 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1996 int rc;
[1]1997
[63809]1998 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]1999
[63809]2000 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]2001 {
[63809]2002 size_t cchComment = pszComment ? strlen(pszComment) : 0;
2003 if (cchComment < VDI_IMAGE_COMMENT_SIZE)
[7159]2004 {
[33524]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);
[7159]2013
[33524]2014 /* write out new the header */
2015 rc = vdiUpdateHeader(pImage);
2016 }
2017 else
2018 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[7159]2019 }
[63809]2020 else
2021 {
2022 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
2023 rc = VERR_VD_VDI_COMMENT_TOO_LONG;
2024 }
[1]2025 }
[7159]2026 else
[63809]2027 rc = VERR_VD_IMAGE_READ_ONLY;
[1]2028
[11284]2029 LogFlowFunc(("returns %Rrc\n", rc));
[7159]2030 return rc;
[1]2031}
2032
[63785]2033/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
[57388]2034static DECLCALLBACK(int) vdiGetUuid(void *pBackendData, PRTUUID pUuid)
[1]2035{
[7159]2036 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2037 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2038
[63809]2039 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2040
[63809]2041 *pUuid = *getImageCreationUUID(&pImage->Header);
[1]2042
[63809]2043 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2044 return VINF_SUCCESS;
[1]2045}
2046
[63785]2047/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
[57388]2048static DECLCALLBACK(int) vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
[1]2049{
[11287]2050 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
[7159]2051 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2052
[63809]2053 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2054
[63809]2055 int rc = VINF_SUCCESS;
2056 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]2057 {
[63809]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
[7159]2064 {
[63809]2065 LogFunc(("Version is not supported!\n"));
2066 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[7159]2067 }
[1]2068 }
[7159]2069 else
[63809]2070 rc = VERR_VD_IMAGE_READ_ONLY;
[1]2071
[11284]2072 LogFlowFunc(("returns %Rrc\n", rc));
[7159]2073 return rc;
[1]2074}
2075
[63785]2076/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
[57388]2077static DECLCALLBACK(int) vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
[1]2078{
[7159]2079 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2080 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2081
[63809]2082 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2083
[63809]2084 *pUuid = *getImageModificationUUID(&pImage->Header);
[1]2085
[63809]2086 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2087 return VINF_SUCCESS;
[1]2088}
2089
[63785]2090/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
[57388]2091static DECLCALLBACK(int) vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
[1]2092{
[11287]2093 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
[7159]2094 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2095
[63809]2096 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2097
[63809]2098 int rc = VINF_SUCCESS;
2099 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]2100 {
[63809]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
[7159]2107 {
[63809]2108 LogFunc(("Version is not supported!\n"));
2109 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[7159]2110 }
[1]2111 }
[7159]2112 else
[63809]2113 rc = VERR_VD_IMAGE_READ_ONLY;
[1]2114
[11284]2115 LogFlowFunc(("returns %Rrc\n", rc));
[1]2116 return rc;
2117}
2118
[63785]2119/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
[57388]2120static DECLCALLBACK(int) vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
[1]2121{
[7159]2122 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2123 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2124
[63809]2125 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[7159]2126
[63809]2127 *pUuid = *getImageParentUUID(&pImage->Header);
[1]2128
[63809]2129 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2130 return VINF_SUCCESS;
[1]2131}
2132
[63785]2133/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
[57388]2134static DECLCALLBACK(int) vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
[1]2135{
[11287]2136 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
[7159]2137 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2138
[63809]2139 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2140
[63809]2141 int rc = VINF_SUCCESS;
2142 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]2143 {
[63809]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
[1]2150 {
[63809]2151 LogFunc(("Version is not supported!\n"));
2152 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[1]2153 }
2154 }
[7159]2155 else
[63809]2156 rc = VERR_VD_IMAGE_READ_ONLY;
[1]2157
[11284]2158 LogFlowFunc(("returns %Rrc\n", rc));
[1]2159 return rc;
2160}
2161
[63785]2162/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
[57388]2163static DECLCALLBACK(int) vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
[1]2164{
[7159]2165 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2166 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2167
[63809]2168 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[7159]2169
[63809]2170 *pUuid = *getImageParentModificationUUID(&pImage->Header);
[1]2171
[63809]2172 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2173 return VINF_SUCCESS;
[1]2174}
2175
[63785]2176/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
[57388]2177static DECLCALLBACK(int) vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
[1]2178{
[11287]2179 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
[7159]2180 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2181
[63809]2182 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
[1]2183
[63809]2184 int rc = VINF_SUCCESS;
2185 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
[1]2186 {
[63809]2187 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2188 pImage->Header.u.v1.uuidParentModify = *pUuid;
2189 else
[1]2190 {
[63809]2191 LogFunc(("Version is not supported!\n"));
2192 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
[1]2193 }
2194 }
[7159]2195 else
[63809]2196 rc = VERR_VD_IMAGE_READ_ONLY;
[1]2197
[11284]2198 LogFlowFunc(("returns %Rrc\n", rc));
[1]2199 return rc;
2200}
2201
[63785]2202/** @copydoc VDIMAGEBACKEND::pfnDump */
[57388]2203static DECLCALLBACK(void) vdiDump(void *pBackendData)
[1]2204{
[7159]2205 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
[1]2206
[63809]2207 AssertPtrReturnVoid(pImage);
[38469]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));
[6291]2226 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
[6363]2227 if (pg)
[38469]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));
[1]2233 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
[38469]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);
[139]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 {
[38469]2255 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
2256 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
[139]2257 }
2258 if (cBadBlocks)
2259 {
[38469]2260 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u bad blocks found !!\n",
2261 cBadBlocks);
[139]2262 }
[1]2263}
2264
[63785]2265/** @copydoc VDIMAGEBACKEND::pfnCompact */
[57388]2266static DECLCALLBACK(int) vdiCompact(void *pBackendData, unsigned uPercentStart,
2267 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2268 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
[19176]2269{
[62742]2270 RT_NOREF2(pVDIfsDisk, pVDIfsImage);
[19176]2271 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2272 int rc = VINF_SUCCESS;
2273 void *pvBuf = NULL, *pvTmp = NULL;
2274 unsigned *paBlocks2 = NULL;
[10715]2275
[57399]2276 DECLCALLBACKMEMBER(int, pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
[19176]2277 void *pvParent = NULL;
[38469]2278 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
[19176]2279 if (pIfParentState)
2280 {
[38469]2281 pfnParentRead = pIfParentState->pfnParentRead;
2282 pvParent = pIfParentState->Core.pvUser;
[19176]2283 }
2284
[38469]2285 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
[40031]2286 PVDINTERFACEQUERYRANGEUSE pIfQueryRangeUse = VDIfQueryRangeUseGet(pVDIfsOperation);
2287
[63809]2288 do
2289 {
[19176]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);
[54117]2303 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
[19176]2304 }
2305 pvTmp = RTMemTmpAlloc(cbBlock);
[54117]2306 AssertBreakStmt(pvTmp, rc = VERR_NO_MEMORY);
[19176]2307
2308 uint64_t cbFile;
[38469]2309 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
[19176]2310 AssertRCBreak(rc);
2311 unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
[19202]2312 if (cBlocksAllocated == 0)
2313 {
2314 /* No data blocks in this image, no need to compact. */
2315 rc = VINF_SUCCESS;
2316 break;
2317 }
[19176]2318
2319 /* Allocate block array for back resolving. */
2320 paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
[54117]2321 AssertBreakStmt(paBlocks2, rc = VERR_NO_MEMORY);
[19176]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. */
[19251]2325 for (unsigned i = 0; i < cBlocksAllocated; i++)
[19176]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));
[19251]2351 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
[19176]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);
[44233]2372 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock);
[19176]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 {
[40032]2388 rc = pfnParentRead(pvParent, (uint64_t)i * cbBlock, pvBuf, cbBlock);
[19176]2389 if (RT_FAILURE(rc))
2390 break;
[19202]2391 if (!memcmp(pvTmp, pvBuf, cbBlock))
[19176]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
[40031]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
[63809]2426 vdIfProgress(pIfProgress, (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2427 if (RT_FAILURE(rc))
2428 break;
[19176]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 {
[19202]2441 unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
2442 while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
2443 {
[19176]2444 uBlockUsedPos--;
2445 uBlockData = paBlocks2[uBlockUsedPos];
[19202]2446 }
[19176]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);
[38469]2452 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
[44233]2453 pvTmp, cbBlock);
[19176]2454 u64Offset = (uint64_t)i * pImage->cbTotalBlockData
2455 + (pImage->offStartData + pImage->offStartBlockData);
[38469]2456 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
[44233]2457 pvTmp, cbBlock);
[19176]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
[63809]2468 rc = vdIfProgress(pIfProgress, (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2469 if (RT_FAILURE(rc))
2470 break;
[19176]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. */
[38469]2480 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2481 (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2482 + pImage->offStartData + pImage->offStartBlockData);
[19176]2483 } while (0);
2484
2485 if (paBlocks2)
2486 RTMemTmpFree(paBlocks2);
2487 if (pvTmp)
2488 RTMemTmpFree(pvTmp);
2489 if (pvBuf)
2490 RTMemTmpFree(pvBuf);
2491
[63809]2492 if (RT_SUCCESS(rc))
2493 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
[19176]2494
2495 LogFlowFunc(("returns %Rrc\n", rc));
2496 return rc;
2497}
2498
2499
[63785]2500/** @copydoc VDIMAGEBACKEND::pfnResize */
[57388]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)
[31804]2506{
[62742]2507 RT_NOREF5(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation);
[31804]2508 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2509 int rc = VINF_SUCCESS;
2510
[64693]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
[31920]2518 /*
[32431]2519 * Making the image smaller is not supported at the moment.
[31920]2520 * Resizing is also not supported for fixed size images and
2521 * very old images.
2522 */
[32536]2523 /** @todo implement making the image smaller, it is the responsibility of
2524 * the user to know what he's doing. */
[31920]2525 if ( cbSize < getImageDiskSize(&pImage->Header)
2526 || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2527 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
[31804]2528 rc = VERR_NOT_SUPPORTED;
2529 else if (cbSize > getImageDiskSize(&pImage->Header))
2530 {
[31810]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))
[31804]2534 cBlocksNew++;
2535
[31810]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. */
[32536]2538 uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
[31804]2539
[64504]2540 if (pImage->offStartData < offStartDataNew)
[31804]2541 {
[64504]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++;
[31804]2549
[64504]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;
[31810]2554
[64504]2555 /* Do the relocation. */
2556 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
[31804]2557
[64504]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
[31804]2564 {
[64504]2565 /* Allocate data buffer. */
2566 pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
2567 if (!pvBuf)
2568 {
2569 rc = VERR_NO_MEMORY;
2570 break;
2571 }
[31804]2572
[64504]2573 /* Allocate buffer for overwriting with zeroes. */
2574 pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
2575 if (!pvZero)
2576 {
2577 rc = VERR_NO_MEMORY;
2578 break;
2579 }
[31804]2580
[64504]2581 for (unsigned i = 0; i < cBlocksReloc; i++)
[31804]2582 {
[64504]2583 /* Search the index in the block table. */
2584 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
[31804]2585 {
[64504]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;
[31804]2594
[64504]2595 uint64_t offBlockAppend;
2596 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &offBlockAppend);
2597 if (RT_FAILURE(rc))
2598 break;
[31804]2599
[64504]2600 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2601 offBlockAppend, pvBuf,
2602 pImage->cbTotalBlockData);
2603 if (RT_FAILURE(rc))
2604 break;
[31804]2605
[64504]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;
[31804]2612
[64504]2613 /* Update block counter. */
2614 pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
[31804]2615
[64504]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;
[31804]2632 }
[64504]2633 }
[31810]2634
[64504]2635 if (RT_FAILURE(rc))
[31810]2636 break;
[64504]2637
2638 offStartDataNew += pImage->cbTotalBlockData;
[31804]2639 }
[64504]2640 } while (0);
[31804]2641
[64504]2642 if (pvBuf)
2643 RTMemFree(pvBuf);
2644 if (pvZero)
2645 RTMemFree(pvZero);
2646 }
[31804]2647
[64504]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);
[31804]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 {
[31810]2662 PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
[31804]2663 if (paBlocksNew)
2664 {
[31810]2665 pImage->paBlocks = paBlocksNew;
2666
[31804]2667 /* Mark the new blocks as unallocated. */
[31810]2668 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
[31804]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. */
[40936]2675 vdiConvBlocksEndianess(VDIECONV_H2F, pImage->paBlocks, cBlocksNew);
[38469]2676 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks,
[44233]2677 pImage->paBlocks, cbBlockspaceNew);
[40936]2678 vdiConvBlocksEndianess(VDIECONV_F2H, pImage->paBlocks, cBlocksNew);
[31804]2679
[31810]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;
[44225]2687 pImage->cbImage = cbSize;
[31804]2688
[31810]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 }
[31804]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
[63785]2709/** @copydoc VDIMAGEBACKEND::pfnDiscard */
[44252]2710static DECLCALLBACK(int) vdiDiscard(void *pBackendData, PVDIOCTX pIoCtx,
[63809]2711 uint64_t uOffset, size_t cbDiscard,
2712 size_t *pcbPreAllocated, size_t *pcbPostAllocated,
2713 size_t *pcbActuallyDiscarded, void **ppbmAllocationBitmap,
2714 unsigned fDiscard)
[38876]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;
[44233]2792 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
2793 u64Offset, pvBlock, cbDiscard, pIoCtx,
2794 NULL, NULL);
[38876]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;
[44233]2807 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage, u64Offset,
2808 pbBlockData, pImage->cbTotalBlockData,
2809 pIoCtx, &pMetaXfer, NULL, NULL);
[38876]2810 if (RT_FAILURE(rc))
[42914]2811 {
2812 RTMemFree(pvBlock);
[38876]2813 break;
[42914]2814 }
[38876]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
[63785]2850/** @copydoc VDIMAGEBACKEND::pfnRepair */
[40240]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;
[64784]2873 bool fRepairHdr = false;
[40240]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. */
[44233]2895 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr));
[40240]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 }
[40936]2901 vdiConvPreHeaderEndianess(VDIECONV_F2H, &PreHdr, &PreHdr);
[40240]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. */
[40936]2911 Hdr.uVersion = PreHdr.u32Version;
[40240]2912 switch (GET_MAJOR_HEADER_VERSION(&Hdr))
2913 {
2914 case 0:
2915 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
[44233]2916 &Hdr.u.v0, sizeof(Hdr.u.v0));
[40240]2917 if (RT_FAILURE(rc))
2918 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"),
2919 pszFilename);
[40936]2920 vdiConvHeaderEndianessV0(VDIECONV_F2H, &Hdr.u.v0, &Hdr.u.v0);
[40240]2921 break;
2922 case 1:
2923 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
[44233]2924 &Hdr.u.v1, sizeof(Hdr.u.v1));
[40240]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 }
[40936]2930 vdiConvHeaderEndianessV1(VDIECONV_F2H, &Hdr.u.v1, &Hdr.u.v1);
[40240]2931 if (Hdr.u.v1.cbHeader >= sizeof(Hdr.u.v1plus))
2932 {
2933 /* Read the VDI 1.1+ header completely. */
2934 rc = vdIfIoIntFileReadSync(pIfIo