VirtualBox

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

Last change on this file since 55628 was 54430, checked in by vboxsync, 10 years ago

Storage/VD: make use of the image type (hdd/dvd/floppy) for sanity checking when creating disk images

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

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