VirtualBox

source: vbox/trunk/src/VBox/Storage/VHD.cpp@ 61600

Last change on this file since 61600 was 60608, checked in by vboxsync, 9 years ago

Main+Storage/VD: add a capability which marks preferred medium format backends

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 117.7 KB
Line 
1/* $Id: VHD.cpp 60608 2016-04-20 17:16:37Z vboxsync $ */
2/** @file
3 * VHD Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_VHD
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <VBox/version.h>
28#include <iprt/asm.h>
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31#include <iprt/uuid.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34
35#include "VDBackends.h"
36
37#define VHD_RELATIVE_MAX_PATH 512
38#define VHD_ABSOLUTE_MAX_PATH 512
39
40#define VHD_SECTOR_SIZE 512
41#define VHD_BLOCK_SIZE (2 * _1M)
42
43/* This is common to all VHD disk types and is located at the end of the image */
44#pragma pack(1)
45typedef struct VHDFooter
46{
47 char Cookie[8];
48 uint32_t Features;
49 uint32_t Version;
50 uint64_t DataOffset;
51 uint32_t Timestamp;
52 uint8_t CreatorApp[4];
53 uint32_t CreatorVer;
54 uint32_t CreatorOS;
55 uint64_t OrigSize;
56 uint64_t CurSize;
57 uint16_t DiskGeometryCylinder;
58 uint8_t DiskGeometryHeads;
59 uint8_t DiskGeometrySectors;
60 uint32_t DiskType;
61 uint32_t Checksum;
62 char UniqueID[16];
63 uint8_t SavedState;
64 uint8_t Reserved[427];
65} VHDFooter;
66#pragma pack()
67
68/* this really is spelled with only one n */
69#define VHD_FOOTER_COOKIE "conectix"
70#define VHD_FOOTER_COOKIE_SIZE 8
71
72#define VHD_FOOTER_FEATURES_NOT_ENABLED 0
73#define VHD_FOOTER_FEATURES_TEMPORARY 1
74#define VHD_FOOTER_FEATURES_RESERVED 2
75
76#define VHD_FOOTER_FILE_FORMAT_VERSION 0x00010000
77#define VHD_FOOTER_DATA_OFFSET_FIXED UINT64_C(0xffffffffffffffff)
78#define VHD_FOOTER_DISK_TYPE_FIXED 2
79#define VHD_FOOTER_DISK_TYPE_DYNAMIC 3
80#define VHD_FOOTER_DISK_TYPE_DIFFERENCING 4
81
82#define VHD_MAX_LOCATOR_ENTRIES 8
83#define VHD_PLATFORM_CODE_NONE 0
84#define VHD_PLATFORM_CODE_WI2R 0x57693272
85#define VHD_PLATFORM_CODE_WI2K 0x5769326B
86#define VHD_PLATFORM_CODE_W2RU 0x57327275
87#define VHD_PLATFORM_CODE_W2KU 0x57326B75
88#define VHD_PLATFORM_CODE_MAC 0x4D163220
89#define VHD_PLATFORM_CODE_MACX 0x4D163258
90
91/* Header for expanding disk images. */
92#pragma pack(1)
93typedef struct VHDParentLocatorEntry
94{
95 uint32_t u32Code;
96 uint32_t u32DataSpace;
97 uint32_t u32DataLength;
98 uint32_t u32Reserved;
99 uint64_t u64DataOffset;
100} VHDPLE, *PVHDPLE;
101
102typedef struct VHDDynamicDiskHeader
103{
104 char Cookie[8];
105 uint64_t DataOffset;
106 uint64_t TableOffset;
107 uint32_t HeaderVersion;
108 uint32_t MaxTableEntries;
109 uint32_t BlockSize;
110 uint32_t Checksum;
111 uint8_t ParentUuid[16];
112 uint32_t ParentTimestamp;
113 uint32_t Reserved0;
114 uint16_t ParentUnicodeName[256];
115 VHDPLE ParentLocatorEntry[VHD_MAX_LOCATOR_ENTRIES];
116 uint8_t Reserved1[256];
117} VHDDynamicDiskHeader;
118#pragma pack()
119
120#define VHD_DYNAMIC_DISK_HEADER_COOKIE "cxsparse"
121#define VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE 8
122#define VHD_DYNAMIC_DISK_HEADER_VERSION 0x00010000
123
124/**
125 * Complete VHD image data structure.
126 */
127typedef struct VHDIMAGE
128{
129 /** Image file name. */
130 const char *pszFilename;
131 /** Opaque storage handle. */
132 PVDIOSTORAGE pStorage;
133
134 /** Pointer to the per-disk VD interface list. */
135 PVDINTERFACE pVDIfsDisk;
136 /** Pointer to the per-image VD interface list. */
137 PVDINTERFACE pVDIfsImage;
138 /** Error interface. */
139 PVDINTERFACEERROR pIfError;
140 /** I/O interface. */
141 PVDINTERFACEIOINT pIfIo;
142
143 /** Open flags passed by VBoxHDD layer. */
144 unsigned uOpenFlags;
145 /** Image flags defined during creation or determined during open. */
146 unsigned uImageFlags;
147 /** Total size of the image. */
148 uint64_t cbSize;
149
150 /** Physical geometry of this image. */
151 VDGEOMETRY PCHSGeometry;
152 /** Logical geometry of this image. */
153 VDGEOMETRY LCHSGeometry;
154
155 /** Image UUID. */
156 RTUUID ImageUuid;
157 /** Parent image UUID. */
158 RTUUID ParentUuid;
159
160 /** Parent's time stamp at the time of image creation. */
161 uint32_t u32ParentTimestamp;
162 /** Relative path to the parent image. */
163 char *pszParentFilename;
164
165 /** The Block Allocation Table. */
166 uint32_t *pBlockAllocationTable;
167 /** Number of entries in the table. */
168 uint32_t cBlockAllocationTableEntries;
169
170 /** Size of one data block. */
171 uint32_t cbDataBlock;
172 /** Sectors per data block. */
173 uint32_t cSectorsPerDataBlock;
174 /** Length of the sector bitmap in bytes. */
175 uint32_t cbDataBlockBitmap;
176 /** A copy of the disk footer. */
177 VHDFooter vhdFooterCopy;
178 /** Current end offset of the file (without the disk footer). */
179 uint64_t uCurrentEndOfFile;
180 /** Size of the data block bitmap in sectors. */
181 uint32_t cDataBlockBitmapSectors;
182 /** Start of the block allocation table. */
183 uint64_t uBlockAllocationTableOffset;
184 /** Buffer to hold block's bitmap for bit search operations. */
185 uint8_t *pu8Bitmap;
186 /** Offset to the next data structure (dynamic disk header). */
187 uint64_t u64DataOffset;
188 /** Flag to force dynamic disk header update. */
189 bool fDynHdrNeedsUpdate;
190} VHDIMAGE, *PVHDIMAGE;
191
192/**
193 * Structure tracking the expansion process of the image
194 * for async access.
195 */
196typedef struct VHDIMAGEEXPAND
197{
198 /** Flag indicating the status of each step. */
199 volatile uint32_t fFlags;
200 /** The index in the block allocation table which is written. */
201 uint32_t idxBatAllocated;
202 /** Big endian representation of the block index
203 * which is written in the BAT. */
204 uint32_t idxBlockBe;
205 /** Old end of the file - used for rollback in case of an error. */
206 uint64_t cbEofOld;
207 /** Sector bitmap written to the new block - variable in size. */
208 uint8_t au8Bitmap[1];
209} VHDIMAGEEXPAND, *PVHDIMAGEEXPAND;
210
211/**
212 * Flag defines
213 */
214#define VHDIMAGEEXPAND_STEP_IN_PROGRESS (0x0)
215#define VHDIMAGEEXPAND_STEP_FAILED (0x2)
216#define VHDIMAGEEXPAND_STEP_SUCCESS (0x3)
217/** All steps completed successfully. */
218#define VHDIMAGEEXPAND_ALL_SUCCESS (0xff)
219/** All steps completed (no success indicator) */
220#define VHDIMAGEEXPAND_ALL_COMPLETE (0xaa)
221
222/** Every status field has 2 bits so we can encode 4 steps in one byte. */
223#define VHDIMAGEEXPAND_STATUS_MASK 0x03
224#define VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT 0x00
225#define VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT 0x02
226#define VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT 0x04
227#define VHDIMAGEEXPAND_BAT_STATUS_SHIFT 0x06
228
229/**
230 * Helper macros to get and set the status field.
231 */
232#define VHDIMAGEEXPAND_STATUS_GET(fFlags, cShift) \
233 (((fFlags) >> (cShift)) & VHDIMAGEEXPAND_STATUS_MASK)
234#define VHDIMAGEEXPAND_STATUS_SET(fFlags, cShift, uVal) \
235 ASMAtomicOrU32(&(fFlags), ((uVal) & VHDIMAGEEXPAND_STATUS_MASK) << (cShift))
236
237
238/*********************************************************************************************************************************
239* Static Variables *
240*********************************************************************************************************************************/
241
242/** NULL-terminated array of supported file extensions. */
243static const VDFILEEXTENSION s_aVhdFileExtensions[] =
244{
245 {"vhd", VDTYPE_HDD},
246 {NULL, VDTYPE_INVALID}
247};
248
249
250/*********************************************************************************************************************************
251* Internal Functions *
252*********************************************************************************************************************************/
253
254
255/**
256 * Internal: Compute and update header checksum.
257 */
258static uint32_t vhdChecksum(void *pHeader, uint32_t cbSize)
259{
260 uint32_t checksum = 0;
261 for (uint32_t i = 0; i < cbSize; i++)
262 checksum += ((unsigned char *)pHeader)[i];
263 return ~checksum;
264}
265
266/**
267 * Internal: Convert filename to UTF16 with appropriate endianness.
268 */
269static int vhdFilenameToUtf16(const char *pszFilename, uint16_t *pu16Buf,
270 uint32_t cbBufSize, uint32_t *pcbActualSize,
271 bool fBigEndian)
272{
273 int rc;
274 PRTUTF16 tmp16 = NULL;
275 size_t cTmp16Len;
276
277 rc = RTStrToUtf16(pszFilename, &tmp16);
278 if (RT_FAILURE(rc))
279 goto out;
280 cTmp16Len = RTUtf16Len(tmp16);
281 if (cTmp16Len * sizeof(*tmp16) > cbBufSize)
282 {
283 rc = VERR_FILENAME_TOO_LONG;
284 goto out;
285 }
286
287 if (fBigEndian)
288 for (unsigned i = 0; i < cTmp16Len; i++)
289 pu16Buf[i] = RT_H2BE_U16(tmp16[i]);
290 else
291 memcpy(pu16Buf, tmp16, cTmp16Len * sizeof(*tmp16));
292 if (pcbActualSize)
293 *pcbActualSize = (uint32_t)(cTmp16Len * sizeof(*tmp16));
294
295out:
296 if (tmp16)
297 RTUtf16Free(tmp16);
298 return rc;
299}
300
301/**
302 * Internal: Update one locator entry.
303 */
304static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszFilename)
305{
306 int rc = VINF_SUCCESS;
307 uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace);
308 void *pvBuf = RTMemTmpAllocZ(cbMaxLen);
309 char *pszTmp;
310
311 if (!pvBuf)
312 return VERR_NO_MEMORY;
313
314 switch (RT_BE2H_U32(pLocator->u32Code))
315 {
316 case VHD_PLATFORM_CODE_WI2R:
317 {
318 if (RTPathStartsWithRoot(pszFilename))
319 {
320 /* Convert to relative path. */
321 char szPath[RTPATH_MAX];
322 rc = RTPathCalcRelative(szPath, sizeof(szPath), pImage->pszFilename,
323 pszFilename);
324 if (RT_SUCCESS(rc))
325 {
326 /* Update plain relative name. */
327 cb = (uint32_t)strlen(szPath);
328 if (cb > cbMaxLen)
329 {
330 rc = VERR_FILENAME_TOO_LONG;
331 break;
332 }
333 memcpy(pvBuf, szPath, cb);
334 }
335 }
336 else
337 {
338 /* Update plain relative name. */
339 cb = (uint32_t)strlen(pszFilename);
340 if (cb > cbMaxLen)
341 {
342 rc = VERR_FILENAME_TOO_LONG;
343 break;
344 }
345 memcpy(pvBuf, pszFilename, cb);
346 }
347 if (RT_SUCCESS(rc))
348 pLocator->u32DataLength = RT_H2BE_U32(cb);
349 break;
350 }
351 case VHD_PLATFORM_CODE_WI2K:
352 /* Update plain absolute name. */
353 rc = RTPathAbs(pszFilename, (char *)pvBuf, cbMaxLen);
354 if (RT_SUCCESS(rc))
355 pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
356 break;
357 case VHD_PLATFORM_CODE_W2RU:
358 if (RTPathStartsWithRoot(pszFilename))
359 {
360 /* Convert to relative path. */
361 char szPath[RTPATH_MAX];
362 rc = RTPathCalcRelative(szPath, sizeof(szPath), pImage->pszFilename,
363 pszFilename);
364 if (RT_SUCCESS(rc))
365 rc = vhdFilenameToUtf16(szPath, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
366 }
367 else
368 {
369 /* Update unicode relative name. */
370 rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
371 }
372
373 if (RT_SUCCESS(rc))
374 pLocator->u32DataLength = RT_H2BE_U32(cb);
375 break;
376 case VHD_PLATFORM_CODE_W2KU:
377 /* Update unicode absolute name. */
378 pszTmp = (char*)RTMemTmpAllocZ(cbMaxLen);
379 if (!pszTmp)
380 {
381 rc = VERR_NO_MEMORY;
382 break;
383 }
384 rc = RTPathAbs(pszFilename, pszTmp, cbMaxLen);
385 if (RT_FAILURE(rc))
386 {
387 RTMemTmpFree(pszTmp);
388 break;
389 }
390 rc = vhdFilenameToUtf16(pszTmp, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
391 RTMemTmpFree(pszTmp);
392 if (RT_SUCCESS(rc))
393 pLocator->u32DataLength = RT_H2BE_U32(cb);
394 break;
395 default:
396 rc = VERR_NOT_IMPLEMENTED;
397 break;
398 }
399
400 if (RT_SUCCESS(rc))
401 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
402 RT_BE2H_U64(pLocator->u64DataOffset),
403 pvBuf, cb);
404
405 if (pvBuf)
406 RTMemTmpFree(pvBuf);
407 return rc;
408}
409
410/**
411 * Internal: Update dynamic disk header from VHDIMAGE.
412 */
413static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
414{
415 VHDDynamicDiskHeader ddh;
416 int rc, i;
417
418 if (!pImage)
419 return VERR_VD_NOT_OPENED;
420
421 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
422 pImage->u64DataOffset, &ddh, sizeof(ddh));
423 if (RT_FAILURE(rc))
424 return rc;
425 if (memcmp(ddh.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE) != 0)
426 return VERR_VD_VHD_INVALID_HEADER;
427
428 uint32_t u32Checksum = RT_BE2H_U32(ddh.Checksum);
429 ddh.Checksum = 0;
430 if (u32Checksum != vhdChecksum(&ddh, sizeof(ddh)))
431 return VERR_VD_VHD_INVALID_HEADER;
432
433 /* Update parent's timestamp. */
434 ddh.ParentTimestamp = RT_H2BE_U32(pImage->u32ParentTimestamp);
435 /* Update parent's filename. */
436 if (pImage->pszParentFilename)
437 {
438 rc = vhdFilenameToUtf16(RTPathFilename(pImage->pszParentFilename),
439 ddh.ParentUnicodeName, sizeof(ddh.ParentUnicodeName) - 1, NULL, true);
440 if (RT_FAILURE(rc))
441 return rc;
442 }
443
444 /* Update parent's locators. */
445 for (i = 0; i < VHD_MAX_LOCATOR_ENTRIES; i++)
446 {
447 /* Skip empty locators */
448 if ( ddh.ParentLocatorEntry[i].u32Code != RT_H2BE_U32(VHD_PLATFORM_CODE_NONE)
449 && pImage->pszParentFilename)
450 {
451 rc = vhdLocatorUpdate(pImage, &ddh.ParentLocatorEntry[i], pImage->pszParentFilename);
452 if (RT_FAILURE(rc))
453 return rc;
454 }
455 }
456 /* Update parent's UUID */
457 memcpy(ddh.ParentUuid, pImage->ParentUuid.au8, sizeof(ddh.ParentUuid));
458
459 /* Update data offset and number of table entries. */
460 ddh.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
461
462 ddh.Checksum = 0;
463 ddh.Checksum = RT_H2BE_U32(vhdChecksum(&ddh, sizeof(ddh)));
464 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
465 pImage->u64DataOffset, &ddh, sizeof(ddh));
466 return rc;
467}
468
469/**
470 * Internal: Update the VHD footer.
471 */
472static int vhdUpdateFooter(PVHDIMAGE pImage)
473{
474 int rc = VINF_SUCCESS;
475
476 /* Update fields which can change. */
477 pImage->vhdFooterCopy.CurSize = RT_H2BE_U64(pImage->cbSize);
478 pImage->vhdFooterCopy.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
479 pImage->vhdFooterCopy.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
480 pImage->vhdFooterCopy.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
481
482 pImage->vhdFooterCopy.Checksum = 0;
483 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
484
485 if (pImage->pBlockAllocationTable)
486 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
487 &pImage->vhdFooterCopy, sizeof(VHDFooter));
488
489 if (RT_SUCCESS(rc))
490 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
491 pImage->uCurrentEndOfFile, &pImage->vhdFooterCopy,
492 sizeof(VHDFooter));
493
494 return rc;
495}
496
497/**
498 * Internal. Flush image data to disk.
499 */
500static int vhdFlushImage(PVHDIMAGE pImage)
501{
502 int rc = VINF_SUCCESS;
503
504 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
505 return VINF_SUCCESS;
506
507 if (pImage->pBlockAllocationTable)
508 {
509 /*
510 * This is an expanding image. Write the BAT and copy of the disk footer.
511 */
512 size_t cbBlockAllocationTableToWrite = pImage->cBlockAllocationTableEntries * sizeof(uint32_t);
513 uint32_t *pBlockAllocationTableToWrite = (uint32_t *)RTMemAllocZ(cbBlockAllocationTableToWrite);
514
515 if (!pBlockAllocationTableToWrite)
516 return VERR_NO_MEMORY;
517
518 /*
519 * The BAT entries have to be stored in big endian format.
520 */
521 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
522 pBlockAllocationTableToWrite[i] = RT_H2BE_U32(pImage->pBlockAllocationTable[i]);
523
524 /*
525 * Write the block allocation table after the copy of the disk footer and the dynamic disk header.
526 */
527 vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
528 pBlockAllocationTableToWrite, cbBlockAllocationTableToWrite);
529 if (pImage->fDynHdrNeedsUpdate)
530 rc = vhdDynamicHeaderUpdate(pImage);
531 RTMemFree(pBlockAllocationTableToWrite);
532 }
533
534 if (RT_SUCCESS(rc))
535 rc = vhdUpdateFooter(pImage);
536
537 if (RT_SUCCESS(rc))
538 rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
539
540 return rc;
541}
542
543/**
544 * Internal. Free all allocated space for representing an image except pImage,
545 * and optionally delete the image from disk.
546 */
547static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
548{
549 int rc = VINF_SUCCESS;
550
551 /* Freeing a never allocated image (e.g. because the open failed) is
552 * not signalled as an error. After all nothing bad happens. */
553 if (pImage)
554 {
555 if (pImage->pStorage)
556 {
557 /* No point updating the file that is deleted anyway. */
558 if (!fDelete)
559 vhdFlushImage(pImage);
560
561 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
562 pImage->pStorage = NULL;
563 }
564
565 if (pImage->pszParentFilename)
566 {
567 RTStrFree(pImage->pszParentFilename);
568 pImage->pszParentFilename = NULL;
569 }
570 if (pImage->pBlockAllocationTable)
571 {
572 RTMemFree(pImage->pBlockAllocationTable);
573 pImage->pBlockAllocationTable = NULL;
574 }
575 if (pImage->pu8Bitmap)
576 {
577 RTMemFree(pImage->pu8Bitmap);
578 pImage->pu8Bitmap = NULL;
579 }
580
581 if (fDelete && pImage->pszFilename)
582 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
583 }
584
585 LogFlowFunc(("returns %Rrc\n", rc));
586 return rc;
587}
588
589/* 946684800 is the number of seconds between 1/1/1970 and 1/1/2000 */
590#define VHD_TO_UNIX_EPOCH_SECONDS UINT64_C(946684800)
591
592static uint32_t vhdRtTime2VhdTime(PCRTTIMESPEC pRtTimestamp)
593{
594 uint64_t u64Seconds = RTTimeSpecGetSeconds(pRtTimestamp);
595 return (uint32_t)(u64Seconds - VHD_TO_UNIX_EPOCH_SECONDS);
596}
597
598static void vhdTime2RtTime(PRTTIMESPEC pRtTimestamp, uint32_t u32VhdTimestamp)
599{
600 RTTimeSpecSetSeconds(pRtTimestamp, VHD_TO_UNIX_EPOCH_SECONDS + u32VhdTimestamp);
601}
602
603/**
604 * Internal: Allocates the block bitmap rounding up to the next 32bit or 64bit boundary.
605 * Can be freed with RTMemFree. The memory is zeroed.
606 */
607DECLINLINE(uint8_t *)vhdBlockBitmapAllocate(PVHDIMAGE pImage)
608{
609#ifdef RT_ARCH_AMD64
610 return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 8);
611#else
612 return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 4);
613#endif
614}
615
616/**
617 * Internal: called when the async expansion process completed (failure or success).
618 * Will do the necessary rollback if an error occurred.
619 */
620static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAGEEXPAND pExpand)
621{
622 int rc = VINF_SUCCESS;
623 uint32_t fFlags = ASMAtomicReadU32(&pExpand->fFlags);
624 bool fIoInProgress = false;
625
626 /* Quick path, check if everything succeeded. */
627 if (fFlags == VHDIMAGEEXPAND_ALL_SUCCESS)
628 {
629 pImage->pBlockAllocationTable[pExpand->idxBatAllocated] = RT_BE2H_U32(pExpand->idxBlockBe);
630 RTMemFree(pExpand);
631 }
632 else
633 {
634 uint32_t uStatus;
635
636 uStatus = VHDIMAGEEXPAND_STATUS_GET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
637 if ( uStatus == VHDIMAGEEXPAND_STEP_FAILED
638 || uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
639 {
640 /* Undo and restore the old value. */
641 pImage->pBlockAllocationTable[pExpand->idxBatAllocated] = ~0U;
642
643 /* Restore the old value on the disk.
644 * No need for a completion callback because we can't
645 * do anything if this fails. */
646 if (uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
647 {
648 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
649 pImage->uBlockAllocationTableOffset
650 + pExpand->idxBatAllocated * sizeof(uint32_t),
651 &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
652 sizeof(uint32_t), pIoCtx, NULL, NULL);
653 fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
654 }
655 }
656
657 /* Restore old size (including the footer because another application might
658 * fill up the free space making it impossible to add the footer)
659 * and add the footer at the right place again. */
660 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
661 pExpand->cbEofOld + sizeof(VHDFooter));
662 AssertRC(rc);
663
664 pImage->uCurrentEndOfFile = pExpand->cbEofOld;
665 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
666 pImage->uCurrentEndOfFile,
667 &pImage->vhdFooterCopy, sizeof(VHDFooter),
668 pIoCtx, NULL, NULL);
669 fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
670 }
671
672 return fIoInProgress ? VERR_VD_ASYNC_IO_IN_PROGRESS : rc;
673}
674
675static int vhdAsyncExpansionStepCompleted(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq, unsigned iStep)
676{
677 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
678 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)pvUser;
679
680 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc iStep=%u\n",
681 pBackendData, pIoCtx, pvUser, rcReq, iStep));
682
683 if (RT_SUCCESS(rcReq))
684 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_SUCCESS);
685 else
686 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_FAILED);
687
688 if ((pExpand->fFlags & VHDIMAGEEXPAND_ALL_COMPLETE) == VHDIMAGEEXPAND_ALL_COMPLETE)
689 return vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
690
691 return VERR_VD_ASYNC_IO_IN_PROGRESS;
692}
693
694static DECLCALLBACK(int) vhdAsyncExpansionDataBlockBitmapComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
695{
696 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT);
697}
698
699static DECLCALLBACK(int) vhdAsyncExpansionDataComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
700{
701 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT);
702}
703
704static DECLCALLBACK(int) vhdAsyncExpansionBatUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
705{
706 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
707}
708
709static DECLCALLBACK(int) vhdAsyncExpansionFooterUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
710{
711 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT);
712}
713
714static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffset)
715{
716 VHDDynamicDiskHeader vhdDynamicDiskHeader;
717 int rc = VINF_SUCCESS;
718 uint32_t *pBlockAllocationTable;
719 uint64_t uBlockAllocationTableOffset;
720 unsigned i = 0;
721
722 Log(("Open a dynamic disk.\n"));
723
724 /*
725 * Read the dynamic disk header.
726 */
727 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uDynamicDiskHeaderOffset,
728 &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
729 if (memcmp(vhdDynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE))
730 return VERR_INVALID_PARAMETER;
731
732 pImage->cbDataBlock = RT_BE2H_U32(vhdDynamicDiskHeader.BlockSize);
733 LogFlowFunc(("BlockSize=%u\n", pImage->cbDataBlock));
734 pImage->cBlockAllocationTableEntries = RT_BE2H_U32(vhdDynamicDiskHeader.MaxTableEntries);
735 LogFlowFunc(("MaxTableEntries=%lu\n", pImage->cBlockAllocationTableEntries));
736 AssertMsg(!(pImage->cbDataBlock % VHD_SECTOR_SIZE), ("%s: Data block size is not a multiple of %!\n", __FUNCTION__, VHD_SECTOR_SIZE));
737
738 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
739 LogFlowFunc(("SectorsPerDataBlock=%u\n", pImage->cSectorsPerDataBlock));
740
741 /*
742 * Every block starts with a bitmap indicating which sectors are valid and which are not.
743 * We store the size of it to be able to calculate the real offset.
744 */
745 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
746 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
747 /* Round up to full sector size */
748 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
749 pImage->cDataBlockBitmapSectors++;
750 LogFlowFunc(("cbDataBlockBitmap=%u\n", pImage->cbDataBlockBitmap));
751 LogFlowFunc(("cDataBlockBitmapSectors=%u\n", pImage->cDataBlockBitmapSectors));
752
753 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
754 if (!pImage->pu8Bitmap)
755 return VERR_NO_MEMORY;
756
757 pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
758 if (!pBlockAllocationTable)
759 return VERR_NO_MEMORY;
760
761 /*
762 * Read the table.
763 */
764 uBlockAllocationTableOffset = RT_BE2H_U64(vhdDynamicDiskHeader.TableOffset);
765 LogFlowFunc(("uBlockAllocationTableOffset=%llu\n", uBlockAllocationTableOffset));
766 pImage->uBlockAllocationTableOffset = uBlockAllocationTableOffset;
767 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
768 uBlockAllocationTableOffset, pBlockAllocationTable,
769 pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
770
771 /*
772 * Because the offset entries inside the allocation table are stored big endian
773 * we need to convert them into host endian.
774 */
775 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
776 if (!pImage->pBlockAllocationTable)
777 {
778 RTMemFree(pBlockAllocationTable);
779 return VERR_NO_MEMORY;
780 }
781
782 for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
783 pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
784
785 RTMemFree(pBlockAllocationTable);
786
787 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
788 memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
789
790 return rc;
791}
792
793static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
794{
795 uint64_t FileSize;
796 VHDFooter vhdFooter;
797
798 pImage->uOpenFlags = uOpenFlags;
799
800 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
801 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
802 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
803
804 /*
805 * Open the image.
806 */
807 int rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
808 VDOpenFlagsToFileOpenFlags(uOpenFlags,
809 false /* fCreate */),
810 &pImage->pStorage);
811 if (RT_FAILURE(rc))
812 {
813 /* Do NOT signal an appropriate error here, as the VD layer has the
814 * choice of retrying the open if it failed. */
815 return rc;
816 }
817
818 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &FileSize);
819 pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
820
821 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
822 &vhdFooter, sizeof(VHDFooter));
823 if (RT_SUCCESS(rc))
824 {
825 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
826 {
827 /*
828 * There is also a backup header at the beginning in case the image got corrupted.
829 * Such corrupted images are detected here to let the open handler repair it later.
830 */
831 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
832 &vhdFooter, sizeof(VHDFooter));
833 if (RT_SUCCESS(rc))
834 {
835 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
836 rc = VERR_VD_VHD_INVALID_HEADER;
837 else
838 rc = VERR_VD_IMAGE_CORRUPTED;
839 }
840 }
841 }
842
843 if (RT_FAILURE(rc))
844 return rc;
845
846 switch (RT_BE2H_U32(vhdFooter.DiskType))
847 {
848 case VHD_FOOTER_DISK_TYPE_FIXED:
849 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
850 break;
851 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
852 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
853 break;
854 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
855 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
856 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
857 break;
858 default:
859 return VERR_NOT_IMPLEMENTED;
860 }
861
862 pImage->cbSize = RT_BE2H_U64(vhdFooter.CurSize);
863 pImage->LCHSGeometry.cCylinders = 0;
864 pImage->LCHSGeometry.cHeads = 0;
865 pImage->LCHSGeometry.cSectors = 0;
866 pImage->PCHSGeometry.cCylinders = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
867 pImage->PCHSGeometry.cHeads = vhdFooter.DiskGeometryHeads;
868 pImage->PCHSGeometry.cSectors = vhdFooter.DiskGeometrySectors;
869
870 /*
871 * Copy of the disk footer.
872 * If we allocate new blocks in differencing disks on write access
873 * the footer is overwritten. We need to write it at the end of the file.
874 */
875 memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
876
877 /*
878 * Is there a better way?
879 */
880 memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
881
882 pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
883 LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
884
885 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
886 rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
887
888 if (RT_FAILURE(rc))
889 vhdFreeImage(pImage, false);
890 return rc;
891}
892
893/**
894 * Internal: Checks if a sector in the block bitmap is set
895 */
896DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
897{
898 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
899
900 /*
901 * The index of the bit in the byte of the data block bitmap.
902 * The most significant bit stands for a lower sector number.
903 */
904 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
905 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
906
907 AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
908 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
909
910 return ((*puBitmap) & RT_BIT(iBitInByte)) != 0;
911}
912
913/**
914 * Internal: Sets the given sector in the sector bitmap.
915 */
916DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
917{
918 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
919
920 /*
921 * The index of the bit in the byte of the data block bitmap.
922 * The most significant bit stands for a lower sector number.
923 */
924 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
925 uint8_t *puBitmap = pu8Bitmap + iBitmap;
926
927 AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
928 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
929
930 bool fClear = ((*puBitmap) & RT_BIT(iBitInByte)) == 0;
931 *puBitmap |= RT_BIT(iBitInByte);
932 return fClear;
933}
934
935/**
936 * Internal: Derive drive geometry from its size.
937 */
938static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
939{
940 uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
941 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
942
943 if (u64TotalSectors > 65535 * 16 * 255)
944 {
945 /* ATA disks limited to 127 GB. */
946 u64TotalSectors = 65535 * 16 * 255;
947 }
948
949 if (u64TotalSectors >= 65535 * 16 * 63)
950 {
951 u32SectorsPerTrack = 255;
952 u32Heads = 16;
953 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
954 }
955 else
956 {
957 u32SectorsPerTrack = 17;
958 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
959
960 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
961
962 if (u32Heads < 4)
963 {
964 u32Heads = 4;
965 }
966 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
967 {
968 u32SectorsPerTrack = 31;
969 u32Heads = 16;
970 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
971 }
972 if (u32CylinderTimesHeads >= (u32Heads * 1024))
973 {
974 u32SectorsPerTrack = 63;
975 u32Heads = 16;
976 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
977 }
978 }
979 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
980 pImage->PCHSGeometry.cHeads = u32Heads;
981 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
982 pImage->LCHSGeometry.cCylinders = 0;
983 pImage->LCHSGeometry.cHeads = 0;
984 pImage->LCHSGeometry.cSectors = 0;
985}
986
987
988static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
989{
990 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
991
992 /*
993 * The VHD spec states that the DataSpace field holds the number of sectors
994 * required to store the parent locator path.
995 * As it turned out VPC and Hyper-V store the amount of bytes reserved for the
996 * path and not the number of sectors.
997 */
998
999 /* Unicode absolute Windows path. */
1000 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
1001 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16));
1002 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1003 pLocator++;
1004 u64Offset += VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
1005 /* Unicode relative Windows path. */
1006 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
1007 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16));
1008 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1009 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
1010 return u64Offset;
1011}
1012
1013/**
1014 * Internal: Additional code for dynamic VHD image creation.
1015 */
1016static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
1017{
1018 int rc;
1019 VHDDynamicDiskHeader DynamicDiskHeader;
1020 uint32_t u32BlockAllocationTableSectors;
1021 void *pvTmp = NULL;
1022
1023 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
1024
1025 pImage->u64DataOffset = sizeof(VHDFooter);
1026 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1027 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1028 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1029 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1030 /* Align to sector boundary */
1031 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
1032 pImage->cDataBlockBitmapSectors++;
1033 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
1034 if (!pImage->pu8Bitmap)
1035 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1036
1037 /* Initialize BAT. */
1038 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1039 pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
1040 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1041 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1042 if (!pImage->pBlockAllocationTable)
1043 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1044
1045 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1046 {
1047 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1048 }
1049
1050 /* Round up to the sector size. */
1051 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
1052 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1053 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1054 else
1055 pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
1056
1057 /* Set dynamic image size. */
1058 pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1059 if (!pvTmp)
1060 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1061
1062 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, pvTmp,
1063 pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1064 if (RT_FAILURE(rc))
1065 {
1066 RTMemTmpFree(pvTmp);
1067 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1068 }
1069
1070 RTMemTmpFree(pvTmp);
1071
1072 /* Initialize and write the dynamic disk header. */
1073 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1074 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1075 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1076 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1077 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1078 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1079 /* Compute and update checksum. */
1080 DynamicDiskHeader.Checksum = 0;
1081 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1082
1083 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VHDFooter),
1084 &DynamicDiskHeader, sizeof(DynamicDiskHeader));
1085 if (RT_FAILURE(rc))
1086 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1087
1088 /* Write BAT. */
1089 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
1090 pImage->pBlockAllocationTable,
1091 pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1092 if (RT_FAILURE(rc))
1093 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1094
1095 return rc;
1096}
1097
1098/**
1099 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1100 */
1101static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
1102 unsigned uImageFlags, const char *pszComment,
1103 PCVDGEOMETRY pPCHSGeometry,
1104 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1105 unsigned uOpenFlags,
1106 PFNVDPROGRESS pfnProgress, void *pvUser,
1107 unsigned uPercentStart, unsigned uPercentSpan)
1108{
1109 int rc;
1110 VHDFooter Footer;
1111 RTTIMESPEC now;
1112
1113 pImage->uOpenFlags = uOpenFlags;
1114 pImage->uImageFlags = uImageFlags;
1115
1116 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1117
1118 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1119 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
1120 true /* fCreate */),
1121 &pImage->pStorage);
1122 if (RT_FAILURE(rc))
1123 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1124
1125
1126 pImage->cbSize = cbSize;
1127 pImage->ImageUuid = *pUuid;
1128 RTUuidClear(&pImage->ParentUuid);
1129 vhdSetDiskGeometry(pImage, cbSize);
1130
1131 /* Initialize the footer. */
1132 memset(&Footer, 0, sizeof(Footer));
1133 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1134 Footer.Features = RT_H2BE_U32(0x2);
1135 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1136 Footer.Timestamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1137 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1138 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1139#ifdef RT_OS_DARWIN
1140 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1141#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1142 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1143#endif
1144 Footer.OrigSize = RT_H2BE_U64(cbSize);
1145 Footer.CurSize = Footer.OrigSize;
1146 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1147 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1148 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1149 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1150 Footer.SavedState = 0;
1151
1152 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1153 {
1154 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1155 /*
1156 * Initialize fixed image.
1157 * "The size of the entire file is the size of the hard disk in
1158 * the guest operating system plus the size of the footer."
1159 */
1160 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1161 pImage->uCurrentEndOfFile = cbSize;
1162 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile + sizeof(VHDFooter),
1163 0 /* fFlags */, pfnProgress, pvUser, uPercentStart, uPercentSpan);
1164 if (RT_FAILURE(rc))
1165 {
1166 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1167 goto out;
1168 }
1169 }
1170 else
1171 {
1172 /*
1173 * Initialize dynamic image.
1174 *
1175 * The overall structure of dynamic disk is:
1176 *
1177 * [Copy of hard disk footer (512 bytes)]
1178 * [Dynamic disk header (1024 bytes)]
1179 * [BAT (Block Allocation Table)]
1180 * [Parent Locators]
1181 * [Data block 1]
1182 * [Data block 2]
1183 * ...
1184 * [Data block N]
1185 * [Hard disk footer (512 bytes)]
1186 */
1187 Footer.DiskType = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
1188 ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
1189 : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1190 /* We are half way thorough with creation of image, let the caller know. */
1191 if (pfnProgress)
1192 pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
1193
1194 rc = vhdCreateDynamicImage(pImage, cbSize);
1195 if (RT_FAILURE(rc))
1196 goto out;
1197 }
1198
1199 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1200
1201 /* Compute and update the footer checksum. */
1202 Footer.Checksum = 0;
1203 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1204
1205 pImage->vhdFooterCopy = Footer;
1206
1207 /* Store the footer */
1208 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
1209 &Footer, sizeof(Footer));
1210 if (RT_FAILURE(rc))
1211 {
1212 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1213 goto out;
1214 }
1215
1216 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1217 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1218 {
1219 /* Write the copy of the footer. */
1220 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Footer, sizeof(Footer));
1221 if (RT_FAILURE(rc))
1222 {
1223 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1224 goto out;
1225 }
1226 }
1227
1228out:
1229 if (RT_SUCCESS(rc) && pfnProgress)
1230 pfnProgress(pvUser, uPercentStart + uPercentSpan);
1231
1232 if (RT_FAILURE(rc))
1233 vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1234 return rc;
1235}
1236
1237
1238/** @interface_method_impl{VBOXHDDBACKEND,pfnCheckIfValid} */
1239static DECLCALLBACK(int) vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1240 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1241{
1242 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1243 int rc;
1244 PVDIOSTORAGE pStorage;
1245 uint64_t cbFile;
1246 VHDFooter vhdFooter;
1247
1248 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1249 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1250
1251 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1252 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1253 false /* fCreate */),
1254 &pStorage);
1255 if (RT_FAILURE(rc))
1256 goto out;
1257
1258 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1259 if (RT_FAILURE(rc))
1260 {
1261 vdIfIoIntFileClose(pIfIo, pStorage);
1262 rc = VERR_VD_VHD_INVALID_HEADER;
1263 goto out;
1264 }
1265
1266 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
1267 &vhdFooter, sizeof(VHDFooter));
1268 if (RT_SUCCESS(rc))
1269 {
1270 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
1271 {
1272 /*
1273 * There is also a backup header at the beginning in case the image got corrupted.
1274 * Such corrupted images are detected here to let the open handler repair it later.
1275 */
1276 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &vhdFooter, sizeof(VHDFooter));
1277 if ( RT_FAILURE(rc)
1278 || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
1279 rc = VERR_VD_VHD_INVALID_HEADER;
1280 }
1281
1282 if (RT_SUCCESS(rc))
1283 *penmType = VDTYPE_HDD;
1284 }
1285 else
1286 rc = VERR_VD_VHD_INVALID_HEADER;
1287
1288 vdIfIoIntFileClose(pIfIo, pStorage);
1289
1290out:
1291 LogFlowFunc(("returns %Rrc\n", rc));
1292 return rc;
1293}
1294
1295/** @interface_method_impl{VBOXHDDBACKEND,pfnOpen} */
1296static DECLCALLBACK(int) vhdOpen(const char *pszFilename, unsigned uOpenFlags,
1297 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1298 VDTYPE enmType, void **ppBackendData)
1299{
1300 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1301 int rc = VINF_SUCCESS;
1302 PVHDIMAGE pImage;
1303
1304 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
1305
1306 /* Check open flags. All valid flags are supported. */
1307 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1308 {
1309 rc = VERR_INVALID_PARAMETER;
1310 goto out;
1311 }
1312
1313 /* Check remaining arguments. */
1314 if ( !VALID_PTR(pszFilename)
1315 || !*pszFilename)
1316 {
1317 rc = VERR_INVALID_PARAMETER;
1318 goto out;
1319 }
1320
1321 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1322 if (!pImage)
1323 {
1324 rc = VERR_NO_MEMORY;
1325 goto out;
1326 }
1327
1328 pImage->pszFilename = pszFilename;
1329 pImage->pStorage = NULL;
1330 pImage->pVDIfsDisk = pVDIfsDisk;
1331 pImage->pVDIfsImage = pVDIfsImage;
1332
1333 rc = vhdOpenImage(pImage, uOpenFlags);
1334 if (RT_SUCCESS(rc))
1335 *ppBackendData = pImage;
1336 else
1337 RTMemFree(pImage);
1338
1339out:
1340 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1341 return rc;
1342}
1343
1344/** @interface_method_impl{VBOXHDDBACKEND,pfnCreate} */
1345static DECLCALLBACK(int) vhdCreate(const char *pszFilename, uint64_t cbSize,
1346 unsigned uImageFlags, const char *pszComment,
1347 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1348 PCRTUUID pUuid, unsigned uOpenFlags,
1349 unsigned uPercentStart, unsigned uPercentSpan,
1350 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1351 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1352 void **ppBackendData)
1353{
1354 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 enmType=%u ppBackendData=%#p",
1355 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1356 int rc = VINF_SUCCESS;
1357 PVHDIMAGE pImage;
1358
1359 PFNVDPROGRESS pfnProgress = NULL;
1360 void *pvUser = NULL;
1361 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1362 if (pIfProgress)
1363 {
1364 pfnProgress = pIfProgress->pfnProgress;
1365 pvUser = pIfProgress->Core.pvUser;
1366 }
1367
1368 /* Check the VD container type. */
1369 if (enmType != VDTYPE_HDD)
1370 {
1371 rc = VERR_VD_INVALID_TYPE;
1372 goto out;
1373 }
1374
1375 /* Check open flags. All valid flags are supported. */
1376 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1377 {
1378 rc = VERR_INVALID_PARAMETER;
1379 return rc;
1380 }
1381
1382 /* @todo Check the values of other params */
1383
1384 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1385 if (!pImage)
1386 {
1387 rc = VERR_NO_MEMORY;
1388 return rc;
1389 }
1390 pImage->pszFilename = pszFilename;
1391 pImage->pStorage = NULL;
1392 pImage->pVDIfsDisk = pVDIfsDisk;
1393 pImage->pVDIfsImage = pVDIfsImage;
1394
1395 /* Get I/O interface. */
1396 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1397 if (RT_UNLIKELY(!VALID_PTR(pImage->pIfIo)))
1398 {
1399 RTMemFree(pImage);
1400 return VERR_INVALID_PARAMETER;
1401 }
1402
1403 rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
1404 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1405 pfnProgress, pvUser, uPercentStart, uPercentSpan);
1406
1407 if (RT_SUCCESS(rc))
1408 {
1409 /* So far the image is opened in read/write mode. Make sure the
1410 * image is opened in read-only mode if the caller requested that. */
1411 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1412 {
1413 vhdFreeImage(pImage, false);
1414 rc = vhdOpenImage(pImage, uOpenFlags);
1415 if (RT_FAILURE(rc))
1416 {
1417 RTMemFree(pImage);
1418 goto out;
1419 }
1420 }
1421 *ppBackendData = pImage;
1422 }
1423 else
1424 RTMemFree(pImage);
1425
1426out:
1427 LogFlowFunc(("returns %Rrc\n", rc));
1428 return rc;
1429}
1430
1431/** @interface_method_impl{VBOXHDDBACKEND,pfnRename} */
1432static DECLCALLBACK(int) vhdRename(void *pBackendData, const char *pszFilename)
1433{
1434 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1435 int rc = VINF_SUCCESS;
1436 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1437
1438 /* Check arguments. */
1439 if ( !pImage
1440 || !pszFilename
1441 || !*pszFilename)
1442 {
1443 rc = VERR_INVALID_PARAMETER;
1444 goto out;
1445 }
1446
1447 /* Close the image. */
1448 rc = vhdFreeImage(pImage, false);
1449 if (RT_FAILURE(rc))
1450 goto out;
1451
1452 /* Rename the file. */
1453 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1454 if (RT_FAILURE(rc))
1455 {
1456 /* The move failed, try to reopen the original image. */
1457 int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
1458 if (RT_FAILURE(rc2))
1459 rc = rc2;
1460
1461 goto out;
1462 }
1463
1464 /* Update pImage with the new information. */
1465 pImage->pszFilename = pszFilename;
1466
1467 /* Open the old file with new name. */
1468 rc = vhdOpenImage(pImage, pImage->uOpenFlags);
1469 if (RT_FAILURE(rc))
1470 goto out;
1471
1472out:
1473 LogFlowFunc(("returns %Rrc\n", rc));
1474 return rc;
1475}
1476
1477/** @interface_method_impl{VBOXHDDBACKEND,pfnClose} */
1478static DECLCALLBACK(int) vhdClose(void *pBackendData, bool fDelete)
1479{
1480 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1481 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1482 int rc;
1483
1484 rc = vhdFreeImage(pImage, fDelete);
1485 RTMemFree(pImage);
1486
1487 LogFlowFunc(("returns %Rrc\n", rc));
1488 return rc;
1489}
1490
1491/** @interface_method_impl{VBOXHDDBACKEND,pfnRead} */
1492static DECLCALLBACK(int) vhdRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
1493 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1494{
1495 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1496 int rc = VINF_SUCCESS;
1497
1498 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
1499
1500 if (uOffset + cbRead > pImage->cbSize)
1501 return VERR_INVALID_PARAMETER;
1502
1503 /*
1504 * If we have a dynamic disk image, we need to find the data block and sector to read.
1505 */
1506 if (pImage->pBlockAllocationTable)
1507 {
1508 /*
1509 * Get the data block first.
1510 */
1511 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
1512 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
1513 uint64_t uVhdOffset;
1514
1515 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
1516 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
1517
1518 /*
1519 * Clip read range to remain in this data block.
1520 */
1521 cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1522
1523 /*
1524 * If the block is not allocated the content of the entry is ~0
1525 */
1526 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1527 rc = VERR_VD_BLOCK_FREE;
1528 else
1529 {
1530 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1531 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
1532
1533 /* Read in the block's bitmap. */
1534 PVDMETAXFER pMetaXfer;
1535 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
1536 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1537 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1538 pIoCtx, &pMetaXfer, NULL, NULL);
1539
1540 if (RT_SUCCESS(rc))
1541 {
1542 uint32_t cSectors = 0;
1543
1544 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
1545 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1546 {
1547 cBATEntryIndex++;
1548 cSectors = 1;
1549
1550 /*
1551 * The first sector being read is marked dirty, read as much as we
1552 * can from child. Note that only sectors that are marked dirty
1553 * must be read from child.
1554 */
1555 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
1556 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1557 {
1558 cBATEntryIndex++;
1559 cSectors++;
1560 }
1561
1562 cbRead = cSectors * VHD_SECTOR_SIZE;
1563
1564 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
1565 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage,
1566 uVhdOffset, pIoCtx, cbRead);
1567 }
1568 else
1569 {
1570 /*
1571 * The first sector being read is marked clean, so we should read from
1572 * our parent instead, but only as much as there are the following
1573 * clean sectors, because the block may still contain dirty sectors
1574 * further on. We just need to compute the number of clean sectors
1575 * and pass it to our caller along with the notification that they
1576 * should be read from the parent.
1577 */
1578 cBATEntryIndex++;
1579 cSectors = 1;
1580
1581 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
1582 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1583 {
1584 cBATEntryIndex++;
1585 cSectors++;
1586 }
1587
1588 cbRead = cSectors * VHD_SECTOR_SIZE;
1589 LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
1590 rc = VERR_VD_BLOCK_FREE;
1591 }
1592 }
1593 else
1594 AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
1595 }
1596 }
1597 else
1598 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset, pIoCtx, cbRead);
1599
1600 if (pcbActuallyRead)
1601 *pcbActuallyRead = cbRead;
1602
1603 LogFlowFunc(("returns rc=%Rrc\n", rc));
1604 return rc;
1605}
1606
1607/** @interface_method_impl{VBOXHDDBACKEND,pfnWrite} */
1608static DECLCALLBACK(int) vhdWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
1609 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1610 size_t *pcbPostRead, unsigned fWrite)
1611{
1612 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1613 int rc = VINF_SUCCESS;
1614
1615 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1616 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
1617
1618 AssertPtr(pImage);
1619 Assert(uOffset % VHD_SECTOR_SIZE == 0);
1620 Assert(cbWrite % VHD_SECTOR_SIZE == 0);
1621
1622 if (pImage->pBlockAllocationTable)
1623 {
1624 /*
1625 * Get the data block first.
1626 */
1627 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
1628 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1629 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1630 uint64_t uVhdOffset;
1631
1632 /*
1633 * Clip write range.
1634 */
1635 cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1636
1637 /*
1638 * If the block is not allocated the content of the entry is ~0
1639 * and we need to allocate a new block. Note that while blocks are
1640 * allocated with a relatively big granularity, each sector has its
1641 * own bitmap entry, indicating whether it has been written or not.
1642 * So that means for the purposes of the higher level that the
1643 * granularity is invisible. This means there's no need to return
1644 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
1645 */
1646 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1647 {
1648 /* Check if the block allocation should be suppressed. */
1649 if ( (fWrite & VD_WRITE_NO_ALLOC)
1650 || (cbWrite != pImage->cbDataBlock))
1651 {
1652 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
1653 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
1654
1655 if (pcbWriteProcess)
1656 *pcbWriteProcess = cbWrite;
1657 return VERR_VD_BLOCK_FREE;
1658 }
1659
1660 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
1661 bool fIoInProgress = false;
1662
1663 if (!pExpand)
1664 return VERR_NO_MEMORY;
1665
1666 pExpand->cbEofOld = pImage->uCurrentEndOfFile;
1667 pExpand->idxBatAllocated = cBlockAllocationTableEntry;
1668 pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
1669
1670 /* Set the bits for all sectors having been written. */
1671 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
1672 {
1673 /* No need to check for a changed value because this is an initial write. */
1674 vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
1675 cBATEntryIndex++;
1676 }
1677
1678 do
1679 {
1680 /*
1681 * Start with the sector bitmap.
1682 */
1683 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1684 pImage->uCurrentEndOfFile,
1685 pExpand->au8Bitmap,
1686 pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE, pIoCtx,
1687 vhdAsyncExpansionDataBlockBitmapComplete,
1688 pExpand);
1689 if (RT_SUCCESS(rc))
1690 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1691 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1692 fIoInProgress = true;
1693 else
1694 {
1695 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1696 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1697 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1698 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1699 break;
1700 }
1701
1702
1703 /*
1704 * Write the new block at the current end of the file.
1705 */
1706 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1707 pImage->uCurrentEndOfFile + (pImage->cDataBlockBitmapSectors + (cSector % pImage->cSectorsPerDataBlock)) * VHD_SECTOR_SIZE,
1708 pIoCtx, cbWrite,
1709 vhdAsyncExpansionDataComplete,
1710 pExpand);
1711 if (RT_SUCCESS(rc))
1712 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1713 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1714 fIoInProgress = true;
1715 else
1716 {
1717 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1718 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1719 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1720 break;
1721 }
1722
1723 /*
1724 * Write entry in the BAT.
1725 */
1726 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1727 pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
1728 &pExpand->idxBlockBe, sizeof(uint32_t), pIoCtx,
1729 vhdAsyncExpansionBatUpdateComplete,
1730 pExpand);
1731 if (RT_SUCCESS(rc))
1732 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1733 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1734 fIoInProgress = true;
1735 else
1736 {
1737 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1738 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1739 break;
1740 }
1741
1742 /*
1743 * Set the new end of the file and link the new block into the BAT.
1744 */
1745 pImage->uCurrentEndOfFile += pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE + pImage->cbDataBlock;
1746
1747 /* Update the footer. */
1748 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1749 pImage->uCurrentEndOfFile,
1750 &pImage->vhdFooterCopy,
1751 sizeof(VHDFooter), pIoCtx,
1752 vhdAsyncExpansionFooterUpdateComplete,
1753 pExpand);
1754 if (RT_SUCCESS(rc))
1755 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1756 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1757 fIoInProgress = true;
1758 else
1759 {
1760 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1761 break;
1762 }
1763
1764 } while (0);
1765
1766 if (!fIoInProgress)
1767 vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
1768 else
1769 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1770 }
1771 else
1772 {
1773 /*
1774 * Calculate the real offset in the file.
1775 */
1776 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1777
1778 /* Read in the block's bitmap. */
1779 PVDMETAXFER pMetaXfer;
1780 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
1781 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1782 pImage->pu8Bitmap,
1783 pImage->cbDataBlockBitmap, pIoCtx,
1784 &pMetaXfer, NULL, NULL);
1785 if (RT_SUCCESS(rc))
1786 {
1787 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
1788
1789 /* Write data. */
1790 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1791 uVhdOffset, pIoCtx, cbWrite,
1792 NULL, NULL);
1793 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1794 {
1795 bool fChanged = false;
1796
1797 /* Set the bits for all sectors having been written. */
1798 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
1799 {
1800 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
1801 cBATEntryIndex++;
1802 }
1803
1804 /* Only write the bitmap if it was changed. */
1805 if (fChanged)
1806 {
1807 /*
1808 * Write the bitmap back.
1809 *
1810 * @note We don't have a completion callback here because we
1811 * can't do anything if the write fails for some reason.
1812 * The error will propagated to the device/guest
1813 * by the generic VD layer already and we don't need
1814 * to rollback anything here.
1815 */
1816 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1817 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1818 pImage->pu8Bitmap,
1819 pImage->cbDataBlockBitmap,
1820 pIoCtx, NULL, NULL);
1821 }
1822 }
1823 }
1824 }
1825 }
1826 else
1827 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1828 uOffset, pIoCtx, cbWrite, NULL, NULL);
1829
1830 if (pcbWriteProcess)
1831 *pcbWriteProcess = cbWrite;
1832
1833 /* Stay on the safe side. Do not run the risk of confusing the higher
1834 * level, as that can be pretty lethal to image consistency. */
1835 *pcbPreRead = 0;
1836 *pcbPostRead = 0;
1837
1838 return rc;
1839}
1840
1841/** @interface_method_impl{VBOXHDDBACKEND,pfnFlush} */
1842static DECLCALLBACK(int) vhdFlush(void *pBackendData, PVDIOCTX pIoCtx)
1843{
1844 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1845
1846 /* No need to write anything here. Data is always updated on a write. */
1847 return vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
1848}
1849
1850/** @interface_method_impl{VBOXHDDBACKEND,pfnGetVersion} */
1851static DECLCALLBACK(unsigned) vhdGetVersion(void *pBackendData)
1852{
1853 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1854 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1855 unsigned ver = 0;
1856
1857 AssertPtr(pImage);
1858
1859 if (pImage)
1860 ver = 1; /**< @todo use correct version */
1861
1862 LogFlowFunc(("returns %u\n", ver));
1863 return ver;
1864}
1865
1866/** @interface_method_impl{VBOXHDDBACKEND,pfnGetSectorSize} */
1867static DECLCALLBACK(uint32_t) vhdGetSectorSize(void *pBackendData)
1868{
1869 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1870 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1871 uint32_t cb = 0;
1872
1873 AssertPtr(pImage);
1874
1875 if (pImage)
1876 cb = 512;
1877
1878 LogFlowFunc(("returns %zu\n", cb));
1879 return cb;
1880}
1881
1882/** @interface_method_impl{VBOXHDDBACKEND,pfnGetSize} */
1883static DECLCALLBACK(uint64_t) vhdGetSize(void *pBackendData)
1884{
1885 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1886 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1887 uint64_t cb = 0;
1888
1889 AssertPtr(pImage);
1890
1891 if (pImage && pImage->pStorage)
1892 cb = pImage->cbSize;
1893
1894 LogFlowFunc(("returns %llu\n", cb));
1895 return cb;
1896}
1897
1898/** @interface_method_impl{VBOXHDDBACKEND,pfnGetFileSize} */
1899static DECLCALLBACK(uint64_t) vhdGetFileSize(void *pBackendData)
1900{
1901 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1902 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1903 uint64_t cb = 0;
1904
1905 AssertPtr(pImage);
1906
1907 if (pImage && pImage->pStorage)
1908 cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
1909
1910 LogFlowFunc(("returns %lld\n", cb));
1911 return cb;
1912}
1913
1914/** @interface_method_impl{VBOXHDDBACKEND,pfnGetPCHSGeometry} */
1915static DECLCALLBACK(int) vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1916{
1917 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1918 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1919 int rc;
1920
1921 AssertPtr(pImage);
1922
1923 if (pImage)
1924 {
1925 if (pImage->PCHSGeometry.cCylinders)
1926 {
1927 *pPCHSGeometry = pImage->PCHSGeometry;
1928 rc = VINF_SUCCESS;
1929 }
1930 else
1931 rc = VERR_VD_GEOMETRY_NOT_SET;
1932 }
1933 else
1934 rc = VERR_VD_NOT_OPENED;
1935
1936 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
1937 return rc;
1938}
1939
1940/** @interface_method_impl{VBOXHDDBACKEND,pfnSetPCHSGeometry} */
1941static DECLCALLBACK(int) vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1942{
1943 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1944 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1945 int rc;
1946
1947 AssertPtr(pImage);
1948
1949 if (pImage)
1950 {
1951 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1952 {
1953 rc = VERR_VD_IMAGE_READ_ONLY;
1954 goto out;
1955 }
1956
1957 pImage->PCHSGeometry = *pPCHSGeometry;
1958 rc = VINF_SUCCESS;
1959 }
1960 else
1961 rc = VERR_VD_NOT_OPENED;
1962
1963out:
1964 LogFlowFunc(("returns %Rrc\n", rc));
1965 return rc;
1966}
1967
1968/** @interface_method_impl{VBOXHDDBACKEND,pfnGetLCHSGeometry} */
1969static DECLCALLBACK(int) vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1970{
1971LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1972 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1973 int rc;
1974
1975 AssertPtr(pImage);
1976
1977 if (pImage)
1978 {
1979 if (pImage->LCHSGeometry.cCylinders)
1980 {
1981 *pLCHSGeometry = pImage->LCHSGeometry;
1982 rc = VINF_SUCCESS;
1983 }
1984 else
1985 rc = VERR_VD_GEOMETRY_NOT_SET;
1986 }
1987 else
1988 rc = VERR_VD_NOT_OPENED;
1989
1990 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
1991 return rc;
1992}
1993
1994/** @interface_method_impl{VBOXHDDBACKEND,pfnSetLCHSGeometry} */
1995static DECLCALLBACK(int) vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1996{
1997 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1998 int rc;
1999
2000 AssertPtr(pImage);
2001
2002 if (pImage)
2003 {
2004 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2005 {
2006 rc = VERR_VD_IMAGE_READ_ONLY;
2007 goto out;
2008 }
2009
2010 pImage->LCHSGeometry = *pLCHSGeometry;
2011 rc = VINF_SUCCESS;
2012 }
2013 else
2014 rc = VERR_VD_NOT_OPENED;
2015
2016out:
2017 LogFlowFunc(("returns %Rrc\n", rc));
2018 return rc;
2019}
2020
2021/** @interface_method_impl{VBOXHDDBACKEND,pfnGetImageFlags} */
2022static DECLCALLBACK(unsigned) vhdGetImageFlags(void *pBackendData)
2023{
2024 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2025 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2026 unsigned uImageFlags;
2027
2028 AssertPtr(pImage);
2029
2030 if (pImage)
2031 uImageFlags = pImage->uImageFlags;
2032 else
2033 uImageFlags = 0;
2034
2035 LogFlowFunc(("returns %#x\n", uImageFlags));
2036 return uImageFlags;
2037}
2038
2039/** @interface_method_impl{VBOXHDDBACKEND,pfnGetOpenFlags} */
2040static DECLCALLBACK(unsigned) vhdGetOpenFlags(void *pBackendData)
2041{
2042 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2043 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2044 unsigned uOpenFlags;
2045
2046 AssertPtr(pImage);
2047
2048 if (pImage)
2049 uOpenFlags = pImage->uOpenFlags;
2050 else
2051 uOpenFlags = 0;
2052
2053 LogFlowFunc(("returns %#x\n", uOpenFlags));
2054 return uOpenFlags;
2055}
2056
2057/** @interface_method_impl{VBOXHDDBACKEND,pfnSetOpenFlags} */
2058static DECLCALLBACK(int) vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2059{
2060 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2061 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2062 int rc;
2063
2064 /* Image must be opened and the new flags must be valid. */
2065 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
2066 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
2067 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2068 {
2069 rc = VERR_INVALID_PARAMETER;
2070 goto out;
2071 }
2072
2073 /* Implement this operation via reopening the image. */
2074 rc = vhdFreeImage(pImage, false);
2075 if (RT_FAILURE(rc))
2076 goto out;
2077 rc = vhdOpenImage(pImage, uOpenFlags);
2078
2079out:
2080 LogFlowFunc(("returns %Rrc\n", rc));
2081 return rc;
2082}
2083
2084/** @interface_method_impl{VBOXHDDBACKEND,pfnGetComment} */
2085static DECLCALLBACK(int) vhdGetComment(void *pBackendData, char *pszComment,
2086 size_t cbComment)
2087{
2088 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2089 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2090 int rc;
2091
2092 AssertPtr(pImage);
2093
2094 if (pImage)
2095 rc = VERR_NOT_SUPPORTED;
2096 else
2097 rc = VERR_VD_NOT_OPENED;
2098
2099 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2100 return rc;
2101}
2102
2103/** @interface_method_impl{VBOXHDDBACKEND,pfnSetComment} */
2104static DECLCALLBACK(int) vhdSetComment(void *pBackendData, const char *pszComment)
2105{
2106 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2107 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2108 int rc;
2109
2110 AssertPtr(pImage);
2111
2112 if (pImage)
2113 {
2114 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2115 rc = VERR_VD_IMAGE_READ_ONLY;
2116 else
2117 rc = VERR_NOT_SUPPORTED;
2118 }
2119 else
2120 rc = VERR_VD_NOT_OPENED;
2121
2122 LogFlowFunc(("returns %Rrc\n", rc));
2123 return rc;
2124}
2125
2126/** @interface_method_impl{VBOXHDDBACKEND,pfnGetUuid} */
2127static DECLCALLBACK(int) vhdGetUuid(void *pBackendData, PRTUUID pUuid)
2128{
2129 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2130 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2131 int rc;
2132
2133 AssertPtr(pImage);
2134
2135 if (pImage)
2136 {
2137 *pUuid = pImage->ImageUuid;
2138 rc = VINF_SUCCESS;
2139 }
2140 else
2141 rc = VERR_VD_NOT_OPENED;
2142
2143 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2144 return rc;
2145}
2146
2147/** @interface_method_impl{VBOXHDDBACKEND,pfnSetUuid} */
2148static DECLCALLBACK(int) vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
2149{
2150 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2151 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2152 int rc;
2153
2154 AssertPtr(pImage);
2155
2156 if (pImage)
2157 {
2158 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2159 {
2160 pImage->ImageUuid = *pUuid;
2161 /* Update the footer copy. It will get written to disk when the image is closed. */
2162 memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
2163 /* Update checksum. */
2164 pImage->vhdFooterCopy.Checksum = 0;
2165 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
2166
2167 /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
2168 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2169 pImage->fDynHdrNeedsUpdate = true;
2170 rc = VINF_SUCCESS;
2171 }
2172 else
2173 rc = VERR_VD_IMAGE_READ_ONLY;
2174 }
2175 else
2176 rc = VERR_VD_NOT_OPENED;
2177
2178 LogFlowFunc(("returns %Rrc\n", rc));
2179 return rc;
2180}
2181
2182/** @interface_method_impl{VBOXHDDBACKEND,pfnGetModificationUuid} */
2183static DECLCALLBACK(int) vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2184{
2185 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2186 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2187 int rc;
2188
2189 AssertPtr(pImage);
2190
2191 if (pImage)
2192 rc = VERR_NOT_SUPPORTED;
2193 else
2194 rc = VERR_VD_NOT_OPENED;
2195
2196 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2197 return rc;
2198}
2199
2200/** @interface_method_impl{VBOXHDDBACKEND,pfnSetModificationUuid} */
2201static DECLCALLBACK(int) vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2202{
2203 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2204 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2205 int rc;
2206
2207 AssertPtr(pImage);
2208
2209 if (pImage)
2210 {
2211 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2212 rc = VERR_NOT_SUPPORTED;
2213 else
2214 rc = VERR_VD_IMAGE_READ_ONLY;
2215 }
2216 else
2217 rc = VERR_VD_NOT_OPENED;
2218
2219 LogFlowFunc(("returns %Rrc\n", rc));
2220 return rc;
2221}
2222
2223/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentUuid} */
2224static DECLCALLBACK(int) vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
2225{
2226 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2227 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2228 int rc;
2229
2230 AssertPtr(pImage);
2231
2232 if (pImage)
2233 {
2234 *pUuid = pImage->ParentUuid;
2235 rc = VINF_SUCCESS;
2236 }
2237 else
2238 rc = VERR_VD_NOT_OPENED;
2239
2240 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2241 return rc;
2242}
2243
2244/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentUuid} */
2245static DECLCALLBACK(int) vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2246{
2247 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2248 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2249 int rc = VINF_SUCCESS;
2250
2251 AssertPtr(pImage);
2252
2253 if (pImage && pImage->pStorage)
2254 {
2255 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2256 {
2257 pImage->ParentUuid = *pUuid;
2258 pImage->fDynHdrNeedsUpdate = true;
2259 }
2260 else
2261 rc = VERR_VD_IMAGE_READ_ONLY;
2262 }
2263 else
2264 rc = VERR_VD_NOT_OPENED;
2265
2266 LogFlowFunc(("returns %Rrc\n", rc));
2267 return rc;
2268}
2269
2270/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentModificationUuid} */
2271static DECLCALLBACK(int) vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2272{
2273 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2274 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2275 int rc;
2276
2277 AssertPtr(pImage);
2278
2279 if (pImage)
2280 rc = VERR_NOT_SUPPORTED;
2281 else
2282 rc = VERR_VD_NOT_OPENED;
2283
2284 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2285 return rc;
2286}
2287
2288/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentModificationUuid} */
2289static DECLCALLBACK(int) vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2290{
2291 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2292 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2293 int rc;
2294
2295 AssertPtr(pImage);
2296
2297 if (pImage)
2298 {
2299 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2300 rc = VERR_NOT_SUPPORTED;
2301 else
2302 rc = VERR_VD_IMAGE_READ_ONLY;
2303 }
2304 else
2305 rc = VERR_VD_NOT_OPENED;
2306
2307 LogFlowFunc(("returns %Rrc\n", rc));
2308 return rc;
2309}
2310
2311/** @interface_method_impl{VBOXHDDBACKEND,pfnDump} */
2312static DECLCALLBACK(void) vhdDump(void *pBackendData)
2313{
2314 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2315
2316 AssertPtr(pImage);
2317 if (pImage)
2318 {
2319 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
2320 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2321 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2322 VHD_SECTOR_SIZE);
2323 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
2324 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
2325 }
2326}
2327
2328/** @interface_method_impl{VBOXHDDBACKEND,pfnGetTimestamp} */
2329static DECLCALLBACK(int) vhdGetTimestamp(void *pBackendData, PRTTIMESPEC pTimestamp)
2330{
2331 int rc = VINF_SUCCESS;
2332 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2333
2334 AssertPtr(pImage);
2335
2336 if (pImage)
2337 rc = vdIfIoIntFileGetModificationTime(pImage->pIfIo, pImage->pszFilename, pTimestamp);
2338 else
2339 rc = VERR_VD_NOT_OPENED;
2340
2341 LogFlowFunc(("returns %Rrc\n", rc));
2342 return rc;
2343}
2344
2345/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentTimestamp} */
2346static DECLCALLBACK(int) vhdGetParentTimestamp(void *pBackendData, PRTTIMESPEC pTimestamp)
2347{
2348 int rc = VINF_SUCCESS;
2349 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2350
2351 AssertPtr(pImage);
2352
2353 if (pImage)
2354 vhdTime2RtTime(pTimestamp, pImage->u32ParentTimestamp);
2355 else
2356 rc = VERR_VD_NOT_OPENED;
2357
2358 LogFlowFunc(("returns %Rrc\n", rc));
2359 return rc;
2360}
2361
2362/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentTimestamp} */
2363static DECLCALLBACK(int) vhdSetParentTimestamp(void *pBackendData, PCRTTIMESPEC pTimestamp)
2364{
2365 int rc = VINF_SUCCESS;
2366 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2367
2368 AssertPtr(pImage);
2369 if (pImage)
2370 {
2371 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2372 rc = VERR_VD_IMAGE_READ_ONLY;
2373 else
2374 {
2375 pImage->u32ParentTimestamp = vhdRtTime2VhdTime(pTimestamp);
2376 pImage->fDynHdrNeedsUpdate = true;
2377 }
2378 }
2379 else
2380 rc = VERR_VD_NOT_OPENED;
2381
2382 LogFlowFunc(("returns %Rrc\n", rc));
2383 return rc;
2384}
2385
2386/** @interface_method_impl{VBOXHDDBACKEND,pfnGetParentFilename} */
2387static DECLCALLBACK(int) vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
2388{
2389 int rc = VINF_SUCCESS;
2390 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2391
2392 AssertPtr(pImage);
2393 if (pImage)
2394 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
2395 else
2396 rc = VERR_VD_NOT_OPENED;
2397
2398 LogFlowFunc(("returns %Rrc\n", rc));
2399 return rc;
2400}
2401
2402/** @interface_method_impl{VBOXHDDBACKEND,pfnSetParentFilename} */
2403static DECLCALLBACK(int) vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
2404{
2405 int rc = VINF_SUCCESS;
2406 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2407
2408 AssertPtr(pImage);
2409 if (pImage)
2410 {
2411 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2412 rc = VERR_VD_IMAGE_READ_ONLY;
2413 else
2414 {
2415 if (pImage->pszParentFilename)
2416 RTStrFree(pImage->pszParentFilename);
2417 pImage->pszParentFilename = RTStrDup(pszParentFilename);
2418 if (!pImage->pszParentFilename)
2419 rc = VERR_NO_MEMORY;
2420 else
2421 pImage->fDynHdrNeedsUpdate = true;
2422 }
2423 }
2424 else
2425 rc = VERR_VD_NOT_OPENED;
2426
2427 LogFlowFunc(("returns %Rrc\n", rc));
2428 return rc;
2429}
2430
2431/** @interface_method_impl{VBOXHDDBACKEND,pfnCompact} */
2432static DECLCALLBACK(int) vhdCompact(void *pBackendData, unsigned uPercentStart,
2433 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2434 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2435{
2436 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2437 int rc = VINF_SUCCESS;
2438 void *pvBuf = NULL, *pvReplace = NULL;
2439 uint32_t *paBlocks = NULL;
2440
2441 DECLCALLBACKMEMBER(int, pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2442 void *pvParent = NULL;
2443 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2444 if (pIfParentState)
2445 {
2446 pfnParentRead = pIfParentState->pfnParentRead;
2447 pvParent = pIfParentState->Core.pvUser;
2448 }
2449
2450 PFNVDPROGRESS pfnProgress = NULL;
2451 void *pvUser = NULL;
2452 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2453 if (pIfProgress)
2454 {
2455 pfnProgress = pIfProgress->pfnProgress;
2456 pvUser = pIfProgress->Core.pvUser;
2457 }
2458
2459 do
2460 {
2461 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2462
2463 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2464 rc = VERR_VD_IMAGE_READ_ONLY);
2465
2466 /* Reject fixed images as they don't have a BAT. */
2467 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2468 {
2469 rc = VERR_NOT_SUPPORTED;
2470 break;
2471 }
2472
2473 if (pfnParentRead)
2474 {
2475 pvParent = RTMemTmpAlloc(pImage->cbDataBlock);
2476 AssertBreakStmt(pvParent, rc = VERR_NO_MEMORY);
2477 }
2478 pvBuf = RTMemTmpAlloc(pImage->cbDataBlock);
2479 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
2480
2481 unsigned cBlocksAllocated = 0;
2482 unsigned cBlocksToMove = 0;
2483 unsigned cBlocks = pImage->cBlockAllocationTableEntries;
2484 uint32_t offBlocksStart = ~0U; /* Start offset of data blocks in sectors. */
2485 uint32_t *paBat = pImage->pBlockAllocationTable;
2486
2487 /* Count the number of allocated blocks and find the start offset for the data blocks. */
2488 for (unsigned i = 0; i < cBlocks; i++)
2489 if (paBat[i] != ~0U)
2490 {
2491 cBlocksAllocated++;
2492 if (paBat[i] < offBlocksStart)
2493 offBlocksStart = paBat[i];
2494 }
2495
2496 if (!cBlocksAllocated)
2497 {
2498 /* Nothing to do. */
2499 rc = VINF_SUCCESS;
2500 break;
2501 }
2502
2503 paBlocks = (uint32_t *)RTMemTmpAllocZ(cBlocksAllocated * sizeof(uint32_t));
2504 AssertBreakStmt(paBlocks, rc = VERR_NO_MEMORY);
2505
2506 /* Invalidate the back resolving array. */
2507 for (unsigned i = 0; i < cBlocksAllocated; i++)
2508 paBlocks[i] = ~0U;
2509
2510 /* Fill the back resolving table. */
2511 for (unsigned i = 0; i < cBlocks; i++)
2512 if (paBat[i] != ~0U)
2513 {
2514 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2515 if ( idxBlock < cBlocksAllocated
2516 && paBlocks[idxBlock] == ~0U)
2517 paBlocks[idxBlock] = i;
2518 else
2519 {
2520 /* The image is in an inconsistent state. Don't go further. */
2521 rc = VERR_INVALID_STATE;
2522 break;
2523 }
2524 }
2525
2526 if (RT_FAILURE(rc))
2527 break;
2528
2529 /* Find redundant information and update the block pointers
2530 * accordingly, creating bubbles. Keep disk up to date, as this
2531 * enables cancelling. */
2532 for (unsigned i = 0; i < cBlocks; i++)
2533 {
2534 if (paBat[i] != ~0U)
2535 {
2536 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2537
2538 /* Block present in image file, read relevant data. */
2539 uint64_t u64Offset = ((uint64_t)paBat[i] + pImage->cDataBlockBitmapSectors) * VHD_SECTOR_SIZE;
2540 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2541 u64Offset, pvBuf, pImage->cbDataBlock);
2542 if (RT_FAILURE(rc))
2543 break;
2544
2545 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)pImage->cbDataBlock * 8) == -1)
2546 {
2547 paBat[i] = ~0;
2548 paBlocks[idxBlock] = ~0U;
2549 /* Adjust progress info, one block to be relocated. */
2550 cBlocksToMove++;
2551 }
2552 else if (pfnParentRead)
2553 {
2554 rc = pfnParentRead(pvParent, (uint64_t)i * pImage->cbDataBlock, pvParent, pImage->cbDataBlock);
2555 if (RT_FAILURE(rc))
2556 break;
2557 if (!memcmp(pvParent, pvBuf, pImage->cbDataBlock))
2558 {
2559 paBat[i] = ~0U;
2560 paBlocks[idxBlock] = ~0U;
2561 /* Adjust progress info, one block to be relocated. */
2562 cBlocksToMove++;
2563 }
2564 }
2565 }
2566
2567 if (pIfProgress && pIfProgress->pfnProgress)
2568 {
2569 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2570 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2571 if (RT_FAILURE(rc))
2572 break;
2573 }
2574 }
2575
2576 if (RT_SUCCESS(rc))
2577 {
2578 /* Fill bubbles with other data (if available). */
2579 unsigned cBlocksMoved = 0;
2580 unsigned uBlockUsedPos = cBlocksAllocated;
2581 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of whole block containing the bitmap and the user data. */
2582
2583 /* Allocate data buffer to hold the data block and allocation bitmap in front of the actual data. */
2584 RTMemTmpFree(pvBuf);
2585 pvBuf = RTMemTmpAllocZ(cbBlock);
2586 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
2587
2588 for (unsigned i = 0; i < cBlocksAllocated; i++)
2589 {
2590 unsigned uBlock = paBlocks[i];
2591 if (uBlock == ~0U)
2592 {
2593 unsigned uBlockData = ~0U;
2594 while (uBlockUsedPos > i && uBlockData == ~0U)
2595 {
2596 uBlockUsedPos--;
2597 uBlockData = paBlocks[uBlockUsedPos];
2598 }
2599 /* Terminate early if there is no block which needs copying. */
2600 if (uBlockUsedPos == i)
2601 break;
2602 uint64_t u64Offset = (uint64_t)uBlockUsedPos * cbBlock
2603 + (offBlocksStart * VHD_SECTOR_SIZE);
2604 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2605 u64Offset, pvBuf, cbBlock);
2606 if (RT_FAILURE(rc))
2607 break;
2608
2609 u64Offset = (uint64_t)i * cbBlock
2610 + (offBlocksStart * VHD_SECTOR_SIZE);
2611 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2612 u64Offset, pvBuf, cbBlock);
2613 if (RT_FAILURE(rc))
2614 break;
2615
2616 paBat[uBlockData] = i*(pImage->cSectorsPerDataBlock + pImage->cDataBlockBitmapSectors) + offBlocksStart;
2617
2618 /* Truncate the file but leave enough room for the footer to avoid
2619 * races if other processes fill the whole harddisk. */
2620 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2621 pImage->uCurrentEndOfFile - cbBlock + VHD_SECTOR_SIZE);
2622 if (RT_FAILURE(rc))
2623 break;
2624
2625 /* Update pointers and write footer. */
2626 pImage->uCurrentEndOfFile -= cbBlock;
2627
2628 /* We're kinda screwed if this failes. */
2629 rc = vhdUpdateFooter(pImage);
2630 if (RT_FAILURE(rc))
2631 break;
2632
2633 paBlocks[i] = uBlockData;
2634 paBlocks[uBlockUsedPos] = ~0U;
2635 cBlocksMoved++;
2636 }
2637
2638 if (pIfProgress && pIfProgress->pfnProgress)
2639 {
2640 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2641 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2642
2643 if (RT_FAILURE(rc))
2644 break;
2645 }
2646 }
2647 }
2648
2649 /* Write the new BAT in any case. */
2650 rc = vhdFlushImage(pImage);
2651 } while (0);
2652
2653 if (paBlocks)
2654 RTMemTmpFree(paBlocks);
2655 if (pvParent)
2656 RTMemTmpFree(pvParent);
2657 if (pvBuf)
2658 RTMemTmpFree(pvBuf);
2659
2660 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2661 {
2662 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2663 uPercentStart + uPercentSpan);
2664 }
2665
2666 LogFlowFunc(("returns %Rrc\n", rc));
2667 return rc;
2668}
2669
2670/** @interface_method_impl{VBOXHDDBACKEND,pfnResize} */
2671static DECLCALLBACK(int) vhdResize(void *pBackendData, uint64_t cbSize,
2672 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2673 unsigned uPercentStart, unsigned uPercentSpan,
2674 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2675 PVDINTERFACE pVDIfsOperation)
2676{
2677 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2678 int rc = VINF_SUCCESS;
2679
2680 PFNVDPROGRESS pfnProgress = NULL;
2681 void *pvUser = NULL;
2682 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2683 if (pIfProgress)
2684 {
2685 pfnProgress = pIfProgress->pfnProgress;
2686 pvUser = pIfProgress->Core.pvUser;
2687 }
2688
2689 /* Making the image smaller is not supported at the moment. */
2690 if ( cbSize < pImage->cbSize
2691 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2692 rc = VERR_NOT_SUPPORTED;
2693 else if (cbSize > pImage->cbSize)
2694 {
2695 unsigned cBlocksAllocated = 0;
2696 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of a block including the sector bitmap. */
2697 uint32_t cBlocksNew = cbSize / pImage->cbDataBlock; /** < New number of blocks in the image after the resize */
2698 if (cbSize % pImage->cbDataBlock)
2699 cBlocksNew++;
2700
2701 uint32_t cBlocksOld = pImage->cBlockAllocationTableEntries; /** < Number of blocks before the resize. */
2702 uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE); /** < Required space for the block array after the resize. */
2703 uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
2704 uint64_t offStartDataOld = ~0ULL;
2705
2706 /* Go through the BAT and find the data start offset. */
2707 for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
2708 {
2709 if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
2710 {
2711 uint64_t offStartBlock = (uint64_t)pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
2712 if (offStartBlock < offStartDataOld)
2713 offStartDataOld = offStartBlock;
2714 cBlocksAllocated++;
2715 }
2716 }
2717
2718 if ( offStartDataOld != offStartDataNew
2719 && cBlocksAllocated > 0)
2720 {
2721 /* Calculate how many sectors nee to be relocated. */
2722 uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
2723 unsigned cBlocksReloc = (unsigned)(cbOverlapping / cbBlock);
2724 if (cbOverlapping % cbBlock)
2725 cBlocksReloc++;
2726
2727 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2728 offStartDataNew = offStartDataOld;
2729
2730 /* Do the relocation. */
2731 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2732
2733 /*
2734 * Get the blocks we need to relocate first, they are appended to the end
2735 * of the image.
2736 */
2737 void *pvBuf = NULL, *pvZero = NULL;
2738 do
2739 {
2740 /* Allocate data buffer. */
2741 pvBuf = RTMemAllocZ(cbBlock);
2742 if (!pvBuf)
2743 {
2744 rc = VERR_NO_MEMORY;
2745 break;
2746 }
2747
2748 /* Allocate buffer for overwriting with zeroes. */
2749 pvZero = RTMemAllocZ(cbBlock);
2750 if (!pvZero)
2751 {
2752 rc = VERR_NO_MEMORY;
2753 break;
2754 }
2755
2756 for (unsigned i = 0; i < cBlocksReloc; i++)
2757 {
2758 uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
2759
2760 /* Search the index in the block table. */
2761 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2762 {
2763 if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
2764 {
2765 /* Read data and append to the end of the image. */
2766 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2767 offStartDataNew, pvBuf, cbBlock);
2768 if (RT_FAILURE(rc))
2769 break;
2770
2771 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2772 pImage->uCurrentEndOfFile, pvBuf, cbBlock);
2773 if (RT_FAILURE(rc))
2774 break;
2775
2776 /* Zero out the old block area. */
2777 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2778 offStartDataNew, pvZero, cbBlock);
2779 if (RT_FAILURE(rc))
2780 break;
2781
2782 /* Update block counter. */
2783 pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2784
2785 pImage->uCurrentEndOfFile += cbBlock;
2786
2787 /* Continue with the next block. */
2788 break;
2789 }
2790 }
2791
2792 if (RT_FAILURE(rc))
2793 break;
2794
2795 offStartDataNew += cbBlock;
2796 }
2797 } while (0);
2798
2799 if (pvBuf)
2800 RTMemFree(pvBuf);
2801 if (pvZero)
2802 RTMemFree(pvZero);
2803 }
2804
2805 /*
2806 * Relocation done, expand the block array and update the header with
2807 * the new data.
2808 */
2809 if (RT_SUCCESS(rc))
2810 {
2811 uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
2812 if (paBlocksNew)
2813 {
2814 pImage->pBlockAllocationTable = paBlocksNew;
2815
2816 /* Mark the new blocks as unallocated. */
2817 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2818 pImage->pBlockAllocationTable[idxBlock] = ~0U;
2819 }
2820 else
2821 rc = VERR_NO_MEMORY;
2822
2823 if (RT_SUCCESS(rc))
2824 {
2825 /* Write the block array before updating the rest. */
2826 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2827 pImage->uBlockAllocationTableOffset,
2828 pImage->pBlockAllocationTable,
2829 cBlocksNew * sizeof(uint32_t));
2830 }
2831
2832 if (RT_SUCCESS(rc))
2833 {
2834 /* Update size and new block count. */
2835 pImage->cBlockAllocationTableEntries = cBlocksNew;
2836 pImage->cbSize = cbSize;
2837
2838 /* Update geometry. */
2839 pImage->PCHSGeometry = *pPCHSGeometry;
2840 pImage->LCHSGeometry = *pLCHSGeometry;
2841 }
2842 }
2843
2844 /* Update header information in base image file. */
2845 pImage->fDynHdrNeedsUpdate = true;
2846 vhdFlushImage(pImage);
2847 }
2848 /* Same size doesn't change the image at all. */
2849
2850 LogFlowFunc(("returns %Rrc\n", rc));
2851 return rc;
2852}
2853
2854/** @interface_method_impl{VBOXHDDBACKEND,pfnRepair} */
2855static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
2856 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
2857{
2858 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
2859 int rc;
2860 PVDINTERFACEERROR pIfError;
2861 PVDINTERFACEIOINT pIfIo;
2862 PVDIOSTORAGE pStorage;
2863 uint64_t cbFile;
2864 VHDFooter vhdFooter;
2865 VHDDynamicDiskHeader dynamicDiskHeader;
2866 uint32_t *paBat = NULL;
2867 uint32_t *pu32BlockBitmap = NULL;
2868
2869 pIfIo = VDIfIoIntGet(pVDIfsImage);
2870 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
2871
2872 pIfError = VDIfErrorGet(pVDIfsDisk);
2873
2874 do
2875 {
2876 uint64_t offDynamicDiskHeader = 0;
2877 uint64_t offBat = 0;
2878 uint64_t offFooter = 0;
2879 uint32_t cBatEntries = 0;
2880 bool fDynamic = false;
2881 bool fRepairFooter = false;
2882 bool fRepairBat = false;
2883 bool fRepairDynHeader = false;
2884
2885 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
2886 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
2887 ? VD_OPEN_FLAGS_READONLY
2888 : 0,
2889 false /* fCreate */),
2890 &pStorage);
2891 if (RT_FAILURE(rc))
2892 {
2893 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to open image \"%s\"", pszFilename);
2894 break;
2895 }
2896
2897 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
2898 if (RT_FAILURE(rc))
2899 {
2900 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to query image size");
2901 break;
2902 }
2903
2904 if (cbFile < sizeof(VHDFooter))
2905 {
2906 rc = vdIfError(pIfError, VERR_VD_INVALID_SIZE, RT_SRC_POS,
2907 "Image must be at least %u bytes (got %llu)",
2908 sizeof(VHDFooter), cbFile);
2909 break;
2910 }
2911
2912 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
2913 &vhdFooter, sizeof(VHDFooter));
2914 if (RT_FAILURE(rc))
2915 {
2916 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read footer of image");
2917 break;
2918 }
2919
2920 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
2921 {
2922 /* Dynamic images have a backup at the beginning of the image. */
2923 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
2924 &vhdFooter, sizeof(VHDFooter));
2925 if (RT_FAILURE(rc))
2926 {
2927 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read header of image");
2928 break;
2929 }
2930
2931 /*
2932 * Check for the header, if this fails the image is either completely corrupted
2933 * and impossible to repair or in another format.
2934 */
2935 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
2936 {
2937 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2938 "No valid VHD structures found");
2939 break;
2940 }
2941 else
2942 vdIfErrorMessage(pIfError, "Missing footer structure, using backup\n");
2943
2944 /* Remember to fix the footer structure. */
2945 fRepairFooter = true;
2946 }
2947
2948 offFooter = cbFile - sizeof(VHDFooter);
2949
2950 /* Verify that checksums match. */
2951 uint32_t u32ChkSumOld = RT_BE2H_U32(vhdFooter.Checksum);
2952 vhdFooter.Checksum = 0;
2953 uint32_t u32ChkSum = vhdChecksum(&vhdFooter, sizeof(VHDFooter));
2954
2955 vhdFooter.Checksum = RT_H2BE_U32(u32ChkSum);
2956
2957 if (u32ChkSumOld != u32ChkSum)
2958 {
2959 vdIfErrorMessage(pIfError, "Checksum is invalid (should be %u got %u), repairing\n",
2960 u32ChkSum, u32ChkSumOld);
2961 fRepairFooter = true;
2962 break;
2963 }
2964
2965 switch (RT_BE2H_U32(vhdFooter.DiskType))
2966 {
2967 case VHD_FOOTER_DISK_TYPE_FIXED:
2968 fDynamic = false;
2969 break;
2970 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
2971 fDynamic = true;
2972 break;
2973 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
2974 fDynamic = true;
2975 break;
2976 default:
2977 {
2978 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2979 "VHD image type %u is not supported",
2980 RT_BE2H_U32(vhdFooter.DiskType));
2981 break;
2982 }
2983 }
2984
2985 /* Load and check dynamic disk header if required. */
2986 if (fDynamic)
2987 {
2988 size_t cbBlock;
2989
2990 offDynamicDiskHeader = RT_BE2H_U64(vhdFooter.DataOffset);
2991 if (offDynamicDiskHeader + sizeof(VHDDynamicDiskHeader) > cbFile)
2992 {
2993 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2994 "VHD image type is not supported");
2995 break;
2996 }
2997
2998 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offDynamicDiskHeader,
2999 &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
3000 if (RT_FAILURE(rc))
3001 {
3002 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3003 "Failed to read dynamic disk header (at %llu), %Rrc",
3004 offDynamicDiskHeader, rc);
3005 break;
3006 }
3007
3008 /* Verify that checksums match. */
3009 u32ChkSumOld = RT_BE2H_U32(dynamicDiskHeader.Checksum);
3010 dynamicDiskHeader.Checksum = 0;
3011 u32ChkSum = vhdChecksum(&dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
3012
3013 dynamicDiskHeader.Checksum = RT_H2BE_U32(u32ChkSum);
3014
3015 if (u32ChkSumOld != u32ChkSum)
3016 {
3017 vdIfErrorMessage(pIfError, "Checksum of dynamic disk header is invalid (should be %u got %u), repairing\n",
3018 u32ChkSum, u32ChkSumOld);
3019 fRepairDynHeader = true;
3020 break;
3021 }
3022
3023 /* Read the block allocation table and fix any inconsistencies. */
3024 offBat = RT_BE2H_U64(dynamicDiskHeader.TableOffset);
3025 cBatEntries = RT_BE2H_U32(dynamicDiskHeader.MaxTableEntries);
3026 cbBlock = RT_BE2H_U32(dynamicDiskHeader.BlockSize);
3027 cbBlock += cbBlock / VHD_SECTOR_SIZE / 8;
3028
3029 if (offBat + cBatEntries * sizeof(uint32_t) > cbFile)
3030 {
3031 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3032 "Block allocation table is not inside the image");
3033 break;
3034 }
3035
3036 paBat = (uint32_t *)RTMemAllocZ(cBatEntries * sizeof(uint32_t));
3037 if (!paBat)
3038 {
3039 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3040 "Could not allocate memory for the block allocation table (%u bytes)",
3041 cBatEntries * sizeof(uint32_t));
3042 break;
3043 }
3044
3045 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offBat, paBat,
3046 cBatEntries * sizeof(uint32_t));
3047 if (RT_FAILURE(rc))
3048 {
3049 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3050 "Could not read block allocation table (at %llu), %Rrc",
3051 offBat, rc);
3052 break;
3053 }
3054
3055 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(cBatEntries / 8, 4));
3056 if (!pu32BlockBitmap)
3057 {
3058 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3059 "Failed to allocate memory for block bitmap");
3060 break;
3061 }
3062
3063 uint32_t idxMinBlock = UINT32_C(0xffffffff);
3064 for (uint32_t i = 0; i < cBatEntries; i++)
3065 {
3066 paBat[i] = RT_BE2H_U32(paBat[i]);
3067 if (paBat[i] < idxMinBlock)
3068 idxMinBlock = paBat[i];
3069 }
3070
3071 vdIfErrorMessage(pIfError, "First data block at sector %u\n", idxMinBlock);
3072
3073 for (uint32_t i = 0; i < cBatEntries; i++)
3074 {
3075 if (paBat[i] != UINT32_C(0xffffffff))
3076 {
3077 uint64_t offBlock =(uint64_t)paBat[i] * VHD_SECTOR_SIZE;
3078
3079 /*
3080 * Check that the offsets are valid (inside of the image) and
3081 * that there are no double references.
3082 */
3083 if (offBlock + cbBlock > cbFile)
3084 {
3085 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
3086 i, offBlock);
3087 paBat[i] = UINT32_C(0xffffffff);
3088 fRepairBat = true;
3089 }
3090 else if (offBlock + cbBlock > offFooter)
3091 {
3092 vdIfErrorMessage(pIfError, "Entry %u intersects with footer, aligning footer\n",
3093 i);
3094 offFooter = offBlock + cbBlock;
3095 fRepairBat = true;
3096 }
3097
3098 if ( paBat[i] != UINT32_C(0xffffffff)
3099 && ASMBitTestAndSet(pu32BlockBitmap, (uint32_t)((paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE))))
3100 {
3101 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
3102 i);
3103 paBat[i] = UINT32_C(0xffffffff);
3104 fRepairBat = true;
3105 }
3106 }
3107 }
3108 }
3109
3110 /* Write repaired structures now. */
3111 if (!(fRepairBat || fRepairDynHeader || fRepairFooter))
3112 vdIfErrorMessage(pIfError, "VHD image is in a consistent state, no repair required\n");
3113 else if (!(fFlags & VD_REPAIR_DRY_RUN))
3114 {
3115 if (fRepairBat)
3116 {
3117 for (uint32_t i = 0; i < cBatEntries; i++)
3118 paBat[i] = RT_H2BE_U32(paBat[i]);
3119
3120 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
3121
3122 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offBat, paBat,
3123 cBatEntries * sizeof(uint32_t));
3124 if (RT_FAILURE(rc))
3125 {
3126 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3127 "Could not write repaired block allocation table (at %llu), %Rrc",
3128 offBat, rc);
3129 break;
3130 }
3131 }
3132
3133 if (fRepairDynHeader)
3134 {
3135 Assert(fDynamic);
3136
3137 vdIfErrorMessage(pIfError, "Writing repaired dynamic disk header...\n");
3138 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offDynamicDiskHeader, &dynamicDiskHeader,
3139 sizeof(VHDDynamicDiskHeader));
3140 if (RT_FAILURE(rc))
3141 {
3142 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3143 "Could not write repaired dynamic disk header (at %llu), %Rrc",
3144 offDynamicDiskHeader, rc);
3145 break;
3146 }
3147 }
3148
3149 if (fRepairFooter)
3150 {
3151 vdIfErrorMessage(pIfError, "Writing repaired Footer...\n");
3152
3153 if (fDynamic)
3154 {
3155 /* Write backup at image beginning. */
3156 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, 0, &vhdFooter,
3157 sizeof(VHDFooter));
3158 if (RT_FAILURE(rc))
3159 {
3160 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3161 "Could not write repaired backup footer (at %llu), %Rrc",
3162 0, rc);
3163 break;
3164 }
3165 }
3166
3167 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offFooter, &vhdFooter,
3168 sizeof(VHDFooter));
3169 if (RT_FAILURE(rc))
3170 {
3171 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3172 "Could not write repaired footer (at %llu), %Rrc",
3173 cbFile - sizeof(VHDFooter), rc);
3174 break;
3175 }
3176 }
3177
3178 vdIfErrorMessage(pIfError, "Corrupted VHD image repaired successfully\n");
3179 }
3180 } while(0);
3181
3182 if (paBat)
3183 RTMemFree(paBat);
3184
3185 if (pu32BlockBitmap)
3186 RTMemFree(pu32BlockBitmap);
3187
3188 if (pStorage)
3189 {
3190 int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
3191 if (RT_SUCCESS(rc))
3192 rc = rc2; /* Propagate status code only when repairing the image was successful. */
3193 }
3194
3195 LogFlowFunc(("returns %Rrc\n", rc));
3196 return rc;
3197}
3198
3199
3200const VBOXHDDBACKEND g_VhdBackend =
3201{
3202 /* pszBackendName */
3203 "VHD",
3204 /* cbSize */
3205 sizeof(VBOXHDDBACKEND),
3206 /* uBackendCaps */
3207 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
3208 VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
3209 VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_PREFERRED,
3210 /* paFileExtensions */
3211 s_aVhdFileExtensions,
3212 /* paConfigInfo */
3213 NULL,
3214 /* pfnCheckIfValid */
3215 vhdCheckIfValid,
3216 /* pfnOpen */
3217 vhdOpen,
3218 /* pfnCreate */
3219 vhdCreate,
3220 /* pfnRename */
3221 vhdRename,
3222 /* pfnClose */
3223 vhdClose,
3224 /* pfnRead */
3225 vhdRead,
3226 /* pfnWrite */
3227 vhdWrite,
3228 /* pfnFlush */
3229 vhdFlush,
3230 /* pfnDiscard */
3231 NULL,
3232 /* pfnGetVersion */
3233 vhdGetVersion,
3234 /* pfnGetSectorSize */
3235 vhdGetSectorSize,
3236 /* pfnGetSize */
3237 vhdGetSize,
3238 /* pfnGetFileSize */
3239 vhdGetFileSize,
3240 /* pfnGetPCHSGeometry */
3241 vhdGetPCHSGeometry,
3242 /* pfnSetPCHSGeometry */
3243 vhdSetPCHSGeometry,
3244 /* pfnGetLCHSGeometry */
3245 vhdGetLCHSGeometry,
3246 /* pfnSetLCHSGeometry */
3247 vhdSetLCHSGeometry,
3248 /* pfnGetImageFlags */
3249 vhdGetImageFlags,
3250 /* pfnGetOpenFlags */
3251 vhdGetOpenFlags,
3252 /* pfnSetOpenFlags */
3253 vhdSetOpenFlags,
3254 /* pfnGetComment */
3255 vhdGetComment,
3256 /* pfnSetComment */
3257 vhdSetComment,
3258 /* pfnGetUuid */
3259 vhdGetUuid,
3260 /* pfnSetUuid */
3261 vhdSetUuid,
3262 /* pfnGetModificationUuid */
3263 vhdGetModificationUuid,
3264 /* pfnSetModificationUuid */
3265 vhdSetModificationUuid,
3266 /* pfnGetParentUuid */
3267 vhdGetParentUuid,
3268 /* pfnSetParentUuid */
3269 vhdSetParentUuid,
3270 /* pfnGetParentModificationUuid */
3271 vhdGetParentModificationUuid,
3272 /* pfnSetParentModificationUuid */
3273 vhdSetParentModificationUuid,
3274 /* pfnDump */
3275 vhdDump,
3276 /* pfnGetTimestamp */
3277 vhdGetTimestamp,
3278 /* pfnGetParentTimestamp */
3279 vhdGetParentTimestamp,
3280 /* pfnSetParentTimestamp */
3281 vhdSetParentTimestamp,
3282 /* pfnGetParentFilename */
3283 vhdGetParentFilename,
3284 /* pfnSetParentFilename */
3285 vhdSetParentFilename,
3286 /* pfnComposeLocation */
3287 genericFileComposeLocation,
3288 /* pfnComposeName */
3289 genericFileComposeName,
3290 /* pfnCompact */
3291 vhdCompact,
3292 /* pfnResize */
3293 vhdResize,
3294 /* pfnRepair */
3295 vhdRepair,
3296 /* pfnTraverseMetadata */
3297 NULL
3298};
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