VirtualBox

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

Last change on this file since 7780 was 7780, checked in by vboxsync, 17 years ago

Add function to query all available VD backends, together with their capabilities. Also added a flag to the write operation (purely a dummy at the moment).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.0 KB
Line 
1/** @file
2 * Virtual Disk Image (VDI), Core Code.
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
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 "VBoxHDD-newInternal.h"
22#define VBOX_VDICORE_VD /* Signal that the header is included from here. */
23#include "VDICore.h"
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/uuid.h>
30#include <iprt/file.h>
31#include <iprt/string.h>
32#include <iprt/asm.h>
33
34#define VDI_IMAGE_DEFAULT_BLOCK_SIZE _1M
35
36/*******************************************************************************
37* Internal Functions *
38*******************************************************************************/
39static unsigned getPowerOfTwo(unsigned uNumber);
40static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
41static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
42static void vdiInitHeader(PVDIHEADER pHeader, VDIMAGETYPE enmType,
43 uint32_t uImageFlags, const char *pszComment,
44 uint64_t cbDisk, uint32_t cbBlock,
45 uint32_t cbBlockExtra);
46static int vdiValidateHeader(PVDIHEADER pHeader);
47static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
48static int vdiUpdateHeader(PVDIIMAGEDESC pImage);
49static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
50static void vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete);
51
52
53/**
54 * Internal: signal an error to the frontend.
55 */
56DECLINLINE(int) vdiError(PVDIIMAGEDESC pImage, int rc, RT_SRC_POS_DECL,
57 const char *pszFormat, ...)
58{
59 va_list va;
60 va_start(va, pszFormat);
61 if (pImage->pfnError)
62 pImage->pfnError(pImage->pvErrorUser, rc, RT_SRC_POS_ARGS,
63 pszFormat, va);
64 va_end(va);
65 return rc;
66}
67
68
69/**
70 * internal: return power of 2 or 0 if num error.
71 */
72static unsigned getPowerOfTwo(unsigned uNumber)
73{
74 if (uNumber == 0)
75 return 0;
76 unsigned uPower2 = 0;
77 while ((uNumber & 1) == 0)
78 {
79 uNumber >>= 1;
80 uPower2++;
81 }
82 return uNumber == 1 ? uPower2 : 0;
83}
84
85
86/**
87 * Internal: Init VDI preheader.
88 */
89static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
90{
91 pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
92 pPreHdr->u32Version = VDI_IMAGE_VERSION;
93 memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
94 strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
95}
96
97/**
98 * Internal: check VDI preheader.
99 */
100static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
101{
102 if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
103 return VERR_VDI_INVALID_SIGNATURE;
104
105 if ( VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
106 && pPreHdr->u32Version != 0x00000002) /* old version. */
107 return VERR_VDI_UNSUPPORTED_VERSION;
108
109 return VINF_SUCCESS;
110}
111
112/**
113 * Internal: Init VDI header. Always use latest header version.
114 * @param pHeader Assumes it was initially initialized to all zeros.
115 */
116static void vdiInitHeader(PVDIHEADER pHeader, VDIMAGETYPE enmType,
117 uint32_t uImageFlags, const char *pszComment,
118 uint64_t cbDisk, uint32_t cbBlock,
119 uint32_t cbBlockExtra)
120{
121 pHeader->uVersion = VDI_IMAGE_VERSION;
122 pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
123 pHeader->u.v1.u32Type = (uint32_t)( enmType == VD_IMAGE_TYPE_NORMAL
124 ? VDI_IMAGE_TYPE_NORMAL
125 : VDI_IMAGE_TYPE_DIFF);
126 pHeader->u.v1.fFlags = uImageFlags;
127#ifdef VBOX_STRICT
128 char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
129 Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
130#endif
131 pHeader->u.v1.szComment[0] = '\0';
132 if (pszComment)
133 {
134 AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
135 ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
136 strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
137 }
138
139 /* Mark the legacy geometry not-calculated. */
140 pHeader->u.v1.LegacyGeometry.cCylinders = 0;
141 pHeader->u.v1.LegacyGeometry.cHeads = 0;
142 pHeader->u.v1.LegacyGeometry.cSectors = 0;
143 pHeader->u.v1.LegacyGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
144 pHeader->u.v1.u32Dummy = 0; /* used to be the translation value */
145
146 pHeader->u.v1.cbDisk = cbDisk;
147 pHeader->u.v1.cbBlock = cbBlock;
148 pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
149 if (cbDisk % cbBlock)
150 pHeader->u.v1.cBlocks++;
151 pHeader->u.v1.cbBlockExtra = cbBlockExtra;
152 pHeader->u.v1.cBlocksAllocated = 0;
153
154 /* Init offsets. */
155 pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
156 pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
157
158 /* Init uuids. */
159 RTUuidCreate(&pHeader->u.v1.uuidCreate);
160 RTUuidClear(&pHeader->u.v1.uuidModify);
161 RTUuidClear(&pHeader->u.v1.uuidLinkage);
162 RTUuidClear(&pHeader->u.v1.uuidParentModify);
163
164 /* Mark LCHS geometry not-calculated. */
165 pHeader->u.v1plus.LCHSGeometry.cCylinders = 0;
166 pHeader->u.v1plus.LCHSGeometry.cHeads = 0;
167 pHeader->u.v1plus.LCHSGeometry.cSectors = 0;
168 pHeader->u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
169}
170
171/**
172 * Internal: Check VDI header.
173 */
174static int vdiValidateHeader(PVDIHEADER pHeader)
175{
176 /* Check version-dependend header parameters. */
177 switch (GET_MAJOR_HEADER_VERSION(pHeader))
178 {
179 case 0:
180 {
181 /* Old header version. */
182 break;
183 }
184 case 1:
185 {
186 /* Current header version. */
187
188 if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
189 {
190 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
191 pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
192 return VERR_VDI_INVALID_HEADER;
193 }
194
195 if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
196 {
197 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
198 getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
199 return VERR_VDI_INVALID_HEADER;
200 }
201
202 if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
203 {
204 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
205 getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
206 return VERR_VDI_INVALID_HEADER;
207 }
208
209 break;
210 }
211 default:
212 /* Unsupported. */
213 return VERR_VDI_UNSUPPORTED_VERSION;
214 }
215
216 /* Check common header parameters. */
217
218 bool fFailed = false;
219
220 if ( getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
221 || getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
222 {
223 LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
224 fFailed = true;
225 }
226
227 if (getImageFlags(pHeader) & ~VD_VDI_IMAGE_FLAGS_MASK)
228 {
229 LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
230 fFailed = true;
231 }
232
233 if ( getImageLCHSGeometry(pHeader)
234 && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
235 {
236 LogRel(("VDI: wrong sector size (%d != %d)\n",
237 (getImageLCHSGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
238 fFailed = true;
239 }
240
241 if ( getImageDiskSize(pHeader) == 0
242 || getImageBlockSize(pHeader) == 0
243 || getImageBlocks(pHeader) == 0
244 || getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
245 {
246 LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
247 getImageDiskSize(pHeader), getImageBlockSize(pHeader),
248 getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
249 fFailed = true;
250 }
251
252 if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
253 {
254 LogRel(("VDI: too many blocks allocated (%d > %d)\n"
255 " blocksize=%d disksize=%lld\n",
256 getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
257 getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
258 fFailed = true;
259 }
260
261 if ( getImageExtraBlockSize(pHeader) != 0
262 && getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
263 {
264 LogRel(("VDI: wrong extra size (%d, %d)\n",
265 getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
266 fFailed = true;
267 }
268
269 if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
270 {
271 LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
272 getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
273 fFailed = true;
274 }
275
276 if (RTUuidIsNull(getImageCreationUUID(pHeader)))
277 {
278 LogRel(("VDI: uuid of creator is 0\n"));
279 fFailed = true;
280 }
281
282 if (RTUuidIsNull(getImageModificationUUID(pHeader)))
283 {
284 LogRel(("VDI: uuid of modificator is 0\n"));
285 fFailed = true;
286 }
287
288 return fFailed ? VERR_VDI_INVALID_HEADER : VINF_SUCCESS;
289}
290
291/**
292 * Internal: Set up VDIIMAGEDESC structure by image header.
293 */
294static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
295{
296 pImage->uImageFlags = getImageFlags(&pImage->Header);
297 pImage->offStartBlocks = getImageBlocksOffset(&pImage->Header);
298 pImage->offStartData = getImageDataOffset(&pImage->Header);
299 pImage->uBlockMask = getImageBlockSize(&pImage->Header) - 1;
300 pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
301 pImage->offStartBlockData = getImageExtraBlockSize(&pImage->Header);
302 pImage->cbTotalBlockData = pImage->offStartBlockData
303 + getImageBlockSize(&pImage->Header);
304}
305
306/**
307 * Internal: Create VDI image file.
308 */
309static int vdiCreateImage(PVDIIMAGEDESC pImage, VDIMAGETYPE enmType,
310 uint64_t cbSize, unsigned uImageFlags,
311 const char *pszComment,
312 PCPDMMEDIAGEOMETRY pPCHSGeometry,
313 PCPDMMEDIAGEOMETRY pLCHSGeometry,
314 PFNVMPROGRESS pfnProgress, void *pvUser,
315 unsigned uPercentStart, unsigned uPercentSpan)
316{
317 int rc;
318 RTFILE File;
319 uint64_t cbTotal;
320 uint64_t cbFill;
321 uint64_t uOff;
322
323 /* Special check for comment length. */
324 if ( VALID_PTR(pszComment)
325 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
326 {
327 rc = vdiError(pImage, VERR_VDI_COMMENT_TOO_LONG, RT_SRC_POS, N_("VDI: comment is too long for '%s'"), pImage->pszFilename);
328 goto out;
329 }
330 Assert(VALID_PTR(pPCHSGeometry));
331 Assert(VALID_PTR(pLCHSGeometry));
332
333 vdiInitPreHeader(&pImage->PreHeader);
334 vdiInitHeader(&pImage->Header, enmType, uImageFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
335 /* Save PCHS geometry. Not much work, and makes the flow of information
336 * quite a bit clearer - relying on the higher level isn't obvious. */
337 pImage->PCHSGeometry = *pPCHSGeometry;
338 /* Set LCHS geometry (legacy geometry is ignored for the current 1.1+). */
339 pImage->Header.u.v1plus.LCHSGeometry.cCylinders = pLCHSGeometry->cCylinders;
340 pImage->Header.u.v1plus.LCHSGeometry.cHeads = pLCHSGeometry->cHeads;
341 pImage->Header.u.v1plus.LCHSGeometry.cSectors = pLCHSGeometry->cSectors;
342 pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
343
344 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
345 if (!pImage->paBlocks)
346 {
347 rc = VERR_NO_MEMORY;
348 goto out;
349 }
350
351 if (enmType != VD_IMAGE_TYPE_FIXED)
352 {
353 /* for growing images mark all blocks in paBlocks as free. */
354 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
355 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
356 }
357 else
358 {
359 /* for fixed images mark all blocks in paBlocks as allocated */
360 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
361 pImage->paBlocks[i] = i;
362 pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
363 }
364
365 /* Setup image parameters. */
366 vdiSetupImageDesc(pImage);
367
368 /* Create image file. */
369 rc = RTFileOpen(&File, pImage->pszFilename,
370 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
371 if (VBOX_FAILURE(rc))
372 {
373 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: cannot create image '%s'"), pImage->pszFilename);
374 goto out;
375 }
376 pImage->File = File;
377
378 cbTotal = pImage->offStartData
379 + (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
380
381 if (enmType == VD_IMAGE_TYPE_FIXED)
382 {
383 /* Check the free space on the disk and leave early if there is not
384 * sufficient space available. */
385 RTFOFF cbFree = 0;
386 rc = RTFsQuerySizes(pImage->pszFilename, NULL, &cbFree, NULL, NULL);
387 if (VBOX_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbTotal))
388 {
389 rc = vdiError(pImage, VERR_DISK_FULL, RT_SRC_POS, N_("VDI: disk would overflow creating image '%s'"), pImage->pszFilename);
390 goto out;
391 }
392 }
393
394 if (enmType == VD_IMAGE_TYPE_FIXED)
395 {
396 /*
397 * Allocate & commit whole file if fixed image, it must be more
398 * effective than expanding file by write operations.
399 */
400 rc = RTFileSetSize(File, cbTotal);
401 }
402 else
403 {
404 /* Set file size to hold header and blocks array. */
405 rc = RTFileSetSize(pImage->File, pImage->offStartData);
406 }
407 if (VBOX_FAILURE(rc))
408 {
409 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: setting image size failed for '%s'"), pImage->pszFilename);
410 goto out;
411 }
412
413 /* Generate image last-modify uuid */
414 RTUuidCreate(getImageModificationUUID(&pImage->Header));
415
416 /* Write pre-header. */
417 rc = RTFileWriteAt(File, 0, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
418 if (VBOX_FAILURE(rc))
419 {
420 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing pre-header failed for '%s'"), pImage->pszFilename);
421 goto out;
422 }
423
424 /* Write header. */
425 rc = RTFileWriteAt(File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
426 if (VBOX_FAILURE(rc))
427 {
428 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing header failed for '%s'"), pImage->pszFilename);
429 goto out;
430 }
431
432 rc = RTFileWriteAt(File, pImage->offStartBlocks,
433 pImage->paBlocks,
434 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
435 NULL);
436 if (VBOX_FAILURE(rc))
437 {
438 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block pointers failed for '%s'"), pImage->pszFilename);
439 goto out;
440 }
441
442 if (enmType == VD_IMAGE_TYPE_FIXED)
443 {
444 /* Fill image with zeroes. We do this for every fixed-size image since on some systems
445 * (for example Windows Vista), it takes ages to write a block near the end of a sparse
446 * file and the guest could complain about an ATA timeout. */
447
448 /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
449 * Currently supported file systems are ext4 and ocfs2. */
450
451 /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
452 const size_t cbBuf = 128 * _1K;
453 void *pvBuf = RTMemTmpAllocZ(cbBuf);
454 if (!pvBuf)
455 {
456 rc = VERR_NO_MEMORY;
457 goto out;
458 }
459
460 cbFill = (uint64_t)getImageBlocks(&pImage->Header) * pImage->cbTotalBlockData;
461 uOff = 0;
462 /* Write data to all image blocks. */
463 while (uOff < cbFill)
464 {
465 unsigned cbChunk = (unsigned)RT_MIN(cbFill, cbBuf);
466
467 rc = RTFileWriteAt(File, pImage->offStartData + uOff,
468 pvBuf, cbChunk, NULL);
469 if (VBOX_FAILURE(rc))
470 {
471 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: writing block failed for '%s'"), pImage->pszFilename);
472 goto out;
473 }
474
475 uOff += cbChunk;
476
477 if (pfnProgress)
478 {
479 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
480 uPercentStart + uOff * uPercentSpan / cbFill,
481 pvUser);
482 if (VBOX_FAILURE(rc))
483 goto out;
484 }
485 }
486 RTMemTmpFree(pvBuf);
487 }
488
489out:
490 if (VBOX_SUCCESS(rc) && pfnProgress)
491 pfnProgress(NULL /* WARNING! pVM=NULL */,
492 uPercentStart + uPercentSpan, pvUser);
493
494 if (VBOX_FAILURE(rc))
495 vdiFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
496 return rc;
497}
498
499/**
500 * Internal: Open a VDI image.
501 */
502static int vdiOpenImage(PVDIIMAGEDESC pImage, unsigned uOpenFlags)
503{
504 int rc;
505 RTFILE File;
506
507 pImage->uOpenFlags = uOpenFlags;
508
509 /*
510 * Open the image.
511 */
512 rc = RTFileOpen(&File, pImage->pszFilename,
513 uOpenFlags & VD_OPEN_FLAGS_READONLY
514 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
515 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
516 if (VBOX_FAILURE(rc))
517 {
518 /* Do NOT signal an appropriate error here, as the VD layer has the
519 * choice of retrying the open if it failed. */
520 goto out;
521 }
522 pImage->File = File;
523
524 /* Read pre-header. */
525 rc = RTFileReadAt(File, 0, &pImage->PreHeader, sizeof(pImage->PreHeader),
526 NULL);
527 if (VBOX_FAILURE(rc))
528 {
529 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading pre-header in '%s'"), pImage->pszFilename);
530 goto out;
531 }
532 rc = vdiValidatePreHeader(&pImage->PreHeader);
533 if (VBOX_FAILURE(rc))
534 {
535 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: invalid pre-header in '%s'"), pImage->pszFilename);
536 goto out;
537 }
538
539 /* Read header. */
540 pImage->Header.uVersion = pImage->PreHeader.u32Version;
541 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
542 {
543 case 0:
544 rc = RTFileReadAt(File, sizeof(pImage->PreHeader),
545 &pImage->Header.u.v0, sizeof(pImage->Header.u.v0),
546 NULL);
547 if (VBOX_FAILURE(rc))
548 {
549 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v0 header in '%s'"), pImage->pszFilename);
550 goto out;
551 }
552 break;
553 case 1:
554 rc = RTFileReadAt(File, sizeof(pImage->PreHeader),
555 &pImage->Header.u.v1, sizeof(pImage->Header.u.v1),
556 NULL);
557 if (VBOX_FAILURE(rc))
558 {
559 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1 header in '%s'"), pImage->pszFilename);
560 goto out;
561 }
562 /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write mode.
563 * Conversion is harmless, as any VirtualBox version supporting VDI
564 * 1.1 doesn't touch fields it doesn't know about. */
565 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
566 && GET_MINOR_HEADER_VERSION(&pImage->Header) == 1
567 && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
568 {
569 pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
570 /* Mark LCHS geometry not-calculated. */
571 pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
572 pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
573 pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
574 pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
575 }
576 else if (pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
577 {
578 /* Read the actual VDI 1.1+ header completely. */
579 rc = RTFileReadAt(File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
580 if (VBOX_FAILURE(rc))
581 {
582 rc = vdiError(pImage, rc, RT_SRC_POS, N_("VDI: error reading v1.1+ header in '%s'"), pImage->pszFilename);
583 goto out;
584 }
585 }
586 break;
587 default:
588 rc = vdiError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: unsupported major version %u in '%s'"), GET_MAJOR_HEADER_VERSION(&pImage->Header), pImage->pszFilename);
589 goto out;
590 }
591
592 rc = vdiValidateHeader(&pImage->Header);
593 if (VBOX_FAILURE(rc))
594 {
595 rc = vdiError(pImage, VERR_VDI_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VDI: invalid header in '%s'"), pImage->pszFilename);
596 goto out;
597 }
598
599 /* Setup image parameters by header. */
600 vdiSetupImageDesc(pImage);
601
602 /* Allocate memory for blocks array. */
603 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
604 if (!pImage->paBlocks)
605 {
606 rc = VERR_NO_MEMORY;
607 goto out;
608 }
609
610 /* Read blocks array. */
611 rc = RTFileReadAt(pImage->File, pImage->offStartBlocks, pImage->paBlocks,
612 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
613 NULL);
614
615out:
616 if (VBOX_FAILURE(rc))
617 vdiFreeImage(pImage, false);
618 return rc;
619}
620
621/**
622 * Internal: Save header to file.
623 */
624static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
625{
626 int rc;
627 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
628 {
629 case 0:
630 rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
631 break;
632 case 1:
633 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
634 rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
635 else
636 rc = RTFileWriteAt(pImage->File, sizeof(VDIPREHEADER), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
637 break;
638 default:
639 rc = VERR_VDI_UNSUPPORTED_VERSION;
640 break;
641 }
642 AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->pszFilename, rc));
643 return rc;
644}
645
646/**
647 * Internal: Save block pointer to file, save header to file.
648 */
649static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
650{
651 /* Update image header. */
652 int rc = vdiUpdateHeader(pImage);
653 if (VBOX_SUCCESS(rc))
654 {
655 /* write only one block pointer. */
656 rc = RTFileWriteAt(pImage->File,
657 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
658 &pImage->paBlocks[uBlock],
659 sizeof(VDIIMAGEBLOCKPOINTER),
660 NULL);
661 AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
662 uBlock, pImage->pszFilename, rc));
663 }
664 return rc;
665}
666
667/**
668 * Internal: Flush the image file to disk.
669 */
670static void vdiFlushImage(PVDIIMAGEDESC pImage)
671{
672 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
673 {
674 /* Save header. */
675 int rc = vdiUpdateHeader(pImage);
676 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
677 pImage->pszFilename, rc));
678 RTFileFlush(pImage->File);
679 }
680}
681
682/**
683 * Internal: Free all allocated space for representing an image, and optionally
684 * delete the image from disk.
685 */
686static void vdiFreeImage(PVDIIMAGEDESC pImage, bool fDelete)
687{
688 Assert(VALID_PTR(pImage));
689
690 vdiFlushImage(pImage);
691 if (pImage->File != NIL_RTFILE)
692 {
693 RTFileClose(pImage->File);
694 pImage->File = NIL_RTFILE;
695 }
696 if (pImage->paBlocks)
697 {
698 RTMemFree(pImage->paBlocks);
699 pImage->paBlocks = NULL;
700 }
701 if (fDelete && pImage->pszFilename)
702 RTFileDelete(pImage->pszFilename);
703}
704
705
706/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
707static int vdiCheckIfValid(const char *pszFilename)
708{
709 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
710 int rc = VINF_SUCCESS;
711 PVDIIMAGEDESC pImage;
712
713 if ( !VALID_PTR(pszFilename)
714 || !*pszFilename)
715 {
716 rc = VERR_INVALID_PARAMETER;
717 goto out;
718 }
719
720 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
721 if (!pImage)
722 {
723 rc = VERR_NO_MEMORY;
724 goto out;
725 }
726 pImage->pszFilename = pszFilename;
727
728 rc = vdiOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
729 vdiFreeImage(pImage, false);
730
731out:
732 LogFlowFunc(("returns %Vrc\n", rc));
733 return rc;
734}
735
736/** @copydoc VBOXHDDBACKEND::pfnOpen */
737static int vdiOpen(const char *pszFilename, unsigned uOpenFlags,
738 PFNVDERROR pfnError, void *pvErrorUser,
739 void **ppBackendData)
740{
741 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x ppBackendData=%#p\n", pszFilename, uOpenFlags, ppBackendData));
742 int rc;
743 PVDIIMAGEDESC pImage;
744
745 /* Check open flags. All valid flags are supported. */
746 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
747 {
748 rc = VERR_INVALID_PARAMETER;
749 goto out;
750 }
751
752 /* Check remaining arguments. */
753 if ( !VALID_PTR(pszFilename)
754 || !*pszFilename)
755 {
756 rc = VERR_INVALID_PARAMETER;
757 goto out;
758 }
759
760 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
761 if (!pImage)
762 {
763 rc = VERR_NO_MEMORY;
764 goto out;
765 }
766 pImage->pszFilename = pszFilename;
767 pImage->paBlocks = NULL;
768 pImage->pfnError = pfnError;
769 pImage->pvErrorUser = pvErrorUser;
770
771 rc = vdiOpenImage(pImage, uOpenFlags);
772 if (VBOX_SUCCESS(rc))
773 *ppBackendData = pImage;
774
775out:
776 LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData));
777 return rc;
778}
779
780/** @copydoc VBOXHDDBACKEND::pfnCreate */
781static int vdiCreate(const char *pszFilename, VDIMAGETYPE enmType,
782 uint64_t cbSize, unsigned uImageFlags,
783 const char *pszComment,
784 PCPDMMEDIAGEOMETRY pPCHSGeometry,
785 PCPDMMEDIAGEOMETRY pLCHSGeometry,
786 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
787 void *pvUser, unsigned uPercentStart,
788 unsigned uPercentSpan, PFNVDERROR pfnError,
789 void *pvErrorUser, void **ppBackendData)
790{
791 LogFlowFunc(("pszFilename=\"%s\" enmType=%d cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p uOpenFlags=%#x pfnProgress=%#p pvUser=%#p uPercentStart=%u uPercentSpan=%u pfnError=%#p pvErrorUser=%#p ppBackendData=%#p", pszFilename, enmType, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, uOpenFlags, pfnProgress, pvUser, uPercentStart, uPercentSpan, pfnError, pvErrorUser, ppBackendData));
792 int rc;
793 PVDIIMAGEDESC pImage;
794
795 /* Check open flags. All valid flags are supported. */
796 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
797 {
798 rc = VERR_INVALID_PARAMETER;
799 goto out;
800 }
801
802 /* Check remaining arguments. */
803 if ( !VALID_PTR(pszFilename)
804 || !*pszFilename
805 || (enmType != VD_IMAGE_TYPE_NORMAL && enmType != VD_IMAGE_TYPE_FIXED)
806 || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE
807 || !VALID_PTR(pPCHSGeometry)
808 || !VALID_PTR(pLCHSGeometry))
809 {
810 rc = VERR_INVALID_PARAMETER;
811 goto out;
812 }
813
814 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
815 if (!pImage)
816 {
817 rc = VERR_NO_MEMORY;
818 goto out;
819 }
820 pImage->pszFilename = pszFilename;
821 pImage->paBlocks = NULL;
822 pImage->pfnError = pfnError;
823 pImage->pvErrorUser = pvErrorUser;
824
825 rc = vdiCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment,
826 pPCHSGeometry, pLCHSGeometry,
827 pfnProgress, pvUser, uPercentStart, uPercentSpan);
828 if (VBOX_SUCCESS(rc))
829 {
830 /* So far the image is opened in read/write mode. Make sure the
831 * image is opened in read-only mode if the caller requested that. */
832 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
833 {
834 vdiFreeImage(pImage, false);
835 rc = vdiOpenImage(pImage, uOpenFlags);
836 if (VBOX_FAILURE(rc))
837 goto out;
838 }
839 *ppBackendData = pImage;
840 }
841
842out:
843 LogFlowFunc(("returns %Vrc (pBackendData=%#p)\n", rc, *ppBackendData));
844 return rc;
845}
846
847/** @copydoc VBOXHDDBACKEND::pfnRename */
848static int vdiRename(void *pBackendData, const char *pszFilename)
849{
850 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
851
852 int rc = VINF_SUCCESS;
853 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
854
855 /* Check arguments. */
856 if ( !pImage
857 || !pszFilename
858 || !*pszFilename)
859 {
860 rc = VERR_INVALID_PARAMETER;
861 goto out;
862 }
863
864 /* Close the image. */
865 vdiFreeImage(pImage, false);
866
867 /* Rename the file. */
868 rc = RTFileMove(pImage->pszFilename, pszFilename, 0);
869 if (VBOX_FAILURE(rc))
870 {
871 /* The move failed, try to reopen the original image. */
872 int rc2 = vdiOpenImage(pImage, pImage->uOpenFlags);
873 if (VBOX_FAILURE(rc2))
874 rc = rc2;
875
876 goto out;
877 }
878
879 /* Update pImage with the new information. */
880 pImage->pszFilename = pszFilename;
881
882 /* Open the new image. */
883 rc = vdiOpenImage(pImage, pImage->uOpenFlags);
884 if (VBOX_FAILURE(rc))
885 goto out;
886
887out:
888 LogFlowFunc(("returns %Vrc\n", rc));
889 return rc;
890}
891
892/** @copydoc VBOXHDDBACKEND::pfnClose */
893static int vdiClose(void *pBackendData, bool fDelete)
894{
895 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
896 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
897 int rc = VINF_SUCCESS;
898
899 /* Freeing a never allocated image (e.g. because the open failed) is
900 * not signalled as an error. After all nothing bad happens. */
901 if (pImage)
902 vdiFreeImage(pImage, fDelete);
903
904 LogFlowFunc(("returns %Vrc\n", rc));
905 return rc;
906}
907
908/** @copydoc VBOXHDDBACKEND::pfnRead */
909static int vdiRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
910 size_t cbToRead, size_t *pcbActuallyRead)
911{
912 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n", pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
913 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
914 unsigned uBlock;
915 unsigned offRead;
916 int rc;
917
918 Assert(VALID_PTR(pImage));
919 Assert(!(uOffset % 512));
920 Assert(!(cbToRead % 512));
921
922 if ( uOffset + cbToRead > getImageDiskSize(&pImage->Header)
923 || !VALID_PTR(pvBuf)
924 || !cbToRead)
925 {
926 rc = VERR_INVALID_PARAMETER;
927 goto out;
928 }
929
930 /* Calculate starting block number and offset inside it. */
931 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
932 offRead = (unsigned)uOffset & pImage->uBlockMask;
933
934 /* Clip read range to at most the rest of the block. */
935 cbToRead = RT_MIN(cbToRead, getImageBlockSize(&pImage->Header) - offRead);
936 Assert(!(cbToRead % 512));
937
938 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
939 rc = VERR_VDI_BLOCK_FREE;
940 else if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
941 {
942 memset(pvBuf, 0, cbToRead);
943 rc = VINF_SUCCESS;
944 }
945 else
946 {
947 /* Block present in image file, read relevant data. */
948 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
949 + (pImage->offStartData + pImage->offStartBlockData + offRead);
950 rc = RTFileReadAt(pImage->File, u64Offset, pvBuf, cbToRead, NULL);
951 }
952
953 if (VBOX_SUCCESS(rc))
954 *pcbActuallyRead = cbToRead;
955
956out:
957 LogFlowFunc(("returns %Vrc\n", rc));
958 return rc;
959}
960
961/**@copydoc VBOXHDDBACKEND::pfnWrite */
962static int vdiWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
963 size_t cbToWrite, size_t *pcbWriteProcess,
964 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
965{
966 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
967 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
968 unsigned uBlock;
969 unsigned offWrite;
970 int rc = VINF_SUCCESS;
971
972 Assert(VALID_PTR(pImage));
973 Assert(!(uOffset % 512));
974 Assert(!(cbToWrite % 512));
975
976 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
977 {
978 rc = VERR_VDI_IMAGE_READ_ONLY;
979 goto out;
980 }
981
982 if (!VALID_PTR(pvBuf) || !cbToWrite)
983 {
984 rc = VERR_INVALID_PARAMETER;
985 goto out;
986 }
987
988 /* No size check here, will do that later. For dynamic images which are
989 * not multiples of the block size in length, this would prevent writing to
990 * the last grain. */
991
992 /* Calculate starting block number and offset inside it. */
993 uBlock = (unsigned)(uOffset >> pImage->uShiftOffset2Index);
994 offWrite = (unsigned)uOffset & pImage->uBlockMask;
995
996 /* Clip write range to at most the rest of the block. */
997 cbToWrite = RT_MIN(cbToWrite, getImageBlockSize(&pImage->Header) - offWrite);
998 Assert(!(cbToWrite % 512));
999
1000 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1001 {
1002 /* Block is either free or zero. */
1003 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
1004 && ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1005 || cbToWrite == getImageBlockSize(&pImage->Header)))
1006 {
1007 /* If the destination block is unallocated at this point, it's
1008 * either a zero block or a block which hasn't been used so far
1009 * (which also means that it's a zero block. Don't need to write
1010 * anything to this block if the data consists of just zeroes. */
1011 Assert(!(cbToWrite % 4));
1012 Assert(cbToWrite * 8 <= UINT32_MAX);
1013 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
1014 {
1015 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1016 goto out;
1017 }
1018 }
1019
1020 if (cbToWrite == getImageBlockSize(&pImage->Header))
1021 {
1022 /* Full block write to previously unallocated block.
1023 * Allocate block and write data. */
1024 Assert(!offWrite);
1025 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1026 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
1027 + (pImage->offStartData + pImage->offStartBlockData);
1028 rc = RTFileWriteAt(pImage->File, u64Offset, pvBuf, cbToWrite, NULL);
1029 if (VBOX_FAILURE(rc))
1030 goto out;
1031 pImage->paBlocks[uBlock] = cBlocksAllocated;
1032 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
1033
1034 rc = vdiUpdateBlockInfo(pImage, uBlock);
1035 if (VBOX_FAILURE(rc))
1036 goto out;
1037
1038 *pcbPreRead = 0;
1039 *pcbPostRead = 0;
1040 }
1041 else
1042 {
1043 /* Trying to do a partial write to an unallocated block. Don't do
1044 * anything except letting the upper layer know what to do. */
1045 *pcbPreRead = offWrite % getImageBlockSize(&pImage->Header);
1046 *pcbPostRead = getImageBlockSize(&pImage->Header) - cbToWrite - *pcbPreRead;
1047 rc = VERR_VDI_BLOCK_FREE;
1048 }
1049 }
1050 else
1051 {
1052 /* Block present in image file, write relevant data. */
1053 uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
1054 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
1055 rc = RTFileWriteAt(pImage->File, u64Offset, pvBuf, cbToWrite, NULL);
1056 }
1057
1058out:
1059 LogFlowFunc(("returns %Vrc\n", rc));
1060 return rc;
1061}
1062
1063/** @copydoc VBOXHDDBACKEND::pfnFlush */
1064static int vdiFlush(void *pBackendData)
1065{
1066 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1067 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1068 int rc = VINF_SUCCESS;
1069
1070 vdiFlushImage(pImage);
1071 LogFlowFunc(("returns %Vrc\n", rc));
1072 return rc;
1073}
1074
1075/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1076static unsigned vdiGetVersion(void *pBackendData)
1077{
1078 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1079 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1080 unsigned uVersion;
1081
1082 Assert(VALID_PTR(pImage));
1083
1084 if (pImage)
1085 uVersion = pImage->PreHeader.u32Version;
1086 else
1087 uVersion = 0;
1088
1089 LogFlowFunc(("returns %#x\n", uVersion));
1090 return uVersion;
1091}
1092
1093/** @copydoc VBOXHDDBACKEND::pfnGetImageType */
1094static int vdiGetImageType(void *pBackendData, PVDIMAGETYPE penmImageType)
1095{
1096 LogFlowFunc(("pBackendData=%#p penmImageType=%#p\n", pBackendData, penmImageType));
1097 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1098 int rc = VINF_SUCCESS;
1099
1100 Assert(VALID_PTR(pImage));
1101 Assert(VALID_PTR(penmImageType));
1102
1103 if (pImage)
1104 *penmImageType = getImageType(&pImage->Header) == VDI_IMAGE_TYPE_NORMAL
1105 ? VD_IMAGE_TYPE_NORMAL
1106 : VD_IMAGE_TYPE_DIFF;
1107 else
1108 rc = VERR_VDI_NOT_OPENED;
1109
1110 LogFlowFunc(("returns %Vrc enmImageType=%u\n", rc, *penmImageType));
1111 return rc;
1112}
1113
1114/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1115static uint64_t vdiGetSize(void *pBackendData)
1116{
1117 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1118 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1119 uint64_t cbSize;
1120
1121 Assert(VALID_PTR(pImage));
1122
1123 if (pImage)
1124 cbSize = getImageDiskSize(&pImage->Header);
1125 else
1126 cbSize = 0;
1127
1128 LogFlowFunc(("returns %llu\n", cbSize));
1129 return cbSize;
1130}
1131
1132/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1133static uint64_t vdiGetFileSize(void *pBackendData)
1134{
1135 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1136 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1137 uint64_t cb = 0;
1138
1139 Assert(VALID_PTR(pImage));
1140
1141 if (pImage)
1142 {
1143 uint64_t cbFile;
1144 if (pImage->File != NIL_RTFILE)
1145 {
1146 int rc = RTFileGetSize(pImage->File, &cbFile);
1147 if (VBOX_SUCCESS(rc))
1148 cb += cbFile;
1149 }
1150 }
1151
1152 LogFlowFunc(("returns %lld\n", cb));
1153 return cb;
1154}
1155
1156/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1157static int vdiGetPCHSGeometry(void *pBackendData,
1158 PPDMMEDIAGEOMETRY pPCHSGeometry)
1159{
1160 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1161 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1162 int rc;
1163
1164 Assert(VALID_PTR(pImage));
1165
1166 if (pImage)
1167 {
1168 if (pImage->PCHSGeometry.cCylinders)
1169 {
1170 *pPCHSGeometry = pImage->PCHSGeometry;
1171 rc = VINF_SUCCESS;
1172 }
1173 else
1174 rc = VERR_VDI_GEOMETRY_NOT_SET;
1175 }
1176 else
1177 rc = VERR_VDI_NOT_OPENED;
1178
1179 LogFlowFunc(("returns %Vrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1180 return rc;
1181}
1182
1183/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1184static int vdiSetPCHSGeometry(void *pBackendData,
1185 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1186{
1187 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1188 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1189 int rc;
1190
1191 Assert(VALID_PTR(pImage));
1192
1193 if (pImage)
1194 {
1195 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1196 {
1197 rc = VERR_VDI_IMAGE_READ_ONLY;
1198 goto out;
1199 }
1200
1201 pImage->PCHSGeometry = *pPCHSGeometry;
1202 rc = VINF_SUCCESS;
1203 }
1204 else
1205 rc = VERR_VDI_NOT_OPENED;
1206
1207out:
1208 LogFlowFunc(("returns %Vrc\n", rc));
1209 return rc;
1210}
1211
1212/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1213static int vdiGetLCHSGeometry(void *pBackendData,
1214 PPDMMEDIAGEOMETRY pLCHSGeometry)
1215{
1216 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1217 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1218 int rc;
1219
1220 Assert(VALID_PTR(pImage));
1221
1222 if (pImage)
1223 {
1224 VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
1225 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pImage->Header);
1226 if (!pGeometry)
1227 pGeometry = &DummyGeo;
1228
1229 if ( pGeometry->cCylinders > 0
1230 && pGeometry->cHeads > 0
1231 && pGeometry->cSectors > 0)
1232 {
1233 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
1234 pLCHSGeometry->cHeads = pGeometry->cHeads;
1235 pLCHSGeometry->cSectors = pGeometry->cSectors;
1236 rc = VINF_SUCCESS;
1237 }
1238 else
1239 rc = VERR_VDI_GEOMETRY_NOT_SET;
1240 }
1241 else
1242 rc = VERR_VDI_NOT_OPENED;
1243
1244 LogFlowFunc(("returns %Vrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1245 return rc;
1246}
1247
1248/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1249static int vdiSetLCHSGeometry(void *pBackendData,
1250 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1251{
1252 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1253 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1254 PVDIDISKGEOMETRY pGeometry;
1255 int rc;
1256
1257 Assert(VALID_PTR(pImage));
1258
1259 if (pImage)
1260 {
1261 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1262 {
1263 rc = VERR_VDI_IMAGE_READ_ONLY;
1264 goto out;
1265 }
1266
1267 pGeometry = getImageLCHSGeometry(&pImage->Header);
1268 if (pGeometry)
1269 {
1270 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
1271 pGeometry->cHeads = pLCHSGeometry->cHeads;
1272 pGeometry->cSectors = pLCHSGeometry->cSectors;
1273 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
1274
1275 /* Update header information in base image file. */
1276 vdiFlushImage(pImage);
1277 }
1278 rc = VINF_SUCCESS;
1279 }
1280 else
1281 rc = VERR_VDI_NOT_OPENED;
1282
1283out:
1284 LogFlowFunc(("returns %Vrc\n", rc));
1285 return rc;
1286}
1287
1288/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1289static unsigned vdiGetImageFlags(void *pBackendData)
1290{
1291 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1292 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1293 unsigned uImageFlags;
1294
1295 Assert(VALID_PTR(pImage));
1296
1297 if (pImage)
1298 uImageFlags = getImageFlags(&pImage->Header);
1299 else
1300 uImageFlags = 0;
1301
1302 LogFlowFunc(("returns %#x\n", uImageFlags));
1303 return uImageFlags;
1304}
1305
1306/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1307static unsigned vdiGetOpenFlags(void *pBackendData)
1308{
1309 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1310 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1311 unsigned uOpenFlags;
1312
1313 Assert(VALID_PTR(pImage));
1314
1315 if (pImage)
1316 uOpenFlags = pImage->uOpenFlags;
1317 else
1318 uOpenFlags = 0;
1319
1320 LogFlowFunc(("returns %#x\n", uOpenFlags));
1321 return uOpenFlags;
1322}
1323
1324/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1325static int vdiSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1326{
1327 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1328 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1329 int rc;
1330 const char *pszFilename;
1331
1332 /* Image must be opened and the new flags must be valid. Just readonly flag
1333 * is supported. */
1334 if (!pImage || uOpenFlags & ~VD_OPEN_FLAGS_READONLY)
1335 {
1336 rc = VERR_INVALID_PARAMETER;
1337 goto out;
1338 }
1339
1340 /* Implement this operation via reopening the image. */
1341 pszFilename = pImage->pszFilename;
1342 vdiFreeImage(pImage, false);
1343 rc = vdiOpenImage(pImage, uOpenFlags);
1344
1345out:
1346 LogFlowFunc(("returns %Vrc\n", rc));
1347 return rc;
1348}
1349
1350/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1351static int vdiGetComment(void *pBackendData, char *pszComment,
1352 size_t cbComment)
1353{
1354 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1355 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1356 int rc = VINF_SUCCESS;
1357
1358 Assert(VALID_PTR(pImage));
1359
1360 if (pImage)
1361 {
1362 char *pszTmp = getImageComment(&pImage->Header);
1363 unsigned cb = strlen(pszTmp);
1364 if (cb < cbComment)
1365 {
1366 /* memcpy is much better than strncpy. */
1367 memcpy(pszComment, pszTmp, cb + 1);
1368 }
1369 else
1370 rc = VERR_BUFFER_OVERFLOW;
1371 }
1372 else
1373 rc = VERR_VDI_NOT_OPENED;
1374
1375 LogFlowFunc(("returns %Vrc comment=\"%s\"\n", rc, pszComment));
1376 return rc;
1377}
1378
1379/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1380static int vdiSetComment(void *pBackendData, const char *pszComment)
1381{
1382 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1383 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1384 int rc;
1385
1386 Assert(VALID_PTR(pImage));
1387
1388 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1389 {
1390 rc = VERR_VDI_IMAGE_READ_ONLY;
1391 goto out;
1392 }
1393
1394 if (pImage)
1395 {
1396 size_t cchComment = pszComment ? strlen(pszComment) : 0;
1397 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
1398 {
1399 LogFunc(("pszComment is too long, %d bytes!\n", cchComment));
1400 rc = VERR_VDI_COMMENT_TOO_LONG;
1401 goto out;
1402 }
1403
1404 /* we don't support old style images */
1405 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1406 {
1407 /*
1408 * Update the comment field, making sure to zero out all of the previous comment.
1409 */
1410 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
1411 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
1412
1413 /* write out new the header */
1414 rc = vdiUpdateHeader(pImage);
1415 }
1416 else
1417 rc = VERR_VDI_UNSUPPORTED_VERSION;
1418 }
1419 else
1420 rc = VERR_VDI_NOT_OPENED;
1421
1422out:
1423 LogFlowFunc(("returns %Vrc\n", rc));
1424 return rc;
1425}
1426
1427/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
1428static int vdiGetUuid(void *pBackendData, PRTUUID pUuid)
1429{
1430 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1431 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1432 int rc;
1433
1434 Assert(VALID_PTR(pImage));
1435
1436 if (pImage)
1437 {
1438 *pUuid = *getImageCreationUUID(&pImage->Header);
1439 rc = VINF_SUCCESS;
1440 }
1441 else
1442 rc = VERR_VDI_NOT_OPENED;
1443
1444 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
1445 return rc;
1446}
1447
1448/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
1449static int vdiSetUuid(void *pBackendData, PCRTUUID pUuid)
1450{
1451 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
1452 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1453 int rc = VINF_SUCCESS;
1454
1455 Assert(VALID_PTR(pImage));
1456
1457 if (pImage)
1458 {
1459 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1460 {
1461 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1462 pImage->Header.u.v1.uuidCreate = *pUuid;
1463 /* Make it possible to clone old VDIs. */
1464 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
1465 pImage->Header.u.v0.uuidCreate = *pUuid;
1466 else
1467 {
1468 LogFunc(("Version is not supported!\n"));
1469 rc = VERR_VDI_UNSUPPORTED_VERSION;
1470 }
1471 }
1472 else
1473 rc = VERR_VDI_IMAGE_READ_ONLY;
1474 }
1475 else
1476 rc = VERR_VDI_NOT_OPENED;
1477
1478 LogFlowFunc(("returns %Vrc\n", rc));
1479 return rc;
1480}
1481
1482/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
1483static int vdiGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1484{
1485 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1486 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1487 int rc;
1488
1489 Assert(VALID_PTR(pImage));
1490
1491 if (pImage)
1492 {
1493 *pUuid = *getImageModificationUUID(&pImage->Header);
1494 rc = VINF_SUCCESS;
1495 }
1496 else
1497 rc = VERR_VDI_NOT_OPENED;
1498
1499 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
1500 return rc;
1501}
1502
1503/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
1504static int vdiSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1505{
1506 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
1507 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1508 int rc = VINF_SUCCESS;
1509
1510 Assert(VALID_PTR(pImage));
1511
1512 if (pImage)
1513 {
1514 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1515 {
1516 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1517 pImage->Header.u.v1.uuidModify = *pUuid;
1518 /* Make it possible to clone old VDIs. */
1519 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
1520 pImage->Header.u.v0.uuidModify = *pUuid;
1521 else
1522 {
1523 LogFunc(("Version is not supported!\n"));
1524 rc = VERR_VDI_UNSUPPORTED_VERSION;
1525 }
1526 }
1527 else
1528 rc = VERR_VDI_IMAGE_READ_ONLY;
1529 }
1530 else
1531 rc = VERR_VDI_NOT_OPENED;
1532
1533 LogFlowFunc(("returns %Vrc\n", rc));
1534 return rc;
1535}
1536
1537/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
1538static int vdiGetParentUuid(void *pBackendData, PRTUUID pUuid)
1539{
1540 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1541 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1542 int rc;
1543
1544 Assert(VALID_PTR(pImage));
1545
1546 if (pImage)
1547 {
1548 *pUuid = *getImageParentUUID(&pImage->Header);
1549 rc = VINF_SUCCESS;
1550 }
1551 else
1552 rc = VERR_VDI_NOT_OPENED;
1553
1554 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
1555 return rc;
1556}
1557
1558/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
1559static int vdiSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1560{
1561 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
1562 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1563 int rc = VINF_SUCCESS;
1564
1565 Assert(VALID_PTR(pImage));
1566
1567 if (pImage)
1568 {
1569 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1570 {
1571 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1572 pImage->Header.u.v1.uuidLinkage = *pUuid;
1573 /* Make it possible to clone old VDIs. */
1574 else if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0)
1575 pImage->Header.u.v0.uuidLinkage = *pUuid;
1576 else
1577 {
1578 LogFunc(("Version is not supported!\n"));
1579 rc = VERR_VDI_UNSUPPORTED_VERSION;
1580 }
1581 }
1582 else
1583 rc = VERR_VDI_IMAGE_READ_ONLY;
1584 }
1585 else
1586 rc = VERR_VDI_NOT_OPENED;
1587
1588 LogFlowFunc(("returns %Vrc\n", rc));
1589 return rc;
1590}
1591
1592/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
1593static int vdiGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1594{
1595 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1596 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1597 int rc;
1598
1599 Assert(VALID_PTR(pImage));
1600
1601 if (pImage)
1602 {
1603 *pUuid = *getImageParentModificationUUID(&pImage->Header);
1604 rc = VINF_SUCCESS;
1605 }
1606 else
1607 rc = VERR_VDI_NOT_OPENED;
1608
1609 LogFlowFunc(("returns %Vrc (%Vuuid)\n", rc, pUuid));
1610 return rc;
1611}
1612
1613/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
1614static int vdiSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1615{
1616 LogFlowFunc(("pBackendData=%#p Uuid=%Vuuid\n", pBackendData, pUuid));
1617 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1618 int rc = VINF_SUCCESS;
1619
1620 Assert(VALID_PTR(pImage));
1621
1622 if (pImage)
1623 {
1624 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1625 {
1626 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1627 pImage->Header.u.v1.uuidParentModify = *pUuid;
1628 else
1629 {
1630 LogFunc(("Version is not supported!\n"));
1631 rc = VERR_VDI_UNSUPPORTED_VERSION;
1632 }
1633 }
1634 else
1635 rc = VERR_VDI_IMAGE_READ_ONLY;
1636 }
1637 else
1638 rc = VERR_VDI_NOT_OPENED;
1639
1640 LogFlowFunc(("returns %Vrc\n", rc));
1641 return rc;
1642}
1643
1644/** @copydoc VBOXHDDBACKEND::pfnDump */
1645static void vdiDump(void *pBackendData)
1646{
1647 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
1648
1649 RTLogPrintf("Dumping VDI image \"%s\" mode=%s uOpenFlags=%X File=%08X\n",
1650 pImage->pszFilename,
1651 (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "r/o" : "r/w",
1652 pImage->uOpenFlags,
1653 pImage->File);
1654 RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
1655 pImage->PreHeader.u32Version,
1656 getImageType(&pImage->Header),
1657 getImageFlags(&pImage->Header),
1658 getImageDiskSize(&pImage->Header));
1659 RTLogPrintf("Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
1660 getImageBlockSize(&pImage->Header),
1661 getImageExtraBlockSize(&pImage->Header),
1662 getImageBlocks(&pImage->Header),
1663 getImageBlocksAllocated(&pImage->Header));
1664 RTLogPrintf("Header: offBlocks=%u offData=%u\n",
1665 getImageBlocksOffset(&pImage->Header),
1666 getImageDataOffset(&pImage->Header));
1667 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
1668 if (pg)
1669 RTLogPrintf("Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
1670 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
1671 RTLogPrintf("Header: uuidCreation={%Vuuid}\n", getImageCreationUUID(&pImage->Header));
1672 RTLogPrintf("Header: uuidModification={%Vuuid}\n", getImageModificationUUID(&pImage->Header));
1673 RTLogPrintf("Header: uuidParent={%Vuuid}\n", getImageParentUUID(&pImage->Header));
1674 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
1675 RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
1676 RTLogPrintf("Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
1677 pImage->uImageFlags, pImage->offStartBlocks, pImage->offStartData);
1678 RTLogPrintf("Image: uBlockMask=%08X cbTotalBlockData=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
1679 pImage->uBlockMask,
1680 pImage->cbTotalBlockData,
1681 pImage->uShiftOffset2Index,
1682 pImage->offStartBlockData);
1683
1684 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
1685 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
1686 {
1687 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1688 {
1689 cBlocksNotFree++;
1690 if (pImage->paBlocks[uBlock] >= cBlocks)
1691 cBadBlocks++;
1692 }
1693 }
1694 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
1695 {
1696 RTLogPrintf("!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
1697 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
1698 }
1699 if (cBadBlocks)
1700 {
1701 RTLogPrintf("!! WARNING: %u bad blocks found !!\n",
1702 cBadBlocks);
1703 }
1704}
1705
1706VBOXHDDBACKEND g_VDIBackend =
1707{
1708 /* pszBackendName */
1709 "VDI",
1710 /* cbSize */
1711 sizeof(VBOXHDDBACKEND),
1712 /* uBackendCaps */
1713 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
1714 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF,
1715 /* pfnCheckIfValid */
1716 vdiCheckIfValid,
1717 /* pfnOpen */
1718 vdiOpen,
1719 /* pfnCreate */
1720 vdiCreate,
1721 /* pfnRename */
1722 vdiRename,
1723 /* pfnClose */
1724 vdiClose,
1725 /* pfnRead */
1726 vdiRead,
1727 /* pfnWrite */
1728 vdiWrite,
1729 /* pfnFlush */
1730 vdiFlush,
1731 /* pfnGetVersion */
1732 vdiGetVersion,
1733 /* pfnGetImageType */
1734 vdiGetImageType,
1735 /* pfnGetSize */
1736 vdiGetSize,
1737 /* pfnGetFileSize */
1738 vdiGetFileSize,
1739 /* pfnGetPCHSGeometry */
1740 vdiGetPCHSGeometry,
1741 /* pfnSetPCHSGeometry */
1742 vdiSetPCHSGeometry,
1743 /* pfnGetLCHSGeometry */
1744 vdiGetLCHSGeometry,
1745 /* pfnSetLCHSGeometry */
1746 vdiSetLCHSGeometry,
1747 /* pfnGetImageFlags */
1748 vdiGetImageFlags,
1749 /* pfnGetOpenFlags */
1750 vdiGetOpenFlags,
1751 /* pfnSetOpenFlags */
1752 vdiSetOpenFlags,
1753 /* pfnGetComment */
1754 vdiGetComment,
1755 /* pfnSetComment */
1756 vdiSetComment,
1757 /* pfnGetUuid */
1758 vdiGetUuid,
1759 /* pfnSetUuid */
1760 vdiSetUuid,
1761 /* pfnGetModificationUuid */
1762 vdiGetModificationUuid,
1763 /* pfnSetModificationUuid */
1764 vdiSetModificationUuid,
1765 /* pfnGetParentUuid */
1766 vdiGetParentUuid,
1767 /* pfnSetParentUuid */
1768 vdiSetParentUuid,
1769 /* pfnGetParentModificationUuid */
1770 vdiGetParentModificationUuid,
1771 /* pfnSetParentModificationUuid */
1772 vdiSetParentModificationUuid,
1773 /* pfnDump */
1774 vdiDump
1775};
1776
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