VirtualBox

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

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

VBoxHDD/VDI: fix heap corruption in compacting VDI.

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