VirtualBox

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

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

Storage/VDI: Make the block data alignment configurable

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 134.2 KB
Line 
1/* $Id: VDI.cpp 43602 2012-10-10 15:57:27Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD_VDI
22#include <VBox/vd-plugin.h>
23#include "VDICore.h"
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/string.h>
31#include <iprt/asm.h>
32
33#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 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_DISCARD)))
2057 {
2058 rc = VERR_INVALID_PARAMETER;
2059 goto out;
2060 }
2061
2062 /* Implement this operation via reopening the image. */
2063 pszFilename = pImage->pszFilename;
2064 rc = vdiFreeImage(pImage, false);
2065 if (RT_FAILURE(rc))
2066 goto out;
2067 rc = vdiOpenImage(pImage, uOpenFlags);
2068
2069out:
2070 LogFlowFunc(("returns %Rrc\n", rc));
2071 return rc;
2072}
2073
2074/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2075static int vdiGetComment(void *pBackendData, char *pszComment,
2076 size_t cbComment)
2077{
2078 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2079 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2080 int rc = VINF_SUCCESS;
2081
2082 AssertPtr(pImage);
2083
2084 if (pImage)
2085 {
2086 char *pszTmp = getImageComment(&pImage->Header);
2087 /* Make this foolproof even if the image doesn't have the zero
2088 * termination. With some luck the repaired header will be saved. */
2089 size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
2090 if (cb == VDI_IMAGE_COMMENT_SIZE)
2091 {
2092 pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
2093 cb--;
2094 }
2095 if (cb < cbComment)
2096 {
2097 /* memcpy is much better than strncpy. */
2098 memcpy(pszComment, pszTmp, cb + 1);
2099 }
2100 else
2101 rc = VERR_BUFFER_OVERFLOW;
2102 }
2103 else
2104 rc = VERR_VD_NOT_OPENED;
2105
2106 LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
2107 return rc;
2108}
2109
2110/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2111static int vdiSetComment(void *pBackendData, const char *pszComment)
2112{
2113 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2114 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2115 int rc;
2116
2117 AssertPtr(pImage);
2118
2119 if (pImage)
2120 {
2121 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2122 rc = VERR_VD_IMAGE_READ_ONLY;
2123 else
2124 {
2125 size_t cchComment = pszComment ? strlen(pszComment) : 0;
2126 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
2127 {
2128 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
2129 rc = VERR_VD_VDI_COMMENT_TOO_LONG;
2130 goto out;
2131 }
2132
2133 /* we don't support old style images */
2134 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2135 {
2136 /*
2137 * Update the comment field, making sure to zero out all of the previous comment.
2138 */
2139 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
2140 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
2141
2142 /* write out new the header */
2143 rc = vdiUpdateHeader(pImage);
2144 }
2145 else
2146 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2147 }
2148 }
2149 else
2150 rc = VERR_VD_NOT_OPENED;
2151
2152out:
2153 LogFlowFunc(("returns %Rrc\n", rc));
2154 return rc;
2155}
2156
2157/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2158static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
2159{
2160 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2161 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2162 int rc;
2163
2164 AssertPtr(pImage);
2165
2166 if (pImage)
2167 {
2168 *pUuid = *getImageCreationUUID(&pImage->Header);
2169 rc = VINF_SUCCESS;
2170 }
2171 else
2172 rc = VERR_VD_NOT_OPENED;
2173
2174 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2175 return rc;
2176}
2177
2178/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2179static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
2180{
2181 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2182 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2183 int rc = VINF_SUCCESS;
2184
2185 AssertPtr(pImage);
2186
2187 if (pImage)
2188 {
2189 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2190 {
2191 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2192 pImage->Header.u.v1.uuidCreate = *pUuid;
2193 /* Make it possible to clone old VDIs. */
2194 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2195 pImage->Header.u.v0.uuidCreate = *pUuid;
2196 else
2197 {
2198 LogFunc(("Version is not supported!\n"));
2199 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2200 }
2201 }
2202 else
2203 rc = VERR_VD_IMAGE_READ_ONLY;
2204 }
2205 else
2206 rc = VERR_VD_NOT_OPENED;
2207
2208 LogFlowFunc(("returns %Rrc\n", rc));
2209 return rc;
2210}
2211
2212/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2213static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2214{
2215 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2216 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2217 int rc;
2218
2219 AssertPtr(pImage);
2220
2221 if (pImage)
2222 {
2223 *pUuid = *getImageModificationUUID(&pImage->Header);
2224 rc = VINF_SUCCESS;
2225 }
2226 else
2227 rc = VERR_VD_NOT_OPENED;
2228
2229 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2230 return rc;
2231}
2232
2233/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2234static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2235{
2236 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2237 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2238 int rc = VINF_SUCCESS;
2239
2240 AssertPtr(pImage);
2241
2242 if (pImage)
2243 {
2244 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2245 {
2246 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2247 pImage->Header.u.v1.uuidModify = *pUuid;
2248 /* Make it possible to clone old VDIs. */
2249 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2250 pImage->Header.u.v0.uuidModify = *pUuid;
2251 else
2252 {
2253 LogFunc(("Version is not supported!\n"));
2254 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2255 }
2256 }
2257 else
2258 rc = VERR_VD_IMAGE_READ_ONLY;
2259 }
2260 else
2261 rc = VERR_VD_NOT_OPENED;
2262
2263 LogFlowFunc(("returns %Rrc\n", rc));
2264 return rc;
2265}
2266
2267/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2268static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
2269{
2270 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2271 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2272 int rc;
2273
2274 AssertPtr(pImage);
2275
2276 if (pImage)
2277 {
2278 *pUuid = *getImageParentUUID(&pImage->Header);
2279 rc = VINF_SUCCESS;
2280 }
2281 else
2282 rc = VERR_VD_NOT_OPENED;
2283
2284 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2285 return rc;
2286}
2287
2288/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2289static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2290{
2291 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2292 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2293 int rc = VINF_SUCCESS;
2294
2295 AssertPtr(pImage);
2296
2297 if (pImage)
2298 {
2299 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2300 {
2301 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2302 pImage->Header.u.v1.uuidLinkage = *pUuid;
2303 /* Make it possible to clone old VDIs. */
2304 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2305 pImage->Header.u.v0.uuidLinkage = *pUuid;
2306 else
2307 {
2308 LogFunc(("Version is not supported!\n"));
2309 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2310 }
2311 }
2312 else
2313 rc = VERR_VD_IMAGE_READ_ONLY;
2314 }
2315 else
2316 rc = VERR_VD_NOT_OPENED;
2317
2318 LogFlowFunc(("returns %Rrc\n", rc));
2319 return rc;
2320}
2321
2322/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2323static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2324{
2325 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2326 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2327 int rc;
2328
2329 AssertPtr(pImage);
2330
2331 if (pImage)
2332 {
2333 *pUuid = *getImageParentModificationUUID(&pImage->Header);
2334 rc = VINF_SUCCESS;
2335 }
2336 else
2337 rc = VERR_VD_NOT_OPENED;
2338
2339 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2340 return rc;
2341}
2342
2343/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2344static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2345{
2346 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2347 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2348 int rc = VINF_SUCCESS;
2349
2350 AssertPtr(pImage);
2351
2352 if (pImage)
2353 {
2354 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2355 {
2356 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2357 pImage->Header.u.v1.uuidParentModify = *pUuid;
2358 else
2359 {
2360 LogFunc(("Version is not supported!\n"));
2361 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2362 }
2363 }
2364 else
2365 rc = VERR_VD_IMAGE_READ_ONLY;
2366 }
2367 else
2368 rc = VERR_VD_NOT_OPENED;
2369
2370 LogFlowFunc(("returns %Rrc\n", rc));
2371 return rc;
2372}
2373
2374/** @copydoc VBOXHDDBACKEND::pfnDump */
2375static void vdiDump(void *pBackendData)
2376{
2377 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2378
2379 vdIfErrorMessage(pImage->pIfError, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
2380 pImage->pszFilename,
2381 (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
2382 pImage->uOpenFlags,
2383 pImage->pStorage);
2384 vdIfErrorMessage(pImage->pIfError, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
2385 pImage->PreHeader.u32Version,
2386 getImageType(&pImage->Header),
2387 getImageFlags(&pImage->Header),
2388 getImageDiskSize(&pImage->Header));
2389 vdIfErrorMessage(pImage->pIfError, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
2390 getImageBlockSize(&pImage->Header),
2391 getImageExtraBlockSize(&pImage->Header),
2392 getImageBlocks(&pImage->Header),
2393 getImageBlocksAllocated(&pImage->Header));
2394 vdIfErrorMessage(pImage->pIfError, "Header: offBlocks=%u offData=%u\n",
2395 getImageBlocksOffset(&pImage->Header),
2396 getImageDataOffset(&pImage->Header));
2397 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
2398 if (pg)
2399 vdIfErrorMessage(pImage->pIfError, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
2400 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
2401 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
2402 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
2403 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
2404 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
2405 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
2406 vdIfErrorMessage(pImage->pIfError, "Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
2407 pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
2408 vdIfErrorMessage(pImage->pIfError, "Image: uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
2409 pImage->uBlockMask,
2410 pImage->cbTotalBlockData,
2411 pImage->uShiftOffset2Index,
2412 pImage->offStartBlockData);
2413
2414 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
2415 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
2416 {
2417 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2418 {
2419 cBlocksNotFree++;
2420 if (pImage->paBlocks[uBlock] >= cBlocks)
2421 cBadBlocks++;
2422 }
2423 }
2424 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
2425 {
2426 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
2427 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
2428 }
2429 if (cBadBlocks)
2430 {
2431 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u bad blocks found !!\n",
2432 cBadBlocks);
2433 }
2434}
2435
2436static int vdiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
2437 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
2438{
2439 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
2440 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
2441 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2442 unsigned uBlock;
2443 unsigned offRead;
2444 int rc;
2445
2446 AssertPtr(pImage);
2447 Assert(!(uOffset % 512));
2448 Assert(!(cbToRead % 512));
2449
2450 if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
2451 || !VALID_PTR(pIoCtx)
2452 || !cbToRead)
2453 {
2454 rc = VERR_INVALID_PARAMETER;
2455 goto out;
2456 }
2457
2458 /* Calculate starting block number and offset inside it. */
2459 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2460 offRead = (unsigned)uOffset & pImage->uBlockMask;
2461
2462 /* Clip read range to at most the rest of the block. */
2463 cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
2464 Assert(!(cbToRead % 512));
2465
2466 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
2467 rc = VERR_VD_BLOCK_FREE;
2468 else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
2469 {
2470 size_t cbSet;
2471
2472 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
2473 Assert(cbSet == cbToRead);
2474
2475 rc = VINF_SUCCESS;
2476 }
2477 else
2478 {
2479 /* Block present in image file, read relevant data. */
2480 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
2481 + (pImage->offStartData + pImage->offStartBlockData + offRead);
2482
2483 if (u64Offset + cbToRead <= pImage->cbImage)
2484 rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
2485 pIoCtx, cbToRead);
2486 else
2487 {
2488 LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
2489 u64Offset, pImage->pszFilename, pImage->cbImage));
2490 vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
2491 rc = VERR_VD_READ_OUT_OF_RANGE;
2492 }
2493 }
2494
2495 if (pcbActuallyRead)
2496 *pcbActuallyRead = cbToRead;
2497
2498out:
2499 LogFlowFunc(("returns %Rrc\n", rc));
2500 return rc;
2501}
2502
2503static int vdiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
2504 PVDIOCTX pIoCtx,
2505 size_t *pcbWriteProcess, size_t *pcbPreRead,
2506 size_t *pcbPostRead, unsigned fWrite)
2507{
2508 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
2509 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
2510 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2511 unsigned uBlock;
2512 unsigned offWrite;
2513 int rc = VINF_SUCCESS;
2514
2515 AssertPtr(pImage);
2516 Assert(!(uOffset % 512));
2517 Assert(!(cbToWrite % 512));
2518
2519 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2520 {
2521 rc = VERR_VD_IMAGE_READ_ONLY;
2522 goto out;
2523 }
2524
2525 if (!VALID_PTR(pIoCtx) || !cbToWrite)
2526 {
2527 rc = VERR_INVALID_PARAMETER;
2528 goto out;
2529 }
2530
2531 /* No size check here, will do that later. For dynamic images which are
2532 * not multiples of the block size in length, this would prevent writing to
2533 * the last block. */
2534
2535 /* Calculate starting block number and offset inside it. */
2536 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2537 offWrite = (unsigned)uOffset & pImage->uBlockMask;
2538
2539 /* Clip write range to at most the rest of the block. */
2540 cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
2541 Assert(!(cbToWrite % 512));
2542
2543 do
2544 {
2545 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2546 {
2547 /* Block is either free or zero. */
2548 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
2549 && ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
2550 || cbToWrite == getImageBlockSize(&pImage->Header)))
2551 {
2552#if 0 /** @todo Provide interface to check an I/O context for a specific value */
2553 /* If the destination block is unallocated at this point, it's
2554 * either a zero block or a block which hasn't been used so far
2555 * (which also means that it's a zero block. Don't need to write
2556 * anything to this block if the data consists of just zeroes. */
2557 Assert(!(cbToWrite % 4));
2558 Assert(cbToWrite * 8 <= UINT32_MAX);
2559 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
2560 {
2561 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
2562 break;
2563 }
2564#endif
2565 }
2566
2567 if ( cbToWrite == getImageBlockSize(&pImage->Header)
2568 && !(fWrite & VD_WRITE_NO_ALLOC))
2569 {
2570 /* Full block write to previously unallocated block.
2571 * Allocate block and write data. */
2572 Assert(!offWrite);
2573 PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)RTMemAllocZ(sizeof(VDIASYNCBLOCKALLOC));
2574 if (!pBlockAlloc)
2575 {
2576 rc = VERR_NO_MEMORY;
2577 break;
2578 }
2579
2580 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
2581 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
2582 + (pImage->offStartData + pImage->offStartBlockData);
2583
2584 pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
2585 pBlockAlloc->uBlock = uBlock;
2586
2587 *pcbPreRead = 0;
2588 *pcbPostRead = 0;
2589
2590 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2591 u64Offset, pIoCtx, cbToWrite,
2592 vdiAsyncBlockAllocUpdate, pBlockAlloc);
2593 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2594 break;
2595 else if (RT_FAILURE(rc))
2596 {
2597 RTMemFree(pBlockAlloc);
2598 break;
2599 }
2600
2601 rc = vdiAsyncBlockAllocUpdate(pImage, pIoCtx, pBlockAlloc, rc);
2602 }
2603 else
2604 {
2605 /* Trying to do a partial write to an unallocated block. Don't do
2606 * anything except letting the upper layer know what to do. */
2607 *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
2608 *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
2609 rc = VERR_VD_BLOCK_FREE;
2610 }
2611 }
2612 else
2613 {
2614 /* Block present in image file, write relevant data. */
2615 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
2616 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
2617 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2618 u64Offset, pIoCtx, cbToWrite, NULL, NULL);
2619 }
2620 } while (0);
2621
2622 if (pcbWriteProcess)
2623 *pcbWriteProcess = cbToWrite;
2624
2625out:
2626 LogFlowFunc(("returns %Rrc\n", rc));
2627 return rc;
2628}
2629
2630static int vdiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
2631{
2632 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2633 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2634 int rc = VINF_SUCCESS;
2635
2636 Assert(pImage);
2637
2638 rc = vdiFlushImageAsync(pImage, pIoCtx);
2639 LogFlowFunc(("returns %Rrc\n", rc));
2640 return rc;
2641}
2642
2643/** @copydoc VBOXHDDBACKEND::pfnCompact */
2644static int vdiCompact(void *pBackendData, unsigned uPercentStart,
2645 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2646 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2647{
2648 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2649 int rc = VINF_SUCCESS;
2650 void *pvBuf = NULL, *pvTmp = NULL;
2651 unsigned *paBlocks2 = NULL;
2652
2653 int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2654 void *pvParent = NULL;
2655 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2656 if (pIfParentState)
2657 {
2658 pfnParentRead = pIfParentState->pfnParentRead;
2659 pvParent = pIfParentState->Core.pvUser;
2660 }
2661
2662 PFNVDPROGRESS pfnProgress = NULL;
2663 void *pvUser = NULL;
2664 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2665
2666 PVDINTERFACEQUERYRANGEUSE pIfQueryRangeUse = VDIfQueryRangeUseGet(pVDIfsOperation);
2667
2668 do {
2669 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2670
2671 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2672 rc = VERR_VD_IMAGE_READ_ONLY);
2673
2674 unsigned cBlocks;
2675 unsigned cBlocksToMove = 0;
2676 size_t cbBlock;
2677 cBlocks = getImageBlocks(&pImage->Header);
2678 cbBlock = getImageBlockSize(&pImage->Header);
2679 if (pfnParentRead)
2680 {
2681 pvBuf = RTMemTmpAlloc(cbBlock);
2682 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2683 }
2684 pvTmp = RTMemTmpAlloc(cbBlock);
2685 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
2686
2687 uint64_t cbFile;
2688 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2689 AssertRCBreak(rc);
2690 unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
2691 if (cBlocksAllocated == 0)
2692 {
2693 /* No data blocks in this image, no need to compact. */
2694 rc = VINF_SUCCESS;
2695 break;
2696 }
2697
2698 /* Allocate block array for back resolving. */
2699 paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
2700 AssertBreakStmt(VALID_PTR(paBlocks2), rc = VERR_NO_MEMORY);
2701 /* Fill out back resolving, check/fix allocation errors before
2702 * compacting the image, just to be on the safe side. Update the
2703 * image contents straight away, as this enables cancelling. */
2704 for (unsigned i = 0; i < cBlocksAllocated; i++)
2705 paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
2706 rc = VINF_SUCCESS;
2707 for (unsigned i = 0; i < cBlocks; i++)
2708 {
2709 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2710 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2711 {
2712 if (ptrBlock < cBlocksAllocated)
2713 {
2714 if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
2715 paBlocks2[ptrBlock] = i;
2716 else
2717 {
2718 LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
2719 i, pImage->pszFilename));
2720 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2721 rc = vdiUpdateBlockInfo(pImage, i);
2722 if (RT_FAILURE(rc))
2723 break;
2724 }
2725 }
2726 else
2727 {
2728 LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
2729 i, pImage->pszFilename));
2730 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2731 rc = vdiUpdateBlockInfo(pImage, i);
2732 if (RT_FAILURE(rc))
2733 break;
2734 }
2735 }
2736 }
2737 if (RT_FAILURE(rc))
2738 break;
2739
2740 /* Find redundant information and update the block pointers
2741 * accordingly, creating bubbles. Keep disk up to date, as this
2742 * enables cancelling. */
2743 for (unsigned i = 0; i < cBlocks; i++)
2744 {
2745 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2746 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2747 {
2748 /* Block present in image file, read relevant data. */
2749 uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
2750 + (pImage->offStartData + pImage->offStartBlockData);
2751 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock, NULL);
2752 if (RT_FAILURE(rc))
2753 break;
2754
2755 if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
2756 {
2757 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2758 rc = vdiUpdateBlockInfo(pImage, i);
2759 if (RT_FAILURE(rc))
2760 break;
2761 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2762 /* Adjust progress info, one block to be relocated. */
2763 cBlocksToMove++;
2764 }
2765 else if (pfnParentRead)
2766 {
2767 rc = pfnParentRead(pvParent, (uint64_t)i * cbBlock, pvBuf, cbBlock);
2768 if (RT_FAILURE(rc))
2769 break;
2770 if (!memcmp(pvTmp, pvBuf, cbBlock))
2771 {
2772 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2773 rc = vdiUpdateBlockInfo(pImage, i);
2774 if (RT_FAILURE(rc))
2775 break;
2776 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2777 /* Adjust progress info, one block to be relocated. */
2778 cBlocksToMove++;
2779 }
2780 }
2781 }
2782
2783 /* Check if the range is in use if the block is still allocated. */
2784 ptrBlock = pImage->paBlocks[i];
2785 if ( IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock)
2786 && pIfQueryRangeUse)
2787 {
2788 bool fUsed = true;
2789
2790 rc = vdIfQueryRangeUse(pIfQueryRangeUse, (uint64_t)i * cbBlock, cbBlock, &fUsed);
2791 if (RT_FAILURE(rc))
2792 break;
2793 if (!fUsed)
2794 {
2795 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2796 rc = vdiUpdateBlockInfo(pImage, i);
2797 if (RT_FAILURE(rc))
2798 break;
2799 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2800 /* Adjust progress info, one block to be relocated. */
2801 cBlocksToMove++;
2802 }
2803 }
2804
2805 if (pIfProgress && pIfProgress->pfnProgress)
2806 {
2807 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2808 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2809 if (RT_FAILURE(rc))
2810 break;
2811 }
2812 }
2813 if (RT_FAILURE(rc))
2814 break;
2815
2816 /* Fill bubbles with other data (if available). */
2817 unsigned cBlocksMoved = 0;
2818 unsigned uBlockUsedPos = cBlocksAllocated;
2819 for (unsigned i = 0; i < cBlocksAllocated; i++)
2820 {
2821 unsigned uBlock = paBlocks2[i];
2822 if (uBlock == VDI_IMAGE_BLOCK_FREE)
2823 {
2824 unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
2825 while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
2826 {
2827 uBlockUsedPos--;
2828 uBlockData = paBlocks2[uBlockUsedPos];
2829 }
2830 /* Terminate early if there is no block which needs copying. */
2831 if (uBlockUsedPos == i)
2832 break;
2833 uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2834 + (pImage->offStartData + pImage->offStartBlockData);
2835 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2836 pvTmp, cbBlock, NULL);
2837 u64Offset = (uint64_t)i * pImage->cbTotalBlockData
2838 + (pImage->offStartData + pImage->offStartBlockData);
2839 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2840 pvTmp, cbBlock, NULL);
2841 pImage->paBlocks[uBlockData] = i;
2842 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
2843 rc = vdiUpdateBlockInfo(pImage, uBlockData);
2844 if (RT_FAILURE(rc))
2845 break;
2846 paBlocks2[i] = uBlockData;
2847 paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
2848 cBlocksMoved++;
2849 }
2850
2851 if (pIfProgress && pIfProgress->pfnProgress)
2852 {
2853 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2854 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2855
2856 if (RT_FAILURE(rc))
2857 break;
2858 }
2859 }
2860 if (RT_FAILURE(rc))
2861 break;
2862
2863 /* Update image header. */
2864 setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
2865 vdiUpdateHeader(pImage);
2866
2867 /* Truncate the image to the proper size to finish compacting. */
2868 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2869 (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2870 + pImage->offStartData + pImage->offStartBlockData);
2871 } while (0);
2872
2873 if (paBlocks2)
2874 RTMemTmpFree(paBlocks2);
2875 if (pvTmp)
2876 RTMemTmpFree(pvTmp);
2877 if (pvBuf)
2878 RTMemTmpFree(pvBuf);
2879
2880 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2881 {
2882 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2883 uPercentStart + uPercentSpan);
2884 }
2885
2886 LogFlowFunc(("returns %Rrc\n", rc));
2887 return rc;
2888}
2889
2890
2891/** @copydoc VBOXHDDBACKEND::pfnResize */
2892static int vdiResize(void *pBackendData, uint64_t cbSize,
2893 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2894 unsigned uPercentStart, unsigned uPercentSpan,
2895 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2896 PVDINTERFACE pVDIfsOperation)
2897{
2898 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2899 int rc = VINF_SUCCESS;
2900
2901 PFNVDPROGRESS pfnProgress = NULL;
2902 void *pvUser = NULL;
2903 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2904
2905 /*
2906 * Making the image smaller is not supported at the moment.
2907 * Resizing is also not supported for fixed size images and
2908 * very old images.
2909 */
2910 /** @todo implement making the image smaller, it is the responsibility of
2911 * the user to know what he's doing. */
2912 if ( cbSize < getImageDiskSize(&pImage->Header)
2913 || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2914 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2915 rc = VERR_NOT_SUPPORTED;
2916 else if (cbSize > getImageDiskSize(&pImage->Header))
2917 {
2918 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
2919 uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header); /** < New number of blocks in the image after the resize */
2920 if (cbSize % getImageBlockSize(&pImage->Header))
2921 cBlocksNew++;
2922
2923 uint32_t cBlocksOld = getImageBlocks(&pImage->Header); /** < Number of blocks before the resize. */
2924 uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
2925 uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
2926
2927 if ( pImage->offStartData != offStartDataNew
2928 && cBlocksAllocated > 0)
2929 {
2930 /* Calculate how many sectors need to be relocated. */
2931 uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
2932 unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
2933 if (cbOverlapping % getImageBlockSize(&pImage->Header))
2934 cBlocksReloc++;
2935
2936 /* Since only full blocks can be relocated the new data start is
2937 * determined by moving it block by block. */
2938 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2939 offStartDataNew = pImage->offStartData;
2940
2941 /* Do the relocation. */
2942 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2943
2944 /*
2945 * Get the blocks we need to relocate first, they are appended to the end
2946 * of the image.
2947 */
2948 void *pvBuf = NULL, *pvZero = NULL;
2949 do
2950 {
2951 VDIIMAGEBLOCKPOINTER uBlock = 0;
2952
2953 /* Allocate data buffer. */
2954 pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
2955 if (!pvBuf)
2956 {
2957 rc = VERR_NO_MEMORY;
2958 break;
2959 }
2960
2961 /* Allocate buffer for overwriting with zeroes. */
2962 pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
2963 if (!pvZero)
2964 {
2965 rc = VERR_NO_MEMORY;
2966 break;
2967 }
2968
2969 for (unsigned i = 0; i < cBlocksReloc; i++)
2970 {
2971 /* Search the index in the block table. */
2972 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2973 {
2974 if (pImage->paBlocks[idxBlock] == uBlock)
2975 {
2976 /* Read data and append to the end of the image. */
2977 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2978 offStartDataNew, pvBuf,
2979 pImage->cbTotalBlockData, NULL);
2980 if (RT_FAILURE(rc))
2981 break;
2982
2983 uint64_t offBlockAppend;
2984 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &offBlockAppend);
2985 if (RT_FAILURE(rc))
2986 break;
2987
2988 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2989 offBlockAppend, pvBuf,
2990 pImage->cbTotalBlockData, NULL);
2991 if (RT_FAILURE(rc))
2992 break;
2993
2994 /* Zero out the old block area. */
2995 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2996 offStartDataNew, pvZero,
2997 pImage->cbTotalBlockData, NULL);
2998 if (RT_FAILURE(rc))
2999 break;
3000
3001 /* Update block counter. */
3002 pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
3003
3004 /*
3005 * Decrease the block number of all other entries in the array.
3006 * They were moved one block to the front.
3007 * Doing it as a separate step iterating over the array again
3008 * because an error while relocating the block might end up
3009 * in a corrupted image otherwise.
3010 */
3011 for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
3012 {
3013 if ( idxBlock2 != idxBlock
3014 && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
3015 pImage->paBlocks[idxBlock2]--;
3016 }
3017
3018 /* Continue with the next block. */
3019 break;
3020 }
3021 }
3022
3023 if (RT_FAILURE(rc))
3024 break;
3025
3026 uBlock++;
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
3076 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
3077 if (pGeometry)
3078 {
3079 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
3080 pGeometry->cHeads = pLCHSGeometry->cHeads;
3081 pGeometry->cSectors = pLCHSGeometry->cSectors;
3082 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
3083 }
3084 }
3085 }
3086
3087 /* Update header information in base image file. */
3088 vdiFlushImage(pImage);
3089 }
3090 /* Same size doesn't change the image at all. */
3091
3092 LogFlowFunc(("returns %Rrc\n", rc));
3093 return rc;
3094}
3095
3096
3097/** @copydoc VBOXHDDBACKEND::pfnDiscard */
3098static DECLCALLBACK(int) vdiDiscard(void *pBackendData,
3099 uint64_t uOffset, size_t cbDiscard,
3100 size_t *pcbPreAllocated,
3101 size_t *pcbPostAllocated,
3102 size_t *pcbActuallyDiscarded,
3103 void **ppbmAllocationBitmap,
3104 unsigned fDiscard)
3105{
3106 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
3107 unsigned uBlock;
3108 unsigned offDiscard;
3109 int rc = VINF_SUCCESS;
3110 void *pvBlock = NULL;
3111
3112 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
3113 pBackendData, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
3114
3115 AssertPtr(pImage);
3116 Assert(!(uOffset % 512));
3117 Assert(!(cbDiscard % 512));
3118
3119 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
3120 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
3121 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
3122 && cbDiscard,
3123 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
3124 uOffset, cbDiscard),
3125 VERR_INVALID_PARAMETER);
3126
3127 do
3128 {
3129 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
3130 ("Image is opened readonly\n"),
3131 rc = VERR_VD_IMAGE_READ_ONLY);
3132
3133 AssertMsgBreakStmt(cbDiscard,
3134 ("cbDiscard=%u\n", cbDiscard),
3135 rc = VERR_INVALID_PARAMETER);
3136
3137 /* Calculate starting block number and offset inside it. */
3138 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
3139 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
3140
3141 /* Clip range to at most the rest of the block. */
3142 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
3143 Assert(!(cbDiscard % 512));
3144
3145 if (pcbPreAllocated)
3146 *pcbPreAllocated = 0;
3147
3148 if (pcbPostAllocated)
3149 *pcbPostAllocated = 0;
3150
3151 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
3152 {
3153 uint8_t *pbBlockData;
3154 size_t cbPreAllocated, cbPostAllocated;
3155
3156 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
3157 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
3158
3159 /* Read the block data. */
3160 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
3161 if (!pvBlock)
3162 {
3163 rc = VERR_NO_MEMORY;
3164 break;
3165 }
3166
3167 if (!cbPreAllocated && !cbPostAllocated)
3168 {
3169 /*
3170 * Discarding a whole block, don't check for allocated sectors.
3171 * It is possible to just remove the whole block which avoids
3172 * one read and checking the whole block for data.
3173 */
3174 rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
3175 }
3176 else
3177 {
3178 /* Read data. */
3179 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
3180
3181 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
3182 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
3183 pvBlock, pImage->cbTotalBlockData, NULL);
3184 if (RT_FAILURE(rc))
3185 break;
3186
3187 /* Clear data. */
3188 memset(pbBlockData + offDiscard , 0, cbDiscard);
3189
3190 Assert(!(cbDiscard % 4));
3191 Assert(cbDiscard * 8 <= UINT32_MAX);
3192 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
3193 rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
3194 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
3195 {
3196 /* Write changed data to the image. */
3197 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset + offDiscard,
3198 pbBlockData + offDiscard, cbDiscard, NULL);
3199 }
3200 else
3201 {
3202 /* Block has data, create allocation bitmap. */
3203 *pcbPreAllocated = cbPreAllocated;
3204 *pcbPostAllocated = cbPostAllocated;
3205 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
3206 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
3207 rc = VERR_NO_MEMORY;
3208 else
3209 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
3210 }
3211 } /* if: no complete block discarded */
3212 } /* if: Block is allocated. */
3213 /* else: nothing to do. */
3214 } while (0);
3215
3216 if (pcbActuallyDiscarded)
3217 *pcbActuallyDiscarded = cbDiscard;
3218
3219 if (pvBlock)
3220 RTMemFree(pvBlock);
3221
3222 LogFlowFunc(("returns %Rrc\n", rc));
3223 return rc;
3224}
3225
3226/** @copydoc VBOXHDDBACKEND::pfnDiscard */
3227static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
3228 uint64_t uOffset, size_t cbDiscard,
3229 size_t *pcbPreAllocated,
3230 size_t *pcbPostAllocated,
3231 size_t *pcbActuallyDiscarded,
3232 void **ppbmAllocationBitmap,
3233 unsigned fDiscard)
3234{
3235 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
3236 unsigned uBlock;
3237 unsigned offDiscard;
3238 int rc = VINF_SUCCESS;
3239 void *pvBlock = NULL;
3240
3241 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
3242 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
3243
3244 AssertPtr(pImage);
3245 Assert(!(uOffset % 512));
3246 Assert(!(cbDiscard % 512));
3247
3248 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
3249 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
3250 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
3251 && cbDiscard,
3252 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
3253 uOffset, cbDiscard),
3254 VERR_INVALID_PARAMETER);
3255
3256 do
3257 {
3258 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
3259 ("Image is opened readonly\n"),
3260 rc = VERR_VD_IMAGE_READ_ONLY);
3261
3262 AssertMsgBreakStmt(cbDiscard,
3263 ("cbDiscard=%u\n", cbDiscard),
3264 rc = VERR_INVALID_PARAMETER);
3265
3266 /* Calculate starting block number and offset inside it. */
3267 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
3268 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
3269
3270 /* Clip range to at most the rest of the block. */
3271 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
3272 Assert(!(cbDiscard % 512));
3273
3274 if (pcbPreAllocated)
3275 *pcbPreAllocated = 0;
3276
3277 if (pcbPostAllocated)
3278 *pcbPostAllocated = 0;
3279
3280 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
3281 {
3282 uint8_t *pbBlockData;
3283 size_t cbPreAllocated, cbPostAllocated;
3284
3285 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
3286 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
3287
3288 /* Read the block data. */
3289 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
3290 if (!pvBlock)
3291 {
3292 rc = VERR_NO_MEMORY;
3293 break;
3294 }
3295
3296 if (!cbPreAllocated && !cbPostAllocated)
3297 {
3298 /*
3299 * Discarding a whole block, don't check for allocated sectors.
3300 * It is possible to just remove the whole block which avoids
3301 * one read and checking the whole block for data.
3302 */
3303 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
3304 }
3305 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
3306 {
3307 /* Just zero out the given range. */
3308 memset(pvBlock, 0, cbDiscard);
3309
3310 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
3311 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
3312 u64Offset, pvBlock, cbDiscard, pIoCtx,
3313 NULL, NULL);
3314 RTMemFree(pvBlock);
3315 }
3316 else
3317 {
3318 /*
3319 * Read complete block as metadata, the I/O context has no memory buffer
3320 * and we need to access the content directly anyway.
3321 */
3322 PVDMETAXFER pMetaXfer;
3323 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
3324
3325 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
3326 rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
3327 pbBlockData, pImage->cbTotalBlockData,
3328 pIoCtx, &pMetaXfer, NULL, NULL);
3329 if (RT_FAILURE(rc))
3330 {
3331 RTMemFree(pvBlock);
3332 break;
3333 }
3334
3335 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
3336
3337 /* Clear data. */
3338 memset(pbBlockData + offDiscard , 0, cbDiscard);
3339
3340 Assert(!(cbDiscard % 4));
3341 Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
3342 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
3343 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
3344 else
3345 {
3346 /* Block has data, create allocation bitmap. */
3347 *pcbPreAllocated = cbPreAllocated;
3348 *pcbPostAllocated = cbPostAllocated;
3349 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
3350 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
3351 rc = VERR_NO_MEMORY;
3352 else
3353 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
3354
3355 RTMemFree(pvBlock);
3356 }
3357 } /* if: no complete block discarded */
3358 } /* if: Block is allocated. */
3359 /* else: nothing to do. */
3360 } while (0);
3361
3362 if (pcbActuallyDiscarded)
3363 *pcbActuallyDiscarded = cbDiscard;
3364
3365 LogFlowFunc(("returns %Rrc\n", rc));
3366 return rc;
3367}
3368
3369/** @copydoc VBOXHDDBACKEND::pfnRepair */
3370static DECLCALLBACK(int) vdiRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
3371 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
3372{
3373 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
3374 int rc;
3375 PVDINTERFACEERROR pIfError;
3376 PVDINTERFACEIOINT pIfIo;
3377 PVDIOSTORAGE pStorage;
3378 uint64_t cbFile;
3379 PVDIIMAGEBLOCKPOINTER paBlocks = NULL;
3380 uint32_t *pu32BlockBitmap = NULL;
3381 VDIPREHEADER PreHdr;
3382 VDIHEADER Hdr;
3383
3384 pIfIo = VDIfIoIntGet(pVDIfsImage);
3385 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
3386
3387 pIfError = VDIfErrorGet(pVDIfsDisk);
3388
3389 do
3390 {
3391 bool fRepairHdr = false;
3392 bool fRepairBlockArray = false;
3393
3394 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
3395 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
3396 ? VD_OPEN_FLAGS_READONLY
3397 : 0,
3398 false /* fCreate */),
3399 &pStorage);
3400 if (RT_FAILURE(rc))
3401 {
3402 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to open image \"%s\"", pszFilename);
3403 break;
3404 }
3405
3406 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
3407 if (RT_FAILURE(rc))
3408 {
3409 rc = vdIfError(pIfError, rc, RT_SRC_POS, "VDI: Failed to query image size");
3410 break;
3411 }
3412
3413 /* Read pre-header. */
3414 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &PreHdr, sizeof(PreHdr), NULL);
3415 if (RT_FAILURE(rc))
3416 {
3417 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: Error reading pre-header in '%s'"), pszFilename);
3418 break;
3419 }
3420 vdiConvPreHeaderEndianess(VDIECONV_F2H, &PreHdr, &PreHdr);
3421 rc = vdiValidatePreHeader(&PreHdr);
3422 if (RT_FAILURE(rc))
3423 {
3424 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3425 N_("VDI: invalid pre-header in '%s'"), pszFilename);
3426 break;
3427 }
3428
3429 /* Read header. */
3430 Hdr.uVersion = PreHdr.u32Version;
3431 switch (GET_MAJOR_HEADER_VERSION(&Hdr))
3432 {
3433 case 0:
3434 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3435 &Hdr.u.v0, sizeof(Hdr.u.v0),
3436 NULL);
3437 if (RT_FAILURE(rc))
3438 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"),
3439 pszFilename);
3440 vdiConvHeaderEndianessV0(VDIECONV_F2H, &Hdr.u.v0, &Hdr.u.v0);
3441 break;
3442 case 1:
3443 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3444 &Hdr.u.v1, sizeof(Hdr.u.v1), NULL);
3445 if (RT_FAILURE(rc))
3446 {
3447 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"),
3448 pszFilename);
3449 }
3450 vdiConvHeaderEndianessV1(VDIECONV_F2H, &Hdr.u.v1, &Hdr.u.v1);
3451 if (Hdr.u.v1.cbHeader >= sizeof(Hdr.u.v1plus))
3452 {
3453 /* Read the VDI 1.1+ header completely. */
3454 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, sizeof(PreHdr),
3455 &Hdr.u.v1plus, sizeof(Hdr.u.v1plus),
3456 NULL);
3457 if (RT_FAILURE(rc))
3458 rc = vdIfError(pIfError, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"),
3459 pszFilename);
3460 vdiConvHeaderEndianessV1p(VDIECONV_F2H, &Hdr.u.v1plus, &Hdr.u.v1plus);
3461 }
3462 break;
3463 default:
3464 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3465 N_("VDI: unsupported major version %u in '%s'"),
3466 GET_MAJOR_HEADER_VERSION(&Hdr), pszFilename);
3467 break;
3468 }
3469
3470 if (RT_SUCCESS(rc))
3471 {
3472 rc = vdiValidateHeader(&Hdr);
3473 if (RT_FAILURE(rc))
3474 {
3475 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3476 N_("VDI: invalid header in '%s'"), pszFilename);
3477 break;
3478 }
3479 }
3480
3481 /* Setup image parameters by header. */
3482 uint64_t offStartBlocks, offStartData;
3483 size_t cbTotalBlockData;
3484
3485 offStartBlocks = getImageBlocksOffset(&Hdr);
3486 offStartData = getImageDataOffset(&Hdr);
3487 cbTotalBlockData = getImageExtraBlockSize(&Hdr) + getImageBlockSize(&Hdr);
3488
3489 /* Allocate memory for blocks array. */
3490 paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&Hdr));
3491 if (!paBlocks)
3492 {
3493 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3494 "Failed to allocate memory for block array");
3495 break;
3496 }
3497
3498 /* Read blocks array. */
3499 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3500 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER),
3501 NULL);
3502 if (RT_FAILURE(rc))
3503 {
3504 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3505 "Failed to read block array (at %llu), %Rrc",
3506 offStartBlocks, rc);
3507 break;
3508 }
3509 vdiConvBlocksEndianess(VDIECONV_F2H, paBlocks, getImageBlocks(&Hdr));
3510
3511 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(getImageBlocks(&Hdr) / 8, 4));
3512 if (!pu32BlockBitmap)
3513 {
3514 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3515 "Failed to allocate memory for block bitmap");
3516 break;
3517 }
3518
3519 for (uint32_t i = 0; i < getImageBlocks(&Hdr); i++)
3520 {
3521 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(paBlocks[i]))
3522 {
3523 uint64_t offBlock = (uint64_t)paBlocks[i] * cbTotalBlockData
3524 + offStartData;
3525
3526 /*
3527 * Check that the offsets are valid (inside of the image) and
3528 * that there are no double references.
3529 */
3530 if (offBlock + cbTotalBlockData > cbFile)
3531 {
3532 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
3533 i, offBlock);
3534 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3535 fRepairBlockArray = true;
3536 }
3537 else if (ASMBitTestAndSet(pu32BlockBitmap, paBlocks[i]))
3538 {
3539 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
3540 i);
3541 paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
3542 fRepairBlockArray = true;
3543 }
3544 }
3545 }
3546
3547 /* Write repaired structures now. */
3548 if (!fRepairBlockArray)
3549 vdIfErrorMessage(pIfError, "VDI image is in a consistent state, no repair required\n");
3550 else if (!(fFlags & VD_REPAIR_DRY_RUN))
3551 {
3552 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
3553
3554 vdiConvBlocksEndianess(VDIECONV_H2F, paBlocks, getImageBlocks(&Hdr));
3555 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offStartBlocks, paBlocks,
3556 getImageBlocks(&Hdr) * sizeof(VDIIMAGEBLOCKPOINTER),
3557 NULL);
3558 if (RT_FAILURE(rc))
3559 {
3560 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3561 "Could not write repaired block allocation table (at %llu), %Rrc",
3562 offStartBlocks, rc);
3563 break;
3564 }
3565 }
3566
3567 vdIfErrorMessage(pIfError, "Corrupted VDI image repaired successfully\n");
3568 } while(0);
3569
3570 if (paBlocks)
3571 RTMemFree(paBlocks);
3572
3573 if (pu32BlockBitmap)
3574 RTMemFree(pu32BlockBitmap);
3575
3576 if (pStorage)
3577 vdIfIoIntFileClose(pIfIo, pStorage);
3578
3579 LogFlowFunc(("returns %Rrc\n", rc));
3580 return rc;
3581}
3582
3583VBOXHDDBACKEND g_VDIBackend =
3584{
3585 /* pszBackendName */
3586 "VDI",
3587 /* cbSize */
3588 sizeof(VBOXHDDBACKEND),
3589 /* uBackendCaps */
3590 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
3591 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_DISCARD,
3592 /* paFileExtensions */
3593 s_aVdiFileExtensions,
3594 /* paConfigInfo */
3595 NULL,
3596 /* hPlugin */
3597 NIL_RTLDRMOD,
3598 /* pfnCheckIfValid */
3599 vdiCheckIfValid,
3600 /* pfnOpen */
3601 vdiOpen,
3602 /* pfnCreate */
3603 vdiCreate,
3604 /* pfnRename */
3605 vdiRename,
3606 /* pfnClose */
3607 vdiClose,
3608 /* pfnRead */
3609 vdiRead,
3610 /* pfnWrite */
3611 vdiWrite,
3612 /* pfnFlush */
3613 vdiFlush,
3614 /* pfnGetVersion */
3615 vdiGetVersion,
3616 /* pfnGetSize */
3617 vdiGetSize,
3618 /* pfnGetFileSize */
3619 vdiGetFileSize,
3620 /* pfnGetPCHSGeometry */
3621 vdiGetPCHSGeometry,
3622 /* pfnSetPCHSGeometry */
3623 vdiSetPCHSGeometry,
3624 /* pfnGetLCHSGeometry */
3625 vdiGetLCHSGeometry,
3626 /* pfnSetLCHSGeometry */
3627 vdiSetLCHSGeometry,
3628 /* pfnGetImageFlags */
3629 vdiGetImageFlags,
3630 /* pfnGetOpenFlags */
3631 vdiGetOpenFlags,
3632 /* pfnSetOpenFlags */
3633 vdiSetOpenFlags,
3634 /* pfnGetComment */
3635 vdiGetComment,
3636 /* pfnSetComment */
3637 vdiSetComment,
3638 /* pfnGetUuid */
3639 vdiGetUuid,
3640 /* pfnSetUuid */
3641 vdiSetUuid,
3642 /* pfnGetModificationUuid */
3643 vdiGetModificationUuid,
3644 /* pfnSetModificationUuid */
3645 vdiSetModificationUuid,
3646 /* pfnGetParentUuid */
3647 vdiGetParentUuid,
3648 /* pfnSetParentUuid */
3649 vdiSetParentUuid,
3650 /* pfnGetParentModificationUuid */
3651 vdiGetParentModificationUuid,
3652 /* pfnSetParentModificationUuid */
3653 vdiSetParentModificationUuid,
3654 /* pfnDump */
3655 vdiDump,
3656 /* pfnGetTimeStamp */
3657 NULL,
3658 /* pfnGetParentTimeStamp */
3659 NULL,
3660 /* pfnSetParentTimeStamp */
3661 NULL,
3662 /* pfnGetParentFilename */
3663 NULL,
3664 /* pfnSetParentFilename */
3665 NULL,
3666 /* pfnAsyncRead */
3667 vdiAsyncRead,
3668 /* pfnAsyncWrite */
3669 vdiAsyncWrite,
3670 /* pfnAsyncFlush */
3671 vdiAsyncFlush,
3672 /* pfnComposeLocation */
3673 genericFileComposeLocation,
3674 /* pfnComposeName */
3675 genericFileComposeName,
3676 /* pfnCompact */
3677 vdiCompact,
3678 /* pfnResize */
3679 vdiResize,
3680 /* pfnDiscard */
3681 vdiDiscard,
3682 /* pfnAsyncDiscard */
3683 vdiAsyncDiscard,
3684 /* pfnRepair */
3685 vdiRepair
3686};
Note: See TracBrowser for help on using the repository browser.

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