VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VDIHDDCore.cpp@ 28629

Last change on this file since 28629 was 28629, checked in by vboxsync, 15 years ago

VDI: Fix VdiCheckIfValid

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

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