VirtualBox

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

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

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

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