VirtualBox

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

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

Storage/VDI: Another small fix for newly created images, which lacked the file information string at the beginning. Harmless bug because the string is not used

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