VirtualBox

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

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

VD: Initial support to discard unused blocks in an image + support for VDI images

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