VirtualBox

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

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

Storage: Add async discard API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 113.5 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 break;
1512 }
1513 }
1514
1515 if ( cbToWrite == getImageBlockSize(&pImage->Header)
1516 && !(fWrite & VD_WRITE_NO_ALLOC))
1517 {
1518 /* Full block write to previously unallocated block.
1519 * Allocate block and write data. */
1520 Assert(!offWrite);
1521 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1522 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
1523 + (pImage->offStartData + pImage->offStartBlockData);
1524 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
1525 u64Offset, pvBuf, cbToWrite, NULL);
1526 if (RT_FAILURE(rc))
1527 goto out;
1528 pImage->paBlocks[uBlock] = cBlocksAllocated;
1529
1530 if (pImage->paBlocksRev)
1531 pImage->paBlocksRev[cBlocksAllocated] = uBlock;
1532
1533 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
1534
1535 rc = vdiUpdateBlockInfo(pImage, uBlock);
1536 if (RT_FAILURE(rc))
1537 goto out;
1538
1539 *pcbPreRead = 0;
1540 *pcbPostRead = 0;
1541 }
1542 else
1543 {
1544 /* Trying to do a partial write to an unallocated block. Don't do
1545 * anything except letting the upper layer know what to do. */
1546 *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
1547 *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
1548 rc = VERR_VD_BLOCK_FREE;
1549 }
1550 }
1551 else
1552 {
1553 /* Block present in image file, write relevant data. */
1554 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
1555 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
1556 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
1557 pvBuf, cbToWrite, NULL);
1558 }
1559 } while (0);
1560
1561 if (pcbWriteProcess)
1562 *pcbWriteProcess = cbToWrite;
1563
1564out:
1565 LogFlowFunc(("returns %Rrc\n", rc));
1566 return rc;
1567}
1568
1569/** @copydoc VBOXHDDBACKEND::pfnFlush */
1570static int vdiFlush(void *pBackendData)
1571{
1572 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1573 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1574 int rc = VINF_SUCCESS;
1575
1576 Assert(pImage);
1577
1578 vdiFlushImage(pImage);
1579 LogFlowFunc(("returns %Rrc\n", rc));
1580 return rc;
1581}
1582
1583/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1584static unsigned vdiGetVersion(void *pBackendData)
1585{
1586 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1587 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1588 unsigned uVersion;
1589
1590 AssertPtr(pImage);
1591
1592 if (pImage)
1593 uVersion = pImage->PreHeader.u32Version;
1594 else
1595 uVersion = 0;
1596
1597 LogFlowFunc(("returns %#x\n", uVersion));
1598 return uVersion;
1599}
1600
1601/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1602static uint64_t vdiGetSize(void *pBackendData)
1603{
1604 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1605 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1606 uint64_t cbSize;
1607
1608 AssertPtr(pImage);
1609
1610 if (pImage)
1611 cbSize = getImageDiskSize(&pImage->Header);
1612 else
1613 cbSize = 0;
1614
1615 LogFlowFunc(("returns %llu\n", cbSize));
1616 return cbSize;
1617}
1618
1619/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1620static uint64_t vdiGetFileSize(void *pBackendData)
1621{
1622 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1623 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1624 uint64_t cb = 0;
1625
1626 AssertPtr(pImage);
1627
1628 if (pImage)
1629 {
1630 uint64_t cbFile;
1631 if (pImage->pStorage)
1632 {
1633 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1634 if (RT_SUCCESS(rc))
1635 cb += cbFile;
1636 }
1637 }
1638
1639 LogFlowFunc(("returns %lld\n", cb));
1640 return cb;
1641}
1642
1643/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1644static int vdiGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1645{
1646 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1647 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1648 int rc;
1649
1650 AssertPtr(pImage);
1651
1652 if (pImage)
1653 {
1654 if (pImage->PCHSGeometry.cCylinders)
1655 {
1656 *pPCHSGeometry = pImage->PCHSGeometry;
1657 rc = VINF_SUCCESS;
1658 }
1659 else
1660 rc = VERR_VD_GEOMETRY_NOT_SET;
1661 }
1662 else
1663 rc = VERR_VD_NOT_OPENED;
1664
1665 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1666 return rc;
1667}
1668
1669/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1670static int vdiSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1671{
1672 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1673 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1674 int rc;
1675
1676 AssertPtr(pImage);
1677
1678 if (pImage)
1679 {
1680 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1681 {
1682 rc = VERR_VD_IMAGE_READ_ONLY;
1683 goto out;
1684 }
1685
1686 pImage->PCHSGeometry = *pPCHSGeometry;
1687 rc = VINF_SUCCESS;
1688 }
1689 else
1690 rc = VERR_VD_NOT_OPENED;
1691
1692out:
1693 LogFlowFunc(("returns %Rrc\n", rc));
1694 return rc;
1695}
1696
1697/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1698static int vdiGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1699{
1700 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1701 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1702 int rc;
1703
1704 AssertPtr(pImage);
1705
1706 if (pImage)
1707 {
1708 VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
1709 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
1710 if (!pGeometry)
1711 pGeometry = &DummyGeo;
1712
1713 if ( pGeometry->cCylinders > 0
1714 && pGeometry->cHeads > 0
1715 && pGeometry->cSectors > 0)
1716 {
1717 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
1718 pLCHSGeometry->cHeads = pGeometry->cHeads;
1719 pLCHSGeometry->cSectors = pGeometry->cSectors;
1720 rc = VINF_SUCCESS;
1721 }
1722 else
1723 rc = VERR_VD_GEOMETRY_NOT_SET;
1724 }
1725 else
1726 rc = VERR_VD_NOT_OPENED;
1727
1728 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1729 return rc;
1730}
1731
1732/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1733static int vdiSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1734{
1735 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1736 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1737 PVDIDISKGEOMETRY pGeometry;
1738 int rc;
1739
1740 AssertPtr(pImage);
1741
1742 if (pImage)
1743 {
1744 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1745 {
1746 rc = VERR_VD_IMAGE_READ_ONLY;
1747 goto out;
1748 }
1749
1750 pGeometry = getImageLCHSGeometry(&pImage->Header);
1751 if (pGeometry)
1752 {
1753 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
1754 pGeometry->cHeads = pLCHSGeometry->cHeads;
1755 pGeometry->cSectors = pLCHSGeometry->cSectors;
1756 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
1757
1758 /* Update header information in base image file. */
1759 vdiFlushImage(pImage);
1760 }
1761 rc = VINF_SUCCESS;
1762 }
1763 else
1764 rc = VERR_VD_NOT_OPENED;
1765
1766out:
1767 LogFlowFunc(("returns %Rrc\n", rc));
1768 return rc;
1769}
1770
1771/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1772static unsigned vdiGetImageFlags(void *pBackendData)
1773{
1774 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1775 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1776 unsigned uImageFlags;
1777
1778 AssertPtr(pImage);
1779
1780 if (pImage)
1781 uImageFlags = pImage->uImageFlags;
1782 else
1783 uImageFlags = 0;
1784
1785 LogFlowFunc(("returns %#x\n", uImageFlags));
1786 return uImageFlags;
1787}
1788
1789/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1790static unsigned vdiGetOpenFlags(void *pBackendData)
1791{
1792 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1793 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1794 unsigned uOpenFlags;
1795
1796 AssertPtr(pImage);
1797
1798 if (pImage)
1799 uOpenFlags = pImage->uOpenFlags;
1800 else
1801 uOpenFlags = 0;
1802
1803 LogFlowFunc(("returns %#x\n", uOpenFlags));
1804 return uOpenFlags;
1805}
1806
1807/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1808static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1809{
1810 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
1811 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1812 int rc;
1813 const char *pszFilename;
1814
1815 /* Image must be opened and the new flags must be valid. */
1816 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)))
1817 {
1818 rc = VERR_INVALID_PARAMETER;
1819 goto out;
1820 }
1821
1822 /* Implement this operation via reopening the image. */
1823 pszFilename = pImage->pszFilename;
1824 rc = vdiFreeImage(pImage, false);
1825 if (RT_FAILURE(rc))
1826 goto out;
1827 rc = vdiOpenImage(pImage, uOpenFlags);
1828
1829out:
1830 LogFlowFunc(("returns %Rrc\n", rc));
1831 return rc;
1832}
1833
1834/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1835static int vdiGetComment(void *pBackendData, char *pszComment,
1836 size_t cbComment)
1837{
1838 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1839 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1840 int rc = VINF_SUCCESS;
1841
1842 AssertPtr(pImage);
1843
1844 if (pImage)
1845 {
1846 char *pszTmp = getImageComment(&pImage->Header);
1847 /* Make this foolproof even if the image doesn't have the zero
1848 * termination. With some luck the repaired header will be saved. */
1849 size_t cb = RTStrNLen(pszTmp, VDI_IMAGE_COMMENT_SIZE);
1850 if (cb == VDI_IMAGE_COMMENT_SIZE)
1851 {
1852 pszTmp[VDI_IMAGE_COMMENT_SIZE-1] = '\0';
1853 cb--;
1854 }
1855 if (cb < cbComment)
1856 {
1857 /* memcpy is much better than strncpy. */
1858 memcpy(pszComment, pszTmp, cb + 1);
1859 }
1860 else
1861 rc = VERR_BUFFER_OVERFLOW;
1862 }
1863 else
1864 rc = VERR_VD_NOT_OPENED;
1865
1866 LogFlowFunc(("returns %Rrc comment=\"%s\"\n", rc, pszComment));
1867 return rc;
1868}
1869
1870/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1871static int vdiSetComment(void *pBackendData, const char *pszComment)
1872{
1873 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1874 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1875 int rc;
1876
1877 AssertPtr(pImage);
1878
1879 if (pImage)
1880 {
1881 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1882 rc = VERR_VD_IMAGE_READ_ONLY;
1883 else
1884 {
1885 size_t cchComment = pszComment ? strlen(pszComment) : 0;
1886 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
1887 {
1888 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
1889 rc = VERR_VD_VDI_COMMENT_TOO_LONG;
1890 goto out;
1891 }
1892
1893 /* we don't support old style images */
1894 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1895 {
1896 /*
1897 * Update the comment field, making sure to zero out all of the previous comment.
1898 */
1899 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
1900 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
1901
1902 /* write out new the header */
1903 rc = vdiUpdateHeader(pImage);
1904 }
1905 else
1906 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
1907 }
1908 }
1909 else
1910 rc = VERR_VD_NOT_OPENED;
1911
1912out:
1913 LogFlowFunc(("returns %Rrc\n", rc));
1914 return rc;
1915}
1916
1917/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
1918static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
1919{
1920 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1921 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1922 int rc;
1923
1924 AssertPtr(pImage);
1925
1926 if (pImage)
1927 {
1928 *pUuid = *getImageCreationUUID(&pImage->Header);
1929 rc = VINF_SUCCESS;
1930 }
1931 else
1932 rc = VERR_VD_NOT_OPENED;
1933
1934 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1935 return rc;
1936}
1937
1938/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
1939static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
1940{
1941 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1942 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1943 int rc = VINF_SUCCESS;
1944
1945 AssertPtr(pImage);
1946
1947 if (pImage)
1948 {
1949 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1950 {
1951 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1952 pImage->Header.u.v1.uuidCreate = *pUuid;
1953 /* Make it possible to clone old VDIs. */
1954 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
1955 pImage->Header.u.v0.uuidCreate = *pUuid;
1956 else
1957 {
1958 LogFunc(("Version is not supported!\n"));
1959 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
1960 }
1961 }
1962 else
1963 rc = VERR_VD_IMAGE_READ_ONLY;
1964 }
1965 else
1966 rc = VERR_VD_NOT_OPENED;
1967
1968 LogFlowFunc(("returns %Rrc\n", rc));
1969 return rc;
1970}
1971
1972/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
1973static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1974{
1975 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1976 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1977 int rc;
1978
1979 AssertPtr(pImage);
1980
1981 if (pImage)
1982 {
1983 *pUuid = *getImageModificationUUID(&pImage->Header);
1984 rc = VINF_SUCCESS;
1985 }
1986 else
1987 rc = VERR_VD_NOT_OPENED;
1988
1989 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1990 return rc;
1991}
1992
1993/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
1994static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1995{
1996 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1997 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1998 int rc = VINF_SUCCESS;
1999
2000 AssertPtr(pImage);
2001
2002 if (pImage)
2003 {
2004 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2005 {
2006 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2007 pImage->Header.u.v1.uuidModify = *pUuid;
2008 /* Make it possible to clone old VDIs. */
2009 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2010 pImage->Header.u.v0.uuidModify = *pUuid;
2011 else
2012 {
2013 LogFunc(("Version is not supported!\n"));
2014 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2015 }
2016 }
2017 else
2018 rc = VERR_VD_IMAGE_READ_ONLY;
2019 }
2020 else
2021 rc = VERR_VD_NOT_OPENED;
2022
2023 LogFlowFunc(("returns %Rrc\n", rc));
2024 return rc;
2025}
2026
2027/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2028static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
2029{
2030 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2031 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2032 int rc;
2033
2034 AssertPtr(pImage);
2035
2036 if (pImage)
2037 {
2038 *pUuid = *getImageParentUUID(&pImage->Header);
2039 rc = VINF_SUCCESS;
2040 }
2041 else
2042 rc = VERR_VD_NOT_OPENED;
2043
2044 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2045 return rc;
2046}
2047
2048/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2049static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2050{
2051 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2052 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2053 int rc = VINF_SUCCESS;
2054
2055 AssertPtr(pImage);
2056
2057 if (pImage)
2058 {
2059 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2060 {
2061 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2062 pImage->Header.u.v1.uuidLinkage = *pUuid;
2063 /* Make it possible to clone old VDIs. */
2064 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
2065 pImage->Header.u.v0.uuidLinkage = *pUuid;
2066 else
2067 {
2068 LogFunc(("Version is not supported!\n"));
2069 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2070 }
2071 }
2072 else
2073 rc = VERR_VD_IMAGE_READ_ONLY;
2074 }
2075 else
2076 rc = VERR_VD_NOT_OPENED;
2077
2078 LogFlowFunc(("returns %Rrc\n", rc));
2079 return rc;
2080}
2081
2082/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2083static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2084{
2085 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2086 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2087 int rc;
2088
2089 AssertPtr(pImage);
2090
2091 if (pImage)
2092 {
2093 *pUuid = *getImageParentModificationUUID(&pImage->Header);
2094 rc = VINF_SUCCESS;
2095 }
2096 else
2097 rc = VERR_VD_NOT_OPENED;
2098
2099 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2100 return rc;
2101}
2102
2103/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2104static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2105{
2106 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2107 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2108 int rc = VINF_SUCCESS;
2109
2110 AssertPtr(pImage);
2111
2112 if (pImage)
2113 {
2114 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2115 {
2116 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2117 pImage->Header.u.v1.uuidParentModify = *pUuid;
2118 else
2119 {
2120 LogFunc(("Version is not supported!\n"));
2121 rc = VERR_VD_VDI_UNSUPPORTED_VERSION;
2122 }
2123 }
2124 else
2125 rc = VERR_VD_IMAGE_READ_ONLY;
2126 }
2127 else
2128 rc = VERR_VD_NOT_OPENED;
2129
2130 LogFlowFunc(("returns %Rrc\n", rc));
2131 return rc;
2132}
2133
2134/** @copydoc VBOXHDDBACKEND::pfnDump */
2135static void vdiDump(void *pBackendData)
2136{
2137 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2138
2139 vdIfErrorMessage(pImage->pIfError, "Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%#p\n",
2140 pImage->pszFilename,
2141 (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
2142 pImage->uOpenFlags,
2143 pImage->pStorage);
2144 vdIfErrorMessage(pImage->pIfError, "Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
2145 pImage->PreHeader.u32Version,
2146 getImageType(&pImage->Header),
2147 getImageFlags(&pImage->Header),
2148 getImageDiskSize(&pImage->Header));
2149 vdIfErrorMessage(pImage->pIfError, "Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
2150 getImageBlockSize(&pImage->Header),
2151 getImageExtraBlockSize(&pImage->Header),
2152 getImageBlocks(&pImage->Header),
2153 getImageBlocksAllocated(&pImage->Header));
2154 vdIfErrorMessage(pImage->pIfError, "Header: offBlocks=%u offData=%u\n",
2155 getImageBlocksOffset(&pImage->Header),
2156 getImageDataOffset(&pImage->Header));
2157 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
2158 if (pg)
2159 vdIfErrorMessage(pImage->pIfError, "Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
2160 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
2161 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", getImageCreationUUID(&pImage->Header));
2162 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", getImageModificationUUID(&pImage->Header));
2163 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", getImageParentUUID(&pImage->Header));
2164 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
2165 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", getImageParentModificationUUID(&pImage->Header));
2166 vdIfErrorMessage(pImage->pIfError, "Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
2167 pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
2168 vdIfErrorMessage(pImage->pIfError, "Image: uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
2169 pImage->uBlockMask,
2170 pImage->cbTotalBlockData,
2171 pImage->uShiftOffset2Index,
2172 pImage->offStartBlockData);
2173
2174 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
2175 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
2176 {
2177 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2178 {
2179 cBlocksNotFree++;
2180 if (pImage->paBlocks[uBlock] >= cBlocks)
2181 cBadBlocks++;
2182 }
2183 }
2184 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
2185 {
2186 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
2187 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
2188 }
2189 if (cBadBlocks)
2190 {
2191 vdIfErrorMessage(pImage->pIfError, "!! WARNING: %u bad blocks found !!\n",
2192 cBadBlocks);
2193 }
2194}
2195
2196static int vdiAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
2197 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
2198{
2199 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
2200 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
2201 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2202 unsigned uBlock;
2203 unsigned offRead;
2204 int rc;
2205
2206 AssertPtr(pImage);
2207 Assert(!(uOffset % 512));
2208 Assert(!(cbToRead % 512));
2209
2210 if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
2211 || !VALID_PTR(pIoCtx)
2212 || !cbToRead)
2213 {
2214 rc = VERR_INVALID_PARAMETER;
2215 goto out;
2216 }
2217
2218 /* Calculate starting block number and offset inside it. */
2219 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2220 offRead = (unsigned)uOffset & pImage->uBlockMask;
2221
2222 /* Clip read range to at most the rest of the block. */
2223 cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
2224 Assert(!(cbToRead % 512));
2225
2226 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
2227 rc = VERR_VD_BLOCK_FREE;
2228 else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
2229 {
2230 size_t cbSet;
2231
2232 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
2233 Assert(cbSet == cbToRead);
2234
2235 rc = VINF_SUCCESS;
2236 }
2237 else
2238 {
2239 /* Block present in image file, read relevant data. */
2240 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
2241 + (pImage->offStartData + pImage->offStartBlockData + offRead);
2242 rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
2243 pIoCtx, cbToRead);
2244 }
2245
2246 if (pcbActuallyRead)
2247 *pcbActuallyRead = cbToRead;
2248
2249out:
2250 LogFlowFunc(("returns %Rrc\n", rc));
2251 return rc;
2252}
2253
2254static int vdiAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
2255 PVDIOCTX pIoCtx,
2256 size_t *pcbWriteProcess, size_t *pcbPreRead,
2257 size_t *pcbPostRead, unsigned fWrite)
2258{
2259 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
2260 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
2261 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2262 unsigned uBlock;
2263 unsigned offWrite;
2264 int rc = VINF_SUCCESS;
2265
2266 AssertPtr(pImage);
2267 Assert(!(uOffset % 512));
2268 Assert(!(cbToWrite % 512));
2269
2270 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2271 {
2272 rc = VERR_VD_IMAGE_READ_ONLY;
2273 goto out;
2274 }
2275
2276 if (!VALID_PTR(pIoCtx) || !cbToWrite)
2277 {
2278 rc = VERR_INVALID_PARAMETER;
2279 goto out;
2280 }
2281
2282 /* No size check here, will do that later. For dynamic images which are
2283 * not multiples of the block size in length, this would prevent writing to
2284 * the last block. */
2285
2286 /* Calculate starting block number and offset inside it. */
2287 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2288 offWrite = (unsigned)uOffset & pImage->uBlockMask;
2289
2290 /* Clip write range to at most the rest of the block. */
2291 cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
2292 Assert(!(cbToWrite % 512));
2293
2294 do
2295 {
2296 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2297 {
2298 /* Block is either free or zero. */
2299 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
2300 && ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
2301 || cbToWrite == getImageBlockSize(&pImage->Header)))
2302 {
2303#if 0 /** @todo Provide interface to check an I/O context for a specific value */
2304 /* If the destination block is unallocated at this point, it's
2305 * either a zero block or a block which hasn't been used so far
2306 * (which also means that it's a zero block. Don't need to write
2307 * anything to this block if the data consists of just zeroes. */
2308 Assert(!(cbToWrite % 4));
2309 Assert(cbToWrite * 8 <= UINT32_MAX);
2310 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
2311 {
2312 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
2313 break;
2314 }
2315#endif
2316 }
2317
2318 if ( cbToWrite == getImageBlockSize(&pImage->Header)
2319 && !(fWrite & VD_WRITE_NO_ALLOC))
2320 {
2321 /* Full block write to previously unallocated block.
2322 * Allocate block and write data. */
2323 Assert(!offWrite);
2324 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
2325 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
2326 + (pImage->offStartData + pImage->offStartBlockData);
2327 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2328 u64Offset, pIoCtx, cbToWrite, NULL, NULL);
2329 if (RT_UNLIKELY(RT_FAILURE_NP(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)))
2330 goto out;
2331 pImage->paBlocks[uBlock] = cBlocksAllocated;
2332
2333 if (pImage->paBlocksRev)
2334 pImage->paBlocksRev[cBlocksAllocated] = uBlock;
2335
2336 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
2337
2338 rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx, true /* fUpdateHdr */);
2339 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
2340 goto out;
2341
2342 *pcbPreRead = 0;
2343 *pcbPostRead = 0;
2344 }
2345 else
2346 {
2347 /* Trying to do a partial write to an unallocated block. Don't do
2348 * anything except letting the upper layer know what to do. */
2349 *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
2350 *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
2351 rc = VERR_VD_BLOCK_FREE;
2352 }
2353 }
2354 else
2355 {
2356 /* Block present in image file, write relevant data. */
2357 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
2358 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
2359 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2360 u64Offset, pIoCtx, cbToWrite, NULL, NULL);
2361 }
2362 } while (0);
2363
2364 if (pcbWriteProcess)
2365 *pcbWriteProcess = cbToWrite;
2366
2367out:
2368 LogFlowFunc(("returns %Rrc\n", rc));
2369 return rc;
2370}
2371
2372static int vdiAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
2373{
2374 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2375 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2376 int rc = VINF_SUCCESS;
2377
2378 Assert(pImage);
2379
2380 rc = vdiFlushImageAsync(pImage, pIoCtx);
2381 LogFlowFunc(("returns %Rrc\n", rc));
2382 return rc;
2383}
2384
2385/** @copydoc VBOXHDDBACKEND::pfnCompact */
2386static int vdiCompact(void *pBackendData, unsigned uPercentStart,
2387 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2388 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2389{
2390 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2391 int rc = VINF_SUCCESS;
2392 void *pvBuf = NULL, *pvTmp = NULL;
2393 unsigned *paBlocks2 = NULL;
2394
2395 int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2396 void *pvParent = NULL;
2397 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2398 if (pIfParentState)
2399 {
2400 pfnParentRead = pIfParentState->pfnParentRead;
2401 pvParent = pIfParentState->Core.pvUser;
2402 }
2403
2404 PFNVDPROGRESS pfnProgress = NULL;
2405 void *pvUser = NULL;
2406 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2407
2408 do {
2409 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2410
2411 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2412 rc = VERR_VD_IMAGE_READ_ONLY);
2413
2414 unsigned cBlocks;
2415 unsigned cBlocksToMove = 0;
2416 size_t cbBlock;
2417 cBlocks = getImageBlocks(&pImage->Header);
2418 cbBlock = getImageBlockSize(&pImage->Header);
2419 if (pfnParentRead)
2420 {
2421 pvBuf = RTMemTmpAlloc(cbBlock);
2422 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2423 }
2424 pvTmp = RTMemTmpAlloc(cbBlock);
2425 AssertBreakStmt(VALID_PTR(pvTmp), rc = VERR_NO_MEMORY);
2426
2427 uint64_t cbFile;
2428 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2429 AssertRCBreak(rc);
2430 unsigned cBlocksAllocated = (unsigned)((cbFile - pImage->offStartData - pImage->offStartBlockData) >> pImage->uShiftOffset2Index);
2431 if (cBlocksAllocated == 0)
2432 {
2433 /* No data blocks in this image, no need to compact. */
2434 rc = VINF_SUCCESS;
2435 break;
2436 }
2437
2438 /* Allocate block array for back resolving. */
2439 paBlocks2 = (unsigned *)RTMemAlloc(sizeof(unsigned *) * cBlocksAllocated);
2440 AssertBreakStmt(VALID_PTR(paBlocks2), rc = VERR_NO_MEMORY);
2441 /* Fill out back resolving, check/fix allocation errors before
2442 * compacting the image, just to be on the safe side. Update the
2443 * image contents straight away, as this enables cancelling. */
2444 for (unsigned i = 0; i < cBlocksAllocated; i++)
2445 paBlocks2[i] = VDI_IMAGE_BLOCK_FREE;
2446 rc = VINF_SUCCESS;
2447 for (unsigned i = 0; i < cBlocks; i++)
2448 {
2449 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2450 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2451 {
2452 if (ptrBlock < cBlocksAllocated)
2453 {
2454 if (paBlocks2[ptrBlock] == VDI_IMAGE_BLOCK_FREE)
2455 paBlocks2[ptrBlock] = i;
2456 else
2457 {
2458 LogFunc(("Freed cross-linked block %u in file \"%s\"\n",
2459 i, pImage->pszFilename));
2460 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2461 rc = vdiUpdateBlockInfo(pImage, i);
2462 if (RT_FAILURE(rc))
2463 break;
2464 }
2465 }
2466 else
2467 {
2468 LogFunc(("Freed out of bounds reference for block %u in file \"%s\"\n",
2469 i, pImage->pszFilename));
2470 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2471 rc = vdiUpdateBlockInfo(pImage, i);
2472 if (RT_FAILURE(rc))
2473 break;
2474 }
2475 }
2476 }
2477 if (RT_FAILURE(rc))
2478 break;
2479
2480 /* Find redundant information and update the block pointers
2481 * accordingly, creating bubbles. Keep disk up to date, as this
2482 * enables cancelling. */
2483 for (unsigned i = 0; i < cBlocks; i++)
2484 {
2485 VDIIMAGEBLOCKPOINTER ptrBlock = pImage->paBlocks[i];
2486 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(ptrBlock))
2487 {
2488 /* Block present in image file, read relevant data. */
2489 uint64_t u64Offset = (uint64_t)ptrBlock * pImage->cbTotalBlockData
2490 + (pImage->offStartData + pImage->offStartBlockData);
2491 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset, pvTmp, cbBlock, NULL);
2492 if (RT_FAILURE(rc))
2493 break;
2494
2495 if (ASMBitFirstSet((volatile void *)pvTmp, (uint32_t)cbBlock * 8) == -1)
2496 {
2497 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_ZERO;
2498 rc = vdiUpdateBlockInfo(pImage, i);
2499 if (RT_FAILURE(rc))
2500 break;
2501 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2502 /* Adjust progress info, one block to be relocated. */
2503 cBlocksToMove++;
2504 }
2505 else if (pfnParentRead)
2506 {
2507 rc = pfnParentRead(pvParent, i * cbBlock, pvBuf, cbBlock);
2508 if (RT_FAILURE(rc))
2509 break;
2510 if (!memcmp(pvTmp, pvBuf, cbBlock))
2511 {
2512 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
2513 rc = vdiUpdateBlockInfo(pImage, i);
2514 if (RT_FAILURE(rc))
2515 break;
2516 paBlocks2[ptrBlock] = VDI_IMAGE_BLOCK_FREE;
2517 /* Adjust progress info, one block to be relocated. */
2518 cBlocksToMove++;
2519 }
2520 }
2521 }
2522
2523 if (pIfProgress && pIfProgress->pfnProgress)
2524 {
2525 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2526 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2527 if (RT_FAILURE(rc))
2528 break;
2529 }
2530 }
2531 if (RT_FAILURE(rc))
2532 break;
2533
2534 /* Fill bubbles with other data (if available). */
2535 unsigned cBlocksMoved = 0;
2536 unsigned uBlockUsedPos = cBlocksAllocated;
2537 for (unsigned i = 0; i < cBlocksAllocated; i++)
2538 {
2539 unsigned uBlock = paBlocks2[i];
2540 if (uBlock == VDI_IMAGE_BLOCK_FREE)
2541 {
2542 unsigned uBlockData = VDI_IMAGE_BLOCK_FREE;
2543 while (uBlockUsedPos > i && uBlockData == VDI_IMAGE_BLOCK_FREE)
2544 {
2545 uBlockUsedPos--;
2546 uBlockData = paBlocks2[uBlockUsedPos];
2547 }
2548 /* Terminate early if there is no block which needs copying. */
2549 if (uBlockUsedPos == i)
2550 break;
2551 uint64_t u64Offset = (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2552 + (pImage->offStartData + pImage->offStartBlockData);
2553 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2554 pvTmp, cbBlock, NULL);
2555 u64Offset = (uint64_t)i * pImage->cbTotalBlockData
2556 + (pImage->offStartData + pImage->offStartBlockData);
2557 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2558 pvTmp, cbBlock, NULL);
2559 pImage->paBlocks[uBlockData] = i;
2560 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated - cBlocksMoved);
2561 rc = vdiUpdateBlockInfo(pImage, uBlockData);
2562 if (RT_FAILURE(rc))
2563 break;
2564 paBlocks2[i] = uBlockData;
2565 paBlocks2[uBlockUsedPos] = VDI_IMAGE_BLOCK_FREE;
2566 cBlocksMoved++;
2567 }
2568
2569 if (pIfProgress && pIfProgress->pfnProgress)
2570 {
2571 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2572 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2573
2574 if (RT_FAILURE(rc))
2575 break;
2576 }
2577 }
2578 if (RT_FAILURE(rc))
2579 break;
2580
2581 /* Update image header. */
2582 setImageBlocksAllocated(&pImage->Header, uBlockUsedPos);
2583 vdiUpdateHeader(pImage);
2584
2585 /* Truncate the image to the proper size to finish compacting. */
2586 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2587 (uint64_t)uBlockUsedPos * pImage->cbTotalBlockData
2588 + pImage->offStartData + pImage->offStartBlockData);
2589 } while (0);
2590
2591 if (paBlocks2)
2592 RTMemTmpFree(paBlocks2);
2593 if (pvTmp)
2594 RTMemTmpFree(pvTmp);
2595 if (pvBuf)
2596 RTMemTmpFree(pvBuf);
2597
2598 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2599 {
2600 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2601 uPercentStart + uPercentSpan);
2602 }
2603
2604 LogFlowFunc(("returns %Rrc\n", rc));
2605 return rc;
2606}
2607
2608
2609/** @copydoc VBOXHDDBACKEND::pfnResize */
2610static int vdiResize(void *pBackendData, uint64_t cbSize,
2611 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2612 unsigned uPercentStart, unsigned uPercentSpan,
2613 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2614 PVDINTERFACE pVDIfsOperation)
2615{
2616 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2617 int rc = VINF_SUCCESS;
2618
2619 PFNVDPROGRESS pfnProgress = NULL;
2620 void *pvUser = NULL;
2621 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2622
2623 /*
2624 * Making the image smaller is not supported at the moment.
2625 * Resizing is also not supported for fixed size images and
2626 * very old images.
2627 */
2628 /** @todo implement making the image smaller, it is the responsibility of
2629 * the user to know what he's doing. */
2630 if ( cbSize < getImageDiskSize(&pImage->Header)
2631 || GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2632 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2633 rc = VERR_NOT_SUPPORTED;
2634 else if (cbSize > getImageDiskSize(&pImage->Header))
2635 {
2636 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header); /** < Blocks currently allocated, doesn't change during resize */
2637 uint32_t cBlocksNew = cbSize / getImageBlockSize(&pImage->Header); /** < New number of blocks in the image after the resize */
2638 if (cbSize % getImageBlockSize(&pImage->Header))
2639 cBlocksNew++;
2640
2641 uint32_t cBlocksOld = getImageBlocks(&pImage->Header); /** < Number of blocks before the resize. */
2642 uint64_t cbBlockspaceNew = cBlocksNew * sizeof(VDIIMAGEBLOCKPOINTER); /** < Required space for the block array after the resize. */
2643 uint64_t offStartDataNew = RT_ALIGN_32(pImage->offStartBlocks + cbBlockspaceNew, VDI_DATA_ALIGN); /** < New start offset for block data after the resize */
2644
2645 if ( pImage->offStartData != offStartDataNew
2646 && cBlocksAllocated > 0)
2647 {
2648 /* Calculate how many sectors need to be relocated. */
2649 uint64_t cbOverlapping = offStartDataNew - pImage->offStartData;
2650 unsigned cBlocksReloc = cbOverlapping / getImageBlockSize(&pImage->Header);
2651 if (cbOverlapping % getImageBlockSize(&pImage->Header))
2652 cBlocksReloc++;
2653
2654 /* Since only full blocks can be relocated the new data start is
2655 * determined by moving it block by block. */
2656 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2657 offStartDataNew = pImage->offStartData;
2658
2659 /* Do the relocation. */
2660 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2661
2662 /*
2663 * Get the blocks we need to relocate first, they are appended to the end
2664 * of the image.
2665 */
2666 void *pvBuf = NULL, *pvZero = NULL;
2667 do
2668 {
2669 VDIIMAGEBLOCKPOINTER uBlock = 0;
2670
2671 /* Allocate data buffer. */
2672 pvBuf = RTMemAllocZ(pImage->cbTotalBlockData);
2673 if (!pvBuf)
2674 {
2675 rc = VERR_NO_MEMORY;
2676 break;
2677 }
2678
2679 /* Allocate buffer for overwriting with zeroes. */
2680 pvZero = RTMemAllocZ(pImage->cbTotalBlockData);
2681 if (!pvZero)
2682 {
2683 rc = VERR_NO_MEMORY;
2684 break;
2685 }
2686
2687 for (unsigned i = 0; i < cBlocksReloc; i++)
2688 {
2689 /* Search the index in the block table. */
2690 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2691 {
2692 if (pImage->paBlocks[idxBlock] == uBlock)
2693 {
2694 /* Read data and append to the end of the image. */
2695 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2696 offStartDataNew, pvBuf,
2697 pImage->cbTotalBlockData, NULL);
2698 if (RT_FAILURE(rc))
2699 break;
2700
2701 uint64_t offBlockAppend;
2702 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &offBlockAppend);
2703 if (RT_FAILURE(rc))
2704 break;
2705
2706 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2707 offBlockAppend, pvBuf,
2708 pImage->cbTotalBlockData, NULL);
2709 if (RT_FAILURE(rc))
2710 break;
2711
2712 /* Zero out the old block area. */
2713 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2714 offStartDataNew, pvZero,
2715 pImage->cbTotalBlockData, NULL);
2716 if (RT_FAILURE(rc))
2717 break;
2718
2719 /* Update block counter. */
2720 pImage->paBlocks[idxBlock] = cBlocksAllocated - 1;
2721
2722 /*
2723 * Decrease the block number of all other entries in the array.
2724 * They were moved one block to the front.
2725 * Doing it as a separate step iterating over the array again
2726 * because an error while relocating the block might end up
2727 * in a corrupted image otherwise.
2728 */
2729 for (unsigned idxBlock2 = 0; idxBlock2 < cBlocksOld; idxBlock2++)
2730 {
2731 if ( idxBlock2 != idxBlock
2732 && IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[idxBlock2]))
2733 pImage->paBlocks[idxBlock2]--;
2734 }
2735
2736 /* Continue with the next block. */
2737 break;
2738 }
2739 }
2740
2741 if (RT_FAILURE(rc))
2742 break;
2743
2744 uBlock++;
2745 offStartDataNew += pImage->cbTotalBlockData;
2746 }
2747 } while (0);
2748
2749 if (pvBuf)
2750 RTMemFree(pvBuf);
2751 if (pvZero)
2752 RTMemFree(pvZero);
2753 }
2754
2755 /*
2756 * We need to update the new offsets for the image data in the out of memory
2757 * case too because we relocated the blocks already.
2758 */
2759 pImage->offStartData = offStartDataNew;
2760 setImageDataOffset(&pImage->Header, offStartDataNew);
2761
2762 /*
2763 * Relocation done, expand the block array and update the header with
2764 * the new data.
2765 */
2766 if (RT_SUCCESS(rc))
2767 {
2768 PVDIIMAGEBLOCKPOINTER paBlocksNew = (PVDIIMAGEBLOCKPOINTER)RTMemRealloc(pImage->paBlocks, cbBlockspaceNew);
2769 if (paBlocksNew)
2770 {
2771 pImage->paBlocks = paBlocksNew;
2772
2773 /* Mark the new blocks as unallocated. */
2774 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2775 pImage->paBlocks[idxBlock] = VDI_IMAGE_BLOCK_FREE;
2776 }
2777 else
2778 rc = VERR_NO_MEMORY;
2779
2780 /* Write the block array before updating the rest. */
2781 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->offStartBlocks,
2782 pImage->paBlocks, cbBlockspaceNew, NULL);
2783
2784 if (RT_SUCCESS(rc))
2785 {
2786 /* Update size and new block count. */
2787 setImageDiskSize(&pImage->Header, cbSize);
2788 setImageBlocks(&pImage->Header, cBlocksNew);
2789 /* Update geometry. */
2790 pImage->PCHSGeometry = *pPCHSGeometry;
2791
2792 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
2793 if (pGeometry)
2794 {
2795 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
2796 pGeometry->cHeads = pLCHSGeometry->cHeads;
2797 pGeometry->cSectors = pLCHSGeometry->cSectors;
2798 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
2799 }
2800 }
2801 }
2802
2803 /* Update header information in base image file. */
2804 vdiFlushImage(pImage);
2805 }
2806 /* Same size doesn't change the image at all. */
2807
2808 LogFlowFunc(("returns %Rrc\n", rc));
2809 return rc;
2810}
2811
2812
2813/** @copydoc VBOXHDDBACKEND::pfnDiscard */
2814static DECLCALLBACK(int) vdiDiscard(void *pBackendData,
2815 uint64_t uOffset, size_t cbDiscard,
2816 size_t *pcbPreAllocated,
2817 size_t *pcbPostAllocated,
2818 size_t *pcbActuallyDiscarded,
2819 void **ppbmAllocationBitmap,
2820 unsigned fDiscard)
2821{
2822 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2823 unsigned uBlock;
2824 unsigned offDiscard;
2825 int rc = VINF_SUCCESS;
2826 void *pvBlock = NULL;
2827
2828 LogFlowFunc(("pBackendData=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
2829 pBackendData, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
2830
2831 AssertPtr(pImage);
2832 Assert(!(uOffset % 512));
2833 Assert(!(cbDiscard % 512));
2834
2835 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2836 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
2837 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
2838 && cbDiscard,
2839 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
2840 uOffset, cbDiscard),
2841 VERR_INVALID_PARAMETER);
2842
2843 do
2844 {
2845 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2846 ("Image is opened readonly\n"),
2847 rc = VERR_VD_IMAGE_READ_ONLY);
2848
2849 AssertMsgBreakStmt(cbDiscard,
2850 ("cbDiscard=%u\n", cbDiscard),
2851 rc = VERR_INVALID_PARAMETER);
2852
2853 /* Calculate starting block number and offset inside it. */
2854 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2855 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
2856
2857 /* Clip range to at most the rest of the block. */
2858 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
2859 Assert(!(cbDiscard % 512));
2860
2861 if (pcbPreAllocated)
2862 *pcbPreAllocated = 0;
2863
2864 if (pcbPostAllocated)
2865 *pcbPostAllocated = 0;
2866
2867 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2868 {
2869 uint8_t *pbBlockData;
2870 size_t cbPreAllocated, cbPostAllocated;
2871
2872 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
2873 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
2874
2875 /* Read the block data. */
2876 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
2877 if (!pvBlock)
2878 {
2879 rc = VERR_NO_MEMORY;
2880 break;
2881 }
2882
2883 if (!cbPreAllocated && !cbPostAllocated)
2884 {
2885 /*
2886 * Discarding a whole block, don't check for allocated sectors.
2887 * It is possible to just remove the whole block which avoids
2888 * one read and checking the whole block for data.
2889 */
2890 rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
2891 }
2892 else
2893 {
2894 /* Read data. */
2895 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
2896
2897 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
2898 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
2899 pvBlock, pImage->cbTotalBlockData, NULL);
2900 if (RT_FAILURE(rc))
2901 break;
2902
2903 /* Clear data. */
2904 memset(pbBlockData + offDiscard , 0, cbDiscard);
2905
2906 Assert(!(cbDiscard % 4));
2907 Assert(cbDiscard * 8 <= UINT32_MAX);
2908 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
2909 rc = vdiDiscardBlock(pImage, uBlock, pvBlock);
2910 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
2911 {
2912 /* Write changed data to the image. */
2913 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, u64Offset + offDiscard,
2914 pbBlockData + offDiscard, cbDiscard, NULL);
2915 }
2916 else
2917 {
2918 /* Block has data, create allocation bitmap. */
2919 *pcbPreAllocated = cbPreAllocated;
2920 *pcbPostAllocated = cbPostAllocated;
2921 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
2922 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
2923 rc = VERR_NO_MEMORY;
2924 else
2925 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
2926 }
2927 } /* if: no complete block discarded */
2928 } /* if: Block is allocated. */
2929 /* else: nothing to do. */
2930 } while (0);
2931
2932 if (pcbActuallyDiscarded)
2933 *pcbActuallyDiscarded = cbDiscard;
2934
2935 if (pvBlock)
2936 RTMemFree(pvBlock);
2937
2938 LogFlowFunc(("returns %Rrc\n", rc));
2939 return rc;
2940}
2941
2942/** @copydoc VBOXHDDBACKEND::pfnDiscard */
2943static DECLCALLBACK(int) vdiAsyncDiscard(void *pBackendData, PVDIOCTX pIoCtx,
2944 uint64_t uOffset, size_t cbDiscard,
2945 size_t *pcbPreAllocated,
2946 size_t *pcbPostAllocated,
2947 size_t *pcbActuallyDiscarded,
2948 void **ppbmAllocationBitmap,
2949 unsigned fDiscard)
2950{
2951 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
2952 unsigned uBlock;
2953 unsigned offDiscard;
2954 int rc = VINF_SUCCESS;
2955 void *pvBlock = NULL;
2956
2957 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p uOffset=%llu cbDiscard=%zu pcbPreAllocated=%#p pcbPostAllocated=%#p pcbActuallyDiscarded=%#p ppbmAllocationBitmap=%#p fDiscard=%#x\n",
2958 pBackendData, pIoCtx, uOffset, cbDiscard, pcbPreAllocated, pcbPostAllocated, pcbActuallyDiscarded, ppbmAllocationBitmap, fDiscard));
2959
2960 AssertPtr(pImage);
2961 Assert(!(uOffset % 512));
2962 Assert(!(cbDiscard % 512));
2963
2964 AssertMsgReturn(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2965 ("Image is readonly\n"), VERR_VD_IMAGE_READ_ONLY);
2966 AssertMsgReturn( uOffset + cbDiscard <= getImageDiskSize(&pImage->Header)
2967 && cbDiscard,
2968 ("Invalid parameters uOffset=%llu cbDiscard=%zu\n",
2969 uOffset, cbDiscard),
2970 VERR_INVALID_PARAMETER);
2971
2972 do
2973 {
2974 AssertMsgBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2975 ("Image is opened readonly\n"),
2976 rc = VERR_VD_IMAGE_READ_ONLY);
2977
2978 AssertMsgBreakStmt(cbDiscard,
2979 ("cbDiscard=%u\n", cbDiscard),
2980 rc = VERR_INVALID_PARAMETER);
2981
2982 /* Calculate starting block number and offset inside it. */
2983 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
2984 offDiscard = (unsigned)uOffset & pImage->uBlockMask;
2985
2986 /* Clip range to at most the rest of the block. */
2987 cbDiscard = RT_MIN(cbDiscard, getImageBlockSize(&pImage->Header) - offDiscard);
2988 Assert(!(cbDiscard % 512));
2989
2990 if (pcbPreAllocated)
2991 *pcbPreAllocated = 0;
2992
2993 if (pcbPostAllocated)
2994 *pcbPostAllocated = 0;
2995
2996 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
2997 {
2998 uint8_t *pbBlockData;
2999 size_t cbPreAllocated, cbPostAllocated;
3000
3001 cbPreAllocated = offDiscard % getImageBlockSize(&pImage->Header);
3002 cbPostAllocated = getImageBlockSize(&pImage->Header) - cbDiscard - cbPreAllocated;
3003
3004 /* Read the block data. */
3005 pvBlock = RTMemAlloc(pImage->cbTotalBlockData);
3006 if (!pvBlock)
3007 {
3008 rc = VERR_NO_MEMORY;
3009 break;
3010 }
3011
3012 if (!cbPreAllocated && !cbPostAllocated)
3013 {
3014 /*
3015 * Discarding a whole block, don't check for allocated sectors.
3016 * It is possible to just remove the whole block which avoids
3017 * one read and checking the whole block for data.
3018 */
3019 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
3020 }
3021 else if (fDiscard & VD_DISCARD_MARK_UNUSED)
3022 {
3023 /* Just zero out the given range. */
3024 memset(pvBlock, 0, cbDiscard);
3025
3026 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData + offDiscard;
3027 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
3028 u64Offset, pvBlock, cbDiscard, pIoCtx,
3029 NULL, NULL);
3030 RTMemFree(pvBlock);
3031 }
3032 else
3033 {
3034 /*
3035 * Read complete block as metadata, the I/O context has no memory buffer
3036 * and we need to access the content directly anyway.
3037 */
3038 PVDMETAXFER pMetaXfer;
3039 pbBlockData = (uint8_t *)pvBlock + pImage->offStartBlockData;
3040
3041 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData + pImage->offStartData;
3042 rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
3043 pbBlockData, pImage->cbTotalBlockData,
3044 pIoCtx, &pMetaXfer, NULL, NULL);
3045 if (RT_FAILURE(rc))
3046 break;
3047
3048 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
3049
3050 /* Clear data. */
3051 memset(pbBlockData + offDiscard , 0, cbDiscard);
3052
3053 Assert(!(cbDiscard % 4));
3054 Assert(getImageBlockSize(&pImage->Header) * 8 <= UINT32_MAX);
3055 if (ASMBitFirstSet((volatile void *)pbBlockData, getImageBlockSize(&pImage->Header) * 8) == -1)
3056 rc = vdiDiscardBlockAsync(pImage, pIoCtx, uBlock, pvBlock);
3057 else
3058 {
3059 /* Block has data, create allocation bitmap. */
3060 *pcbPreAllocated = cbPreAllocated;
3061 *pcbPostAllocated = cbPostAllocated;
3062 *ppbmAllocationBitmap = vdiAllocationBitmapCreate(pbBlockData, getImageBlockSize(&pImage->Header));
3063 if (RT_UNLIKELY(!*ppbmAllocationBitmap))
3064 rc = VERR_NO_MEMORY;
3065 else
3066 rc = VERR_VD_DISCARD_ALIGNMENT_NOT_MET;
3067
3068 RTMemFree(pvBlock);
3069 }
3070 } /* if: no complete block discarded */
3071 } /* if: Block is allocated. */
3072 /* else: nothing to do. */
3073 } while (0);
3074
3075 if (pcbActuallyDiscarded)
3076 *pcbActuallyDiscarded = cbDiscard;
3077
3078 LogFlowFunc(("returns %Rrc\n", rc));
3079 return rc;
3080}
3081
3082VBOXHDDBACKEND g_VDIBackend =
3083{
3084 /* pszBackendName */
3085 "VDI",
3086 /* cbSize */
3087 sizeof(VBOXHDDBACKEND),
3088 /* uBackendCaps */
3089 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
3090 | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_DISCARD,
3091 /* paFileExtensions */
3092 s_aVdiFileExtensions,
3093 /* paConfigInfo */
3094 NULL,
3095 /* hPlugin */
3096 NIL_RTLDRMOD,
3097 /* pfnCheckIfValid */
3098 vdiCheckIfValid,
3099 /* pfnOpen */
3100 vdiOpen,
3101 /* pfnCreate */
3102 vdiCreate,
3103 /* pfnRename */
3104 vdiRename,
3105 /* pfnClose */
3106 vdiClose,
3107 /* pfnRead */
3108 vdiRead,
3109 /* pfnWrite */
3110 vdiWrite,
3111 /* pfnFlush */
3112 vdiFlush,
3113 /* pfnGetVersion */
3114 vdiGetVersion,
3115 /* pfnGetSize */
3116 vdiGetSize,
3117 /* pfnGetFileSize */
3118 vdiGetFileSize,
3119 /* pfnGetPCHSGeometry */
3120 vdiGetPCHSGeometry,
3121 /* pfnSetPCHSGeometry */
3122 vdiSetPCHSGeometry,
3123 /* pfnGetLCHSGeometry */
3124 vdiGetLCHSGeometry,
3125 /* pfnSetLCHSGeometry */
3126 vdiSetLCHSGeometry,
3127 /* pfnGetImageFlags */
3128 vdiGetImageFlags,
3129 /* pfnGetOpenFlags */
3130 vdiGetOpenFlags,
3131 /* pfnSetOpenFlags */
3132 vdiSetOpenFlags,
3133 /* pfnGetComment */
3134 vdiGetComment,
3135 /* pfnSetComment */
3136 vdiSetComment,
3137 /* pfnGetUuid */
3138 vdiGetUuid,
3139 /* pfnSetUuid */
3140 vdiSetUuid,
3141 /* pfnGetModificationUuid */
3142 vdiGetModificationUuid,
3143 /* pfnSetModificationUuid */
3144 vdiSetModificationUuid,
3145 /* pfnGetParentUuid */
3146 vdiGetParentUuid,
3147 /* pfnSetParentUuid */
3148 vdiSetParentUuid,
3149 /* pfnGetParentModificationUuid */
3150 vdiGetParentModificationUuid,
3151 /* pfnSetParentModificationUuid */
3152 vdiSetParentModificationUuid,
3153 /* pfnDump */
3154 vdiDump,
3155 /* pfnGetTimeStamp */
3156 NULL,
3157 /* pfnGetParentTimeStamp */
3158 NULL,
3159 /* pfnSetParentTimeStamp */
3160 NULL,
3161 /* pfnGetParentFilename */
3162 NULL,
3163 /* pfnSetParentFilename */
3164 NULL,
3165 /* pfnAsyncRead */
3166 vdiAsyncRead,
3167 /* pfnAsyncWrite */
3168 vdiAsyncWrite,
3169 /* pfnAsyncFlush */
3170 vdiAsyncFlush,
3171 /* pfnComposeLocation */
3172 genericFileComposeLocation,
3173 /* pfnComposeName */
3174 genericFileComposeName,
3175 /* pfnCompact */
3176 vdiCompact,
3177 /* pfnResize */
3178 vdiResize,
3179 /* pfnDiscard */
3180 vdiDiscard,
3181 /* pfnAsyncDiscard */
3182 vdiAsyncDiscard
3183};
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