VirtualBox

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

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

Storage/VDI: Plug memory leak in vdiAsyncDiscard

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