VirtualBox

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

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

VDI: use VERR_VD_VDI_INVALID_HEADER instead of VERR_VD_VDI_INVALID_SIGNATURE. VDGetFormat uses the return codes to check fir valid images and woul break outside of the loop on non VDI images which have another format but the backend is behind the VDI backend

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