VirtualBox

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

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

Storage/VDI: Convert block array from/to little endianness

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

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