VirtualBox

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

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

VDI: Fix uninitialized variables causing assertions in debug builds under certain circumstances (completely harmless)

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