VirtualBox

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

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

Storage/VDI: Check for out of bounds access when reading from an image

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

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