VirtualBox

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

Last change on this file since 11421 was 11421, checked in by vboxsync, 16 years ago

Storage/VBoxHDD-new: Added backend info, listing the supported file extensions. Implemented a testcase anf fixed a small bug which it found.

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