VirtualBox

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

Last change on this file since 40936 was 40936, checked in by vboxsync, 13 years ago

Storage/VDI: Commit again after fixing the uuid corruption issue

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