VirtualBox

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

Last change on this file since 93231 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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