VirtualBox

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

Last change on this file since 44232 was 44232, checked in by vboxsync, 12 years ago

Storage: Fix regression introduced when adding VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS resulting in non working online merging of snapshots

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

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