VirtualBox

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

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

VD+VDI: Implement optimization for discard to avoid reading the complete data all the time and bug fixes

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