VirtualBox

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

Last change on this file since 76864 was 76589, checked in by vboxsync, 6 years ago

IPRT: Attempted to address some the more obvious shortcomings of RTPathCalcRelative. Had to add a parameter that clearifies whether the from path is a file (VHD usage) or directory (rest).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 119.0 KB
Line 
1/* $Id: VHD.cpp 76589 2019-01-01 08:56:07Z vboxsync $ */
2/** @file
3 * VHD Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 return rc;
799
800 /*
801 * Because the offset entries inside the allocation table are stored big endian
802 * we need to convert them into host endian.
803 */
804 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
805 if (!pImage->pBlockAllocationTable)
806 {
807 RTMemFree(pBlockAllocationTable);
808 return VERR_NO_MEMORY;
809 }
810
811 for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
812 pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
813
814 RTMemFree(pBlockAllocationTable);
815
816 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
817 memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
818
819 return rc;
820}
821
822static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
823{
824 uint64_t FileSize;
825 VHDFooter vhdFooter;
826
827 pImage->uOpenFlags = uOpenFlags;
828
829 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
830 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
831 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
832
833 /*
834 * Open the image.
835 */
836 int rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
837 VDOpenFlagsToFileOpenFlags(uOpenFlags,
838 false /* fCreate */),
839 &pImage->pStorage);
840 if (RT_FAILURE(rc))
841 {
842 /* Do NOT signal an appropriate error here, as the VD layer has the
843 * choice of retrying the open if it failed. */
844 return rc;
845 }
846
847 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &FileSize);
848 pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
849
850 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
851 &vhdFooter, sizeof(VHDFooter));
852 if (RT_SUCCESS(rc))
853 {
854 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
855 {
856 /*
857 * There is also a backup header at the beginning in case the image got corrupted.
858 * Such corrupted images are detected here to let the open handler repair it later.
859 */
860 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
861 &vhdFooter, sizeof(VHDFooter));
862 if (RT_SUCCESS(rc))
863 {
864 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
865 rc = VERR_VD_VHD_INVALID_HEADER;
866 else
867 rc = VERR_VD_IMAGE_CORRUPTED;
868 }
869 }
870 }
871
872 if (RT_FAILURE(rc))
873 return rc;
874
875 switch (RT_BE2H_U32(vhdFooter.DiskType))
876 {
877 case VHD_FOOTER_DISK_TYPE_FIXED:
878 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
879 break;
880 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
881 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
882 break;
883 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
884 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
885 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
886 break;
887 default:
888 return VERR_NOT_IMPLEMENTED;
889 }
890
891 pImage->cbSize = RT_BE2H_U64(vhdFooter.CurSize);
892 pImage->LCHSGeometry.cCylinders = 0;
893 pImage->LCHSGeometry.cHeads = 0;
894 pImage->LCHSGeometry.cSectors = 0;
895 pImage->PCHSGeometry.cCylinders = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
896 pImage->PCHSGeometry.cHeads = vhdFooter.DiskGeometryHeads;
897 pImage->PCHSGeometry.cSectors = vhdFooter.DiskGeometrySectors;
898
899 /*
900 * Copy of the disk footer.
901 * If we allocate new blocks in differencing disks on write access
902 * the footer is overwritten. We need to write it at the end of the file.
903 */
904 memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
905
906 /*
907 * Is there a better way?
908 */
909 memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
910
911 pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
912 LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
913
914 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
915 rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
916
917 if (RT_SUCCESS(rc))
918 {
919 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
920 pImage->RegionList.fFlags = 0;
921 pImage->RegionList.cRegions = 1;
922
923 pRegion->offRegion = 0; /* Disk start. */
924 pRegion->cbBlock = 512;
925 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
926 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
927 pRegion->cbData = 512;
928 pRegion->cbMetadata = 0;
929 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
930 }
931 else
932 vhdFreeImage(pImage, false);
933 return rc;
934}
935
936/**
937 * Internal: Checks if a sector in the block bitmap is set
938 */
939DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
940{
941 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
942
943 /*
944 * The index of the bit in the byte of the data block bitmap.
945 * The most significant bit stands for a lower sector number.
946 */
947 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
948 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
949
950 AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
951 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
952
953 return ((*puBitmap) & RT_BIT(iBitInByte)) != 0;
954}
955
956/**
957 * Internal: Sets the given sector in the sector bitmap.
958 */
959DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
960{
961 RT_NOREF1(pImage);
962 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
963
964 /*
965 * The index of the bit in the byte of the data block bitmap.
966 * The most significant bit stands for a lower sector number.
967 */
968 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
969 uint8_t *puBitmap = pu8Bitmap + iBitmap;
970
971 AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
972 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
973
974 bool fClear = ((*puBitmap) & RT_BIT(iBitInByte)) == 0;
975 *puBitmap |= RT_BIT(iBitInByte);
976 return fClear;
977}
978
979/**
980 * Internal: Derive drive geometry from its size.
981 */
982static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
983{
984 uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
985 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
986
987 if (u64TotalSectors > 65535 * 16 * 255)
988 {
989 /* ATA disks limited to 127 GB. */
990 u64TotalSectors = 65535 * 16 * 255;
991 }
992
993 if (u64TotalSectors >= 65535 * 16 * 63)
994 {
995 u32SectorsPerTrack = 255;
996 u32Heads = 16;
997 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
998 }
999 else
1000 {
1001 u32SectorsPerTrack = 17;
1002 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1003
1004 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
1005
1006 if (u32Heads < 4)
1007 {
1008 u32Heads = 4;
1009 }
1010 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
1011 {
1012 u32SectorsPerTrack = 31;
1013 u32Heads = 16;
1014 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1015 }
1016 if (u32CylinderTimesHeads >= (u32Heads * 1024))
1017 {
1018 u32SectorsPerTrack = 63;
1019 u32Heads = 16;
1020 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1021 }
1022 }
1023 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
1024 pImage->PCHSGeometry.cHeads = u32Heads;
1025 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
1026 pImage->LCHSGeometry.cCylinders = 0;
1027 pImage->LCHSGeometry.cHeads = 0;
1028 pImage->LCHSGeometry.cSectors = 0;
1029}
1030
1031
1032static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
1033{
1034 RT_NOREF1(pImage);
1035 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
1036
1037 /*
1038 * The VHD spec states that the DataSpace field holds the number of sectors
1039 * required to store the parent locator path.
1040 * As it turned out VPC and Hyper-V store the amount of bytes reserved for the
1041 * path and not the number of sectors.
1042 */
1043
1044 /* Unicode absolute Windows path. */
1045 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
1046 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16));
1047 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1048 pLocator++;
1049 u64Offset += VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
1050 /* Unicode relative Windows path. */
1051 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
1052 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16));
1053 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1054 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
1055 return u64Offset;
1056}
1057
1058/**
1059 * Internal: Additional code for dynamic VHD image creation.
1060 */
1061static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
1062{
1063 int rc;
1064 VHDDynamicDiskHeader DynamicDiskHeader;
1065 uint32_t u32BlockAllocationTableSectors;
1066 void *pvTmp = NULL;
1067
1068 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
1069
1070 pImage->u64DataOffset = sizeof(VHDFooter);
1071 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1072 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1073 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1074 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1075 /* Align to sector boundary */
1076 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
1077 pImage->cDataBlockBitmapSectors++;
1078 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
1079 if (!pImage->pu8Bitmap)
1080 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1081
1082 /* Initialize BAT. */
1083 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1084 pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
1085 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1086 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1087 if (!pImage->pBlockAllocationTable)
1088 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1089
1090 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1091 {
1092 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1093 }
1094
1095 /* Round up to the sector size. */
1096 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
1097 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1098 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1099 else
1100 pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
1101
1102 /* Set dynamic image size. */
1103 pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1104 if (!pvTmp)
1105 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1106
1107 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, pvTmp,
1108 pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1109 if (RT_FAILURE(rc))
1110 {
1111 RTMemTmpFree(pvTmp);
1112 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1113 }
1114
1115 RTMemTmpFree(pvTmp);
1116
1117 /* Initialize and write the dynamic disk header. */
1118 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1119 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1120 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1121 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1122 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1123 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1124 /* Compute and update checksum. */
1125 DynamicDiskHeader.Checksum = 0;
1126 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1127
1128 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VHDFooter),
1129 &DynamicDiskHeader, sizeof(DynamicDiskHeader));
1130 if (RT_FAILURE(rc))
1131 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1132
1133 /* Write BAT. */
1134 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
1135 pImage->pBlockAllocationTable,
1136 pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1137 if (RT_FAILURE(rc))
1138 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1139
1140 return rc;
1141}
1142
1143/**
1144 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1145 */
1146static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
1147 unsigned uImageFlags, const char *pszComment,
1148 PCVDGEOMETRY pPCHSGeometry,
1149 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1150 unsigned uOpenFlags,
1151 PVDINTERFACEPROGRESS pIfProgress,
1152 unsigned uPercentStart, unsigned uPercentSpan)
1153{
1154 RT_NOREF3(pszComment, pPCHSGeometry, pLCHSGeometry);
1155 VHDFooter Footer;
1156 RTTIMESPEC now;
1157
1158 pImage->uOpenFlags = uOpenFlags;
1159 pImage->uImageFlags = uImageFlags;
1160 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1161
1162 int rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1163 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
1164 true /* fCreate */),
1165 &pImage->pStorage);
1166 if (RT_SUCCESS(rc))
1167 {
1168 pImage->cbSize = cbSize;
1169 pImage->ImageUuid = *pUuid;
1170 RTUuidClear(&pImage->ParentUuid);
1171 vhdSetDiskGeometry(pImage, cbSize);
1172
1173 /* Initialize the footer. */
1174 memset(&Footer, 0, sizeof(Footer));
1175 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1176 Footer.Features = RT_H2BE_U32(0x2);
1177 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1178 Footer.Timestamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1179 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1180 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1181#ifdef RT_OS_DARWIN
1182 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1183#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1184 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1185#endif
1186 Footer.OrigSize = RT_H2BE_U64(cbSize);
1187 Footer.CurSize = Footer.OrigSize;
1188 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1189 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1190 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1191 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1192 Footer.SavedState = 0;
1193
1194 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1195 {
1196 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1197 /*
1198 * Initialize fixed image.
1199 * "The size of the entire file is the size of the hard disk in
1200 * the guest operating system plus the size of the footer."
1201 */
1202 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1203 pImage->uCurrentEndOfFile = cbSize;
1204 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile + sizeof(VHDFooter),
1205 0 /* fFlags */, pIfProgress,
1206 uPercentStart, uPercentSpan);
1207 if (RT_FAILURE(rc))
1208 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1209 }
1210 else
1211 {
1212 /*
1213 * Initialize dynamic image.
1214 *
1215 * The overall structure of dynamic disk is:
1216 *
1217 * [Copy of hard disk footer (512 bytes)]
1218 * [Dynamic disk header (1024 bytes)]
1219 * [BAT (Block Allocation Table)]
1220 * [Parent Locators]
1221 * [Data block 1]
1222 * [Data block 2]
1223 * ...
1224 * [Data block N]
1225 * [Hard disk footer (512 bytes)]
1226 */
1227 Footer.DiskType = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
1228 ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
1229 : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1230 /* We are half way thorough with creation of image, let the caller know. */
1231 vdIfProgress(pIfProgress, (uPercentStart + uPercentSpan) / 2);
1232
1233 rc = vhdCreateDynamicImage(pImage, cbSize);
1234 }
1235
1236 if (RT_SUCCESS(rc))
1237 {
1238 /* Compute and update the footer checksum. */
1239 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1240 Footer.Checksum = 0;
1241 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1242
1243 pImage->vhdFooterCopy = Footer;
1244
1245 /* Store the footer */
1246 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
1247 &Footer, sizeof(Footer));
1248 if (RT_SUCCESS(rc))
1249 {
1250 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1251 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1252 {
1253 /* Write the copy of the footer. */
1254 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Footer, sizeof(Footer));
1255 if (RT_FAILURE(rc))
1256 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1257 }
1258 }
1259 else
1260 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1261 }
1262 }
1263 else
1264 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1265
1266 if (RT_SUCCESS(rc))
1267 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
1268
1269 if (RT_SUCCESS(rc))
1270 {
1271 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
1272 pImage->RegionList.fFlags = 0;
1273 pImage->RegionList.cRegions = 1;
1274
1275 pRegion->offRegion = 0; /* Disk start. */
1276 pRegion->cbBlock = 512;
1277 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
1278 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
1279 pRegion->cbData = 512;
1280 pRegion->cbMetadata = 0;
1281 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
1282 }
1283 else
1284 vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1285 return rc;
1286}
1287
1288
1289/** @interface_method_impl{VDIMAGEBACKEND,pfnProbe} */
1290static DECLCALLBACK(int) vhdProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1291 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1292{
1293 RT_NOREF1(pVDIfsDisk);
1294 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1295 PVDIOSTORAGE pStorage;
1296 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1297 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1298
1299 int rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1300 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1301 false /* fCreate */),
1302 &pStorage);
1303 if (RT_SUCCESS(rc))
1304 {
1305 uint64_t cbFile;
1306
1307 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1308 if ( RT_SUCCESS(rc)
1309 && cbFile >= sizeof(VHDFooter))
1310 {
1311 VHDFooter vhdFooter;
1312
1313 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
1314 &vhdFooter, sizeof(VHDFooter));
1315 if (RT_SUCCESS(rc))
1316 {
1317 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
1318 {
1319 /*
1320 * There is also a backup header at the beginning in case the image got corrupted.
1321 * Such corrupted images are detected here to let the open handler repair it later.
1322 */
1323 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &vhdFooter, sizeof(VHDFooter));
1324 if ( RT_FAILURE(rc)
1325 || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
1326 rc = VERR_VD_VHD_INVALID_HEADER;
1327 }
1328
1329 if (RT_SUCCESS(rc))
1330 *penmType = VDTYPE_HDD;
1331 }
1332 else
1333 rc = VERR_VD_VHD_INVALID_HEADER;
1334 }
1335 else if (RT_SUCCESS(rc))
1336 rc = VERR_VD_VHD_INVALID_HEADER;
1337
1338 vdIfIoIntFileClose(pIfIo, pStorage);
1339 }
1340
1341 LogFlowFunc(("returns %Rrc\n", rc));
1342 return rc;
1343}
1344
1345/** @interface_method_impl{VDIMAGEBACKEND,pfnOpen} */
1346static DECLCALLBACK(int) vhdOpen(const char *pszFilename, unsigned uOpenFlags,
1347 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1348 VDTYPE enmType, void **ppBackendData)
1349{
1350 RT_NOREF1(enmType); /**< @todo r=klaus make use of the type info. */
1351
1352 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
1353 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1354 int rc = VINF_SUCCESS;
1355
1356 /* Check open flags. All valid flags are supported. */
1357 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1358 AssertReturn((VALID_PTR(pszFilename) && *pszFilename), VERR_INVALID_PARAMETER);
1359
1360 PVHDIMAGE pImage = (PVHDIMAGE)RTMemAllocZ(RT_UOFFSETOF(VHDIMAGE, RegionList.aRegions[1]));
1361 if (RT_LIKELY(pImage))
1362 {
1363 pImage->pszFilename = pszFilename;
1364 pImage->pStorage = NULL;
1365 pImage->pVDIfsDisk = pVDIfsDisk;
1366 pImage->pVDIfsImage = pVDIfsImage;
1367
1368 rc = vhdOpenImage(pImage, uOpenFlags);
1369 if (RT_SUCCESS(rc))
1370 *ppBackendData = pImage;
1371 else
1372 RTMemFree(pImage);
1373 }
1374 else
1375 rc = VERR_NO_MEMORY;
1376
1377 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1378 return rc;
1379}
1380
1381/** @interface_method_impl{VDIMAGEBACKEND,pfnCreate} */
1382static DECLCALLBACK(int) vhdCreate(const char *pszFilename, uint64_t cbSize,
1383 unsigned uImageFlags, const char *pszComment,
1384 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1385 PCRTUUID pUuid, unsigned uOpenFlags,
1386 unsigned uPercentStart, unsigned uPercentSpan,
1387 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1388 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1389 void **ppBackendData)
1390{
1391 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",
1392 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1393 int rc;
1394 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1395
1396 /* Check the VD container type. */
1397 if (enmType != VDTYPE_HDD)
1398 return VERR_VD_INVALID_TYPE;
1399
1400 /* Check open flags. All valid flags are supported. */
1401 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
1402 AssertReturn( VALID_PTR(pszFilename)
1403 && *pszFilename
1404 && VALID_PTR(pPCHSGeometry)
1405 && VALID_PTR(pLCHSGeometry), VERR_INVALID_PARAMETER);
1406 /** @todo Check the values of other params */
1407
1408 PVHDIMAGE pImage = (PVHDIMAGE)RTMemAllocZ(RT_UOFFSETOF(VHDIMAGE, RegionList.aRegions[1]));
1409 if (RT_LIKELY(pImage))
1410 {
1411 pImage->pszFilename = pszFilename;
1412 pImage->pStorage = NULL;
1413 pImage->pVDIfsDisk = pVDIfsDisk;
1414 pImage->pVDIfsImage = pVDIfsImage;
1415
1416 /* Get I/O interface. */
1417 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1418 if (RT_LIKELY(VALID_PTR(pImage->pIfIo)))
1419 {
1420 rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
1421 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1422 pIfProgress, uPercentStart, uPercentSpan);
1423 if (RT_SUCCESS(rc))
1424 {
1425 /* So far the image is opened in read/write mode. Make sure the
1426 * image is opened in read-only mode if the caller requested that. */
1427 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1428 {
1429 vhdFreeImage(pImage, false);
1430 rc = vhdOpenImage(pImage, uOpenFlags);
1431 }
1432
1433 if (RT_SUCCESS(rc))
1434 *ppBackendData = pImage;
1435 }
1436 }
1437 else
1438 rc = VERR_INVALID_PARAMETER;
1439
1440 if (RT_FAILURE(rc))
1441 RTMemFree(pImage);
1442 }
1443 else
1444 rc = VERR_NO_MEMORY;
1445
1446 LogFlowFunc(("returns %Rrc\n", rc));
1447 return rc;
1448}
1449
1450/** @interface_method_impl{VDIMAGEBACKEND,pfnRename} */
1451static DECLCALLBACK(int) vhdRename(void *pBackendData, const char *pszFilename)
1452{
1453 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1454 int rc = VINF_SUCCESS;
1455 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1456
1457 /* Check arguments. */
1458 AssertReturn((pImage && pszFilename && *pszFilename), VERR_INVALID_PARAMETER);
1459
1460 /* Close the image. */
1461 rc = vhdFreeImage(pImage, false);
1462 if (RT_SUCCESS(rc))
1463 {
1464 /* Rename the file. */
1465 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1466 if (RT_SUCCESS(rc))
1467 {
1468 /* Update pImage with the new information. */
1469 pImage->pszFilename = pszFilename;
1470
1471 /* Open the old file with new name. */
1472 rc = vhdOpenImage(pImage, pImage->uOpenFlags);
1473 }
1474 else
1475 {
1476 /* The move failed, try to reopen the original image. */
1477 int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
1478 if (RT_FAILURE(rc2))
1479 rc = rc2;
1480 }
1481 }
1482
1483 LogFlowFunc(("returns %Rrc\n", rc));
1484 return rc;
1485}
1486
1487/** @interface_method_impl{VDIMAGEBACKEND,pfnClose} */
1488static DECLCALLBACK(int) vhdClose(void *pBackendData, bool fDelete)
1489{
1490 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1491 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1492
1493 int rc = vhdFreeImage(pImage, fDelete);
1494 RTMemFree(pImage);
1495
1496 LogFlowFunc(("returns %Rrc\n", rc));
1497 return rc;
1498}
1499
1500/** @interface_method_impl{VDIMAGEBACKEND,pfnRead} */
1501static DECLCALLBACK(int) vhdRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1502 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1503{
1504 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1505 int rc = VINF_SUCCESS;
1506
1507 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbToRead=%u pcbActuallyRead=%p\n",
1508 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1509
1510 AssertPtr(pImage);
1511 Assert(uOffset % 512 == 0);
1512 Assert(cbToRead % 512 == 0);
1513 AssertReturn((VALID_PTR(pIoCtx) && cbToRead), VERR_INVALID_PARAMETER);
1514 AssertReturn(uOffset + cbToRead <= pImage->cbSize, VERR_INVALID_PARAMETER);
1515
1516 /*
1517 * If we have a dynamic disk image, we need to find the data block and sector to read.
1518 */
1519 if (pImage->pBlockAllocationTable)
1520 {
1521 /*
1522 * Get the data block first.
1523 */
1524 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
1525 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
1526 uint64_t uVhdOffset;
1527
1528 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
1529 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
1530
1531 /*
1532 * Clip read range to remain in this data block.
1533 */
1534 cbToRead = RT_MIN(cbToRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1535
1536 /*
1537 * If the block is not allocated the content of the entry is ~0
1538 */
1539 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1540 rc = VERR_VD_BLOCK_FREE;
1541 else
1542 {
1543 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1544 LogFlowFunc(("uVhdOffset=%llu cbToRead=%u\n", uVhdOffset, cbToRead));
1545
1546 /* Read in the block's bitmap. */
1547 PVDMETAXFER pMetaXfer;
1548 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
1549 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1550 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1551 pIoCtx, &pMetaXfer, NULL, NULL);
1552
1553 if (RT_SUCCESS(rc))
1554 {
1555 uint32_t cSectors = 0;
1556
1557 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
1558 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1559 {
1560 cBATEntryIndex++;
1561 cSectors = 1;
1562
1563 /*
1564 * The first sector being read is marked dirty, read as much as we
1565 * can from child. Note that only sectors that are marked dirty
1566 * must be read from child.
1567 */
1568 while ( (cSectors < (cbToRead / VHD_SECTOR_SIZE))
1569 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1570 {
1571 cBATEntryIndex++;
1572 cSectors++;
1573 }
1574
1575 cbToRead = cSectors * VHD_SECTOR_SIZE;
1576
1577 LogFlowFunc(("uVhdOffset=%llu cbToRead=%u\n", uVhdOffset, cbToRead));
1578 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage,
1579 uVhdOffset, pIoCtx, cbToRead);
1580 }
1581 else
1582 {
1583 /*
1584 * The first sector being read is marked clean, so we should read from
1585 * our parent instead, but only as much as there are the following
1586 * clean sectors, because the block may still contain dirty sectors
1587 * further on. We just need to compute the number of clean sectors
1588 * and pass it to our caller along with the notification that they
1589 * should be read from the parent.
1590 */
1591 cBATEntryIndex++;
1592 cSectors = 1;
1593
1594 while ( (cSectors < (cbToRead / VHD_SECTOR_SIZE))
1595 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1596 {
1597 cBATEntryIndex++;
1598 cSectors++;
1599 }
1600
1601 cbToRead = cSectors * VHD_SECTOR_SIZE;
1602 LogFunc(("Sectors free: uVhdOffset=%llu cbToRead=%u\n", uVhdOffset, cbToRead));
1603 rc = VERR_VD_BLOCK_FREE;
1604 }
1605 }
1606 else
1607 AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
1608 }
1609 }
1610 else
1611 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset, pIoCtx, cbToRead);
1612
1613 if (pcbActuallyRead)
1614 *pcbActuallyRead = cbToRead;
1615
1616 LogFlowFunc(("returns rc=%Rrc\n", rc));
1617 return rc;
1618}
1619
1620/** @interface_method_impl{VDIMAGEBACKEND,pfnWrite} */
1621static DECLCALLBACK(int) vhdWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1622 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1623 size_t *pcbPostRead, unsigned fWrite)
1624{
1625 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1626 int rc = VINF_SUCCESS;
1627
1628 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbToWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1629 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
1630
1631 AssertPtr(pImage);
1632 Assert(!(uOffset % VHD_SECTOR_SIZE));
1633 Assert(!(cbToWrite % VHD_SECTOR_SIZE));
1634 AssertReturn((VALID_PTR(pIoCtx) && cbToWrite), VERR_INVALID_PARAMETER);
1635 AssertReturn(uOffset + cbToWrite <= pImage->cbSize, VERR_INVALID_PARAMETER);
1636
1637 if (pImage->pBlockAllocationTable)
1638 {
1639 /*
1640 * Get the data block first.
1641 */
1642 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
1643 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1644 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1645 uint64_t uVhdOffset;
1646
1647 /*
1648 * Clip write range.
1649 */
1650 cbToWrite = RT_MIN(cbToWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1651
1652 /*
1653 * If the block is not allocated the content of the entry is ~0
1654 * and we need to allocate a new block. Note that while blocks are
1655 * allocated with a relatively big granularity, each sector has its
1656 * own bitmap entry, indicating whether it has been written or not.
1657 * So that means for the purposes of the higher level that the
1658 * granularity is invisible. This means there's no need to return
1659 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
1660 */
1661 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1662 {
1663 /* Check if the block allocation should be suppressed. */
1664 if ( (fWrite & VD_WRITE_NO_ALLOC)
1665 || (cbToWrite != pImage->cbDataBlock))
1666 {
1667 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
1668 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbToWrite - *pcbPreRead;
1669
1670 if (pcbWriteProcess)
1671 *pcbWriteProcess = cbToWrite;
1672 return VERR_VD_BLOCK_FREE;
1673 }
1674
1675 PVHDIMAGEEXPAND pExpand;
1676 pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_UOFFSETOF_DYN(VHDIMAGEEXPAND,
1677 au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
1678 bool fIoInProgress = false;
1679
1680 if (!pExpand)
1681 return VERR_NO_MEMORY;
1682
1683 pExpand->cbEofOld = pImage->uCurrentEndOfFile;
1684 pExpand->idxBatAllocated = cBlockAllocationTableEntry;
1685 pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
1686
1687 /* Set the bits for all sectors having been written. */
1688 for (uint32_t iSector = 0; iSector < (cbToWrite / VHD_SECTOR_SIZE); iSector++)
1689 {
1690 /* No need to check for a changed value because this is an initial write. */
1691 vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
1692 cBATEntryIndex++;
1693 }
1694
1695 do
1696 {
1697 /*
1698 * Start with the sector bitmap.
1699 */
1700 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1701 pImage->uCurrentEndOfFile,
1702 pExpand->au8Bitmap,
1703 pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE, pIoCtx,
1704 vhdAsyncExpansionDataBlockBitmapComplete,
1705 pExpand);
1706 if (RT_SUCCESS(rc))
1707 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1708 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1709 fIoInProgress = true;
1710 else
1711 {
1712 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1713 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1714 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1715 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1716 break;
1717 }
1718
1719
1720 /*
1721 * Write the new block at the current end of the file.
1722 */
1723 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1724 pImage->uCurrentEndOfFile + (pImage->cDataBlockBitmapSectors + (cSector % pImage->cSectorsPerDataBlock)) * VHD_SECTOR_SIZE,
1725 pIoCtx, cbToWrite,
1726 vhdAsyncExpansionDataComplete,
1727 pExpand);
1728 if (RT_SUCCESS(rc))
1729 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1730 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1731 fIoInProgress = true;
1732 else
1733 {
1734 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1735 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1736 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1737 break;
1738 }
1739
1740 /*
1741 * Write entry in the BAT.
1742 */
1743 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1744 pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
1745 &pExpand->idxBlockBe, sizeof(uint32_t), pIoCtx,
1746 vhdAsyncExpansionBatUpdateComplete,
1747 pExpand);
1748 if (RT_SUCCESS(rc))
1749 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1750 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1751 fIoInProgress = true;
1752 else
1753 {
1754 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1755 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1756 break;
1757 }
1758
1759 /*
1760 * Set the new end of the file and link the new block into the BAT.
1761 */
1762 pImage->uCurrentEndOfFile += pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE + pImage->cbDataBlock;
1763
1764 /* Update the footer. */
1765 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1766 pImage->uCurrentEndOfFile,
1767 &pImage->vhdFooterCopy,
1768 sizeof(VHDFooter), pIoCtx,
1769 vhdAsyncExpansionFooterUpdateComplete,
1770 pExpand);
1771 if (RT_SUCCESS(rc))
1772 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
1773 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1774 fIoInProgress = true;
1775 else
1776 {
1777 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
1778 break;
1779 }
1780
1781 } while (0);
1782
1783 if (!fIoInProgress)
1784 vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
1785 else
1786 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1787 }
1788 else
1789 {
1790 /*
1791 * Calculate the real offset in the file.
1792 */
1793 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1794
1795 /* Read in the block's bitmap. */
1796 PVDMETAXFER pMetaXfer;
1797 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pImage->pStorage,
1798 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1799 pImage->pu8Bitmap,
1800 pImage->cbDataBlockBitmap, pIoCtx,
1801 &pMetaXfer, NULL, NULL);
1802 if (RT_SUCCESS(rc))
1803 {
1804 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
1805
1806 /* Write data. */
1807 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1808 uVhdOffset, pIoCtx, cbToWrite,
1809 NULL, NULL);
1810 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1811 {
1812 bool fChanged = false;
1813
1814 /* Set the bits for all sectors having been written. */
1815 for (uint32_t iSector = 0; iSector < (cbToWrite / VHD_SECTOR_SIZE); iSector++)
1816 {
1817 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
1818 cBATEntryIndex++;
1819 }
1820
1821 /* Only write the bitmap if it was changed. */
1822 if (fChanged)
1823 {
1824 /*
1825 * Write the bitmap back.
1826 *
1827 * @note We don't have a completion callback here because we
1828 * can't do anything if the write fails for some reason.
1829 * The error will propagated to the device/guest
1830 * by the generic VD layer already and we don't need
1831 * to rollback anything here.
1832 */
1833 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
1834 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1835 pImage->pu8Bitmap,
1836 pImage->cbDataBlockBitmap,
1837 pIoCtx, NULL, NULL);
1838 }
1839 }
1840 }
1841 }
1842 }
1843 else
1844 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
1845 uOffset, pIoCtx, cbToWrite, NULL, NULL);
1846
1847 if (pcbWriteProcess)
1848 *pcbWriteProcess = cbToWrite;
1849
1850 /* Stay on the safe side. Do not run the risk of confusing the higher
1851 * level, as that can be pretty lethal to image consistency. */
1852 *pcbPreRead = 0;
1853 *pcbPostRead = 0;
1854
1855 return rc;
1856}
1857
1858/** @interface_method_impl{VDIMAGEBACKEND,pfnFlush} */
1859static DECLCALLBACK(int) vhdFlush(void *pBackendData, PVDIOCTX pIoCtx)
1860{
1861 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1862 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1863
1864 /* No need to write anything here. Data is always updated on a write. */
1865 int rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
1866 LogFlowFunc(("returns %Rrc\n", rc));
1867 return rc;
1868}
1869
1870/** @interface_method_impl{VDIMAGEBACKEND,pfnGetVersion} */
1871static DECLCALLBACK(unsigned) vhdGetVersion(void *pBackendData)
1872{
1873 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1874 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1875
1876 AssertPtrReturn(pImage, 0);
1877
1878 unsigned uVersion = 1; /**< @todo use correct version */
1879
1880 LogFlowFunc(("returns %u\n", uVersion));
1881 return uVersion;
1882}
1883
1884/** @interface_method_impl{VDIMAGEBACKEND,pfnGetFileSize} */
1885static DECLCALLBACK(uint64_t) vhdGetFileSize(void *pBackendData)
1886{
1887 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1888 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1889 uint64_t cb = 0;
1890
1891 AssertPtrReturn(pImage, 0);
1892
1893 if (pImage->pStorage)
1894 cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
1895
1896 LogFlowFunc(("returns %lld\n", cb));
1897 return cb;
1898}
1899
1900/** @interface_method_impl{VDIMAGEBACKEND,pfnGetPCHSGeometry} */
1901static DECLCALLBACK(int) vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1902{
1903 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1904 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1905 int rc = VINF_SUCCESS;
1906
1907 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1908
1909 if (pImage->PCHSGeometry.cCylinders)
1910 *pPCHSGeometry = pImage->PCHSGeometry;
1911 else
1912 rc = VERR_VD_GEOMETRY_NOT_SET;
1913
1914 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders,
1915 pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
1916 return rc;
1917}
1918
1919/** @interface_method_impl{VDIMAGEBACKEND,pfnSetPCHSGeometry} */
1920static DECLCALLBACK(int) vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1921{
1922 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1923 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1924 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1925 int rc = VINF_SUCCESS;
1926
1927 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1928
1929 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1930 rc = VERR_VD_IMAGE_READ_ONLY;
1931 else
1932 pImage->PCHSGeometry = *pPCHSGeometry;
1933
1934 LogFlowFunc(("returns %Rrc\n", rc));
1935 return rc;
1936}
1937
1938/** @interface_method_impl{VDIMAGEBACKEND,pfnGetLCHSGeometry} */
1939static DECLCALLBACK(int) vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1940{
1941 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1942 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1943 int rc = VINF_SUCCESS;
1944
1945 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1946
1947 if (pImage->LCHSGeometry.cCylinders)
1948 *pLCHSGeometry = pImage->LCHSGeometry;
1949 else
1950 rc = VERR_VD_GEOMETRY_NOT_SET;
1951
1952 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders,
1953 pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
1954 return rc;
1955}
1956
1957/** @interface_method_impl{VDIMAGEBACKEND,pfnSetLCHSGeometry} */
1958static DECLCALLBACK(int) vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1959{
1960 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1961 int rc = VINF_SUCCESS;
1962
1963 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1964
1965 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1966 rc = VERR_VD_IMAGE_READ_ONLY;
1967 else
1968 pImage->LCHSGeometry = *pLCHSGeometry;
1969
1970 LogFlowFunc(("returns %Rrc\n", rc));
1971 return rc;
1972}
1973
1974/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
1975static DECLCALLBACK(int) vhdQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
1976{
1977 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
1978 PVHDIMAGE pThis = (PVHDIMAGE)pBackendData;
1979
1980 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
1981
1982 *ppRegionList = &pThis->RegionList;
1983 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
1984 return VINF_SUCCESS;
1985}
1986
1987/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
1988static DECLCALLBACK(void) vhdRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
1989{
1990 RT_NOREF1(pRegionList);
1991 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
1992 PVHDIMAGE pThis = (PVHDIMAGE)pBackendData;
1993 AssertPtr(pThis); RT_NOREF(pThis);
1994
1995 /* Nothing to do here. */
1996}
1997
1998/** @interface_method_impl{VDIMAGEBACKEND,pfnGetImageFlags} */
1999static DECLCALLBACK(unsigned) vhdGetImageFlags(void *pBackendData)
2000{
2001 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2002 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2003
2004 AssertPtrReturn(pImage, 0);
2005
2006 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
2007 return pImage->uImageFlags;
2008}
2009
2010/** @interface_method_impl{VDIMAGEBACKEND,pfnGetOpenFlags} */
2011static DECLCALLBACK(unsigned) vhdGetOpenFlags(void *pBackendData)
2012{
2013 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2014 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2015
2016 AssertPtrReturn(pImage, 0);
2017
2018 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
2019 return pImage->uOpenFlags;
2020}
2021
2022/** @interface_method_impl{VDIMAGEBACKEND,pfnSetOpenFlags} */
2023static DECLCALLBACK(int) vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2024{
2025 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2026 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2027 int rc = VINF_SUCCESS;
2028
2029 /* Image must be opened and the new flags must be valid. */
2030 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
2031 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
2032 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2033 rc = VERR_INVALID_PARAMETER;
2034 else
2035 {
2036 /* Implement this operation via reopening the image. */
2037 rc = vhdFreeImage(pImage, false);
2038 if (RT_SUCCESS(rc))
2039 rc = vhdOpenImage(pImage, uOpenFlags);
2040 }
2041
2042 LogFlowFunc(("returns %Rrc\n", rc));
2043 return rc;
2044}
2045
2046/** @interface_method_impl{VDIMAGEBACKEND,pfnGetComment} */
2047static DECLCALLBACK(int) vhdGetComment(void *pBackendData, char *pszComment,
2048 size_t cbComment)
2049{
2050 RT_NOREF2(pszComment, cbComment);
2051 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2052 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2053
2054 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2055
2056 LogFlowFunc(("returns %Rrc comment='%s'\n", VERR_NOT_SUPPORTED, pszComment));
2057 return VERR_NOT_SUPPORTED;
2058}
2059
2060/** @interface_method_impl{VDIMAGEBACKEND,pfnSetComment} */
2061static DECLCALLBACK(int) vhdSetComment(void *pBackendData, const char *pszComment)
2062{
2063 RT_NOREF1(pszComment);
2064 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2065 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2066
2067 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2068
2069 int rc;
2070 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2071 rc = VERR_VD_IMAGE_READ_ONLY;
2072 else
2073 rc = VERR_NOT_SUPPORTED;
2074
2075 LogFlowFunc(("returns %Rrc\n", rc));
2076 return rc;
2077}
2078
2079/** @interface_method_impl{VDIMAGEBACKEND,pfnGetUuid} */
2080static DECLCALLBACK(int) vhdGetUuid(void *pBackendData, PRTUUID pUuid)
2081{
2082 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2083 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2084
2085 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2086
2087 *pUuid = pImage->ImageUuid;
2088
2089 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2090 return VINF_SUCCESS;
2091}
2092
2093/** @interface_method_impl{VDIMAGEBACKEND,pfnSetUuid} */
2094static DECLCALLBACK(int) vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
2095{
2096 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2097 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2098 int rc = VINF_SUCCESS;
2099
2100 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2101
2102 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2103 {
2104 pImage->ImageUuid = *pUuid;
2105 /* Update the footer copy. It will get written to disk when the image is closed. */
2106 memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
2107 /* Update checksum. */
2108 pImage->vhdFooterCopy.Checksum = 0;
2109 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
2110
2111 /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
2112 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2113 pImage->fDynHdrNeedsUpdate = true;
2114 }
2115 else
2116 rc = VERR_VD_IMAGE_READ_ONLY;
2117
2118 LogFlowFunc(("returns %Rrc\n", rc));
2119 return rc;
2120}
2121
2122/** @interface_method_impl{VDIMAGEBACKEND,pfnGetModificationUuid} */
2123static DECLCALLBACK(int) vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2124{
2125 RT_NOREF1(pUuid);
2126 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2127 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2128
2129 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2130
2131 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
2132 return VERR_NOT_SUPPORTED;
2133}
2134
2135/** @interface_method_impl{VDIMAGEBACKEND,pfnSetModificationUuid} */
2136static DECLCALLBACK(int) vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2137{
2138 RT_NOREF1(pUuid);
2139 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2140 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2141
2142 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2143
2144 int rc;
2145 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2146 rc = VERR_VD_IMAGE_READ_ONLY;
2147 else
2148 rc = VERR_NOT_SUPPORTED;
2149
2150 LogFlowFunc(("returns %Rrc\n", rc));
2151 return rc;
2152}
2153
2154/** @interface_method_impl{VDIMAGEBACKEND,pfnGetParentUuid} */
2155static DECLCALLBACK(int) vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
2156{
2157 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2158 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2159
2160 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2161
2162 *pUuid = pImage->ParentUuid;
2163
2164 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
2165 return VINF_SUCCESS;
2166}
2167
2168/** @interface_method_impl{VDIMAGEBACKEND,pfnSetParentUuid} */
2169static DECLCALLBACK(int) vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2170{
2171 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2172 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2173 int rc = VINF_SUCCESS;
2174
2175 if (pImage && pImage->pStorage)
2176 {
2177 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2178 {
2179 pImage->ParentUuid = *pUuid;
2180 pImage->fDynHdrNeedsUpdate = true;
2181 }
2182 else
2183 rc = VERR_VD_IMAGE_READ_ONLY;
2184 }
2185 else
2186 rc = VERR_VD_NOT_OPENED;
2187
2188 LogFlowFunc(("returns %Rrc\n", rc));
2189 return rc;
2190}
2191
2192/** @interface_method_impl{VDIMAGEBACKEND,pfnGetParentModificationUuid} */
2193static DECLCALLBACK(int) vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2194{
2195 RT_NOREF1(pUuid);
2196 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2197 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2198
2199 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2200
2201 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
2202 return VERR_NOT_SUPPORTED;
2203}
2204
2205/** @interface_method_impl{VDIMAGEBACKEND,pfnSetParentModificationUuid} */
2206static DECLCALLBACK(int) vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2207{
2208 RT_NOREF1(pUuid);
2209 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2210 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2211
2212 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2213
2214 int rc;
2215 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2216 rc = VERR_VD_IMAGE_READ_ONLY;
2217 else
2218 rc = VERR_NOT_SUPPORTED;
2219
2220 LogFlowFunc(("returns %Rrc\n", rc));
2221 return rc;
2222}
2223
2224/** @interface_method_impl{VDIMAGEBACKEND,pfnDump} */
2225static DECLCALLBACK(void) vhdDump(void *pBackendData)
2226{
2227 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2228
2229 AssertPtrReturnVoid(pImage);
2230 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
2231 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2232 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2233 VHD_SECTOR_SIZE);
2234 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
2235 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
2236}
2237
2238/** @interface_method_impl{VDIMAGEBACKEND,pfnGetTimestamp} */
2239static DECLCALLBACK(int) vhdGetTimestamp(void *pBackendData, PRTTIMESPEC pTimestamp)
2240{
2241 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2242
2243 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2244
2245 int rc = vdIfIoIntFileGetModificationTime(pImage->pIfIo, pImage->pszFilename, pTimestamp);
2246
2247 LogFlowFunc(("returns %Rrc\n", rc));
2248 return rc;
2249}
2250
2251/** @interface_method_impl{VDIMAGEBACKEND,pfnGetParentTimestamp} */
2252static DECLCALLBACK(int) vhdGetParentTimestamp(void *pBackendData, PRTTIMESPEC pTimestamp)
2253{
2254 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2255
2256 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2257
2258 vhdTime2RtTime(pTimestamp, pImage->u32ParentTimestamp);
2259 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
2260 return VINF_SUCCESS;
2261}
2262
2263/** @interface_method_impl{VDIMAGEBACKEND,pfnSetParentTimestamp} */
2264static DECLCALLBACK(int) vhdSetParentTimestamp(void *pBackendData, PCRTTIMESPEC pTimestamp)
2265{
2266 int rc = VINF_SUCCESS;
2267 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2268
2269 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2270 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2271 rc = VERR_VD_IMAGE_READ_ONLY;
2272 else
2273 {
2274 pImage->u32ParentTimestamp = vhdRtTime2VhdTime(pTimestamp);
2275 pImage->fDynHdrNeedsUpdate = true;
2276 }
2277
2278 LogFlowFunc(("returns %Rrc\n", rc));
2279 return rc;
2280}
2281
2282/** @interface_method_impl{VDIMAGEBACKEND,pfnGetParentFilename} */
2283static DECLCALLBACK(int) vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
2284{
2285 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2286
2287 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2288 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
2289
2290 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
2291 return VINF_SUCCESS;
2292}
2293
2294/** @interface_method_impl{VDIMAGEBACKEND,pfnSetParentFilename} */
2295static DECLCALLBACK(int) vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
2296{
2297 int rc = VINF_SUCCESS;
2298 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2299
2300 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
2301
2302 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2303 rc = VERR_VD_IMAGE_READ_ONLY;
2304 else
2305 {
2306 if (pImage->pszParentFilename)
2307 RTStrFree(pImage->pszParentFilename);
2308 pImage->pszParentFilename = RTStrDup(pszParentFilename);
2309 if (!pImage->pszParentFilename)
2310 rc = VERR_NO_MEMORY;
2311 else
2312 pImage->fDynHdrNeedsUpdate = true;
2313 }
2314
2315 LogFlowFunc(("returns %Rrc\n", rc));
2316 return rc;
2317}
2318
2319/** @interface_method_impl{VDIMAGEBACKEND,pfnCompact} */
2320static DECLCALLBACK(int) vhdCompact(void *pBackendData, unsigned uPercentStart,
2321 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2322 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2323{
2324 RT_NOREF2(pVDIfsDisk, pVDIfsImage);
2325 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2326 int rc = VINF_SUCCESS;
2327 void *pvBuf = NULL;
2328 uint32_t *paBlocks = NULL;
2329 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2330
2331 DECLCALLBACKMEMBER(int, pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2332 void *pvParent = NULL;
2333 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2334 if (pIfParentState)
2335 {
2336 pfnParentRead = pIfParentState->pfnParentRead;
2337 pvParent = pIfParentState->Core.pvUser;
2338 }
2339
2340 do
2341 {
2342 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2343
2344 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2345 rc = VERR_VD_IMAGE_READ_ONLY);
2346
2347 /* Reject fixed images as they don't have a BAT. */
2348 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2349 {
2350 rc = VERR_NOT_SUPPORTED;
2351 break;
2352 }
2353
2354 if (pfnParentRead)
2355 {
2356 pvParent = RTMemTmpAlloc(pImage->cbDataBlock);
2357 AssertBreakStmt(pvParent, rc = VERR_NO_MEMORY);
2358 }
2359 pvBuf = RTMemTmpAlloc(pImage->cbDataBlock);
2360 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
2361
2362 unsigned cBlocksAllocated = 0;
2363 unsigned cBlocksToMove = 0;
2364 unsigned cBlocks = pImage->cBlockAllocationTableEntries;
2365 uint32_t offBlocksStart = ~0U; /* Start offset of data blocks in sectors. */
2366 uint32_t *paBat = pImage->pBlockAllocationTable;
2367
2368 /* Count the number of allocated blocks and find the start offset for the data blocks. */
2369 for (unsigned i = 0; i < cBlocks; i++)
2370 if (paBat[i] != ~0U)
2371 {
2372 cBlocksAllocated++;
2373 if (paBat[i] < offBlocksStart)
2374 offBlocksStart = paBat[i];
2375 }
2376
2377 if (!cBlocksAllocated)
2378 {
2379 /* Nothing to do. */
2380 rc = VINF_SUCCESS;
2381 break;
2382 }
2383
2384 paBlocks = (uint32_t *)RTMemTmpAllocZ(cBlocksAllocated * sizeof(uint32_t));
2385 AssertBreakStmt(paBlocks, rc = VERR_NO_MEMORY);
2386
2387 /* Invalidate the back resolving array. */
2388 for (unsigned i = 0; i < cBlocksAllocated; i++)
2389 paBlocks[i] = ~0U;
2390
2391 /* Fill the back resolving table. */
2392 for (unsigned i = 0; i < cBlocks; i++)
2393 if (paBat[i] != ~0U)
2394 {
2395 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2396 if ( idxBlock < cBlocksAllocated
2397 && paBlocks[idxBlock] == ~0U)
2398 paBlocks[idxBlock] = i;
2399 else
2400 {
2401 /* The image is in an inconsistent state. Don't go further. */
2402 rc = VERR_INVALID_STATE;
2403 break;
2404 }
2405 }
2406
2407 if (RT_FAILURE(rc))
2408 break;
2409
2410 /* Find redundant information and update the block pointers
2411 * accordingly, creating bubbles. Keep disk up to date, as this
2412 * enables cancelling. */
2413 for (unsigned i = 0; i < cBlocks; i++)
2414 {
2415 if (paBat[i] != ~0U)
2416 {
2417 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2418
2419 /* Block present in image file, read relevant data. */
2420 uint64_t u64Offset = ((uint64_t)paBat[i] + pImage->cDataBlockBitmapSectors) * VHD_SECTOR_SIZE;
2421 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2422 u64Offset, pvBuf, pImage->cbDataBlock);
2423 if (RT_FAILURE(rc))
2424 break;
2425
2426 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)pImage->cbDataBlock * 8) == -1)
2427 {
2428 paBat[i] = UINT32_MAX;
2429 paBlocks[idxBlock] = ~0U;
2430 /* Adjust progress info, one block to be relocated. */
2431 cBlocksToMove++;
2432 }
2433 else if (pfnParentRead)
2434 {
2435 rc = pfnParentRead(pvParent, (uint64_t)i * pImage->cbDataBlock, pvParent, pImage->cbDataBlock);
2436 if (RT_FAILURE(rc))
2437 break;
2438 if (!memcmp(pvParent, pvBuf, pImage->cbDataBlock))
2439 {
2440 paBat[i] = ~0U;
2441 paBlocks[idxBlock] = ~0U;
2442 /* Adjust progress info, one block to be relocated. */
2443 cBlocksToMove++;
2444 }
2445 }
2446 }
2447
2448 vdIfProgress(pIfProgress, (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2449 }
2450
2451 if (RT_SUCCESS(rc))
2452 {
2453 /* Fill bubbles with other data (if available). */
2454 unsigned cBlocksMoved = 0;
2455 unsigned uBlockUsedPos = cBlocksAllocated;
2456 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of whole block containing the bitmap and the user data. */
2457
2458 /* Allocate data buffer to hold the data block and allocation bitmap in front of the actual data. */
2459 RTMemTmpFree(pvBuf);
2460 pvBuf = RTMemTmpAllocZ(cbBlock);
2461 AssertBreakStmt(pvBuf, rc = VERR_NO_MEMORY);
2462
2463 for (unsigned i = 0; i < cBlocksAllocated; i++)
2464 {
2465 unsigned uBlock = paBlocks[i];
2466 if (uBlock == ~0U)
2467 {
2468 unsigned uBlockData = ~0U;
2469 while (uBlockUsedPos > i && uBlockData == ~0U)
2470 {
2471 uBlockUsedPos--;
2472 uBlockData = paBlocks[uBlockUsedPos];
2473 }
2474 /* Terminate early if there is no block which needs copying. */
2475 if (uBlockUsedPos == i)
2476 break;
2477 uint64_t u64Offset = (uint64_t)uBlockUsedPos * cbBlock
2478 + (offBlocksStart * VHD_SECTOR_SIZE);
2479 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2480 u64Offset, pvBuf, cbBlock);
2481 if (RT_FAILURE(rc))
2482 break;
2483
2484 u64Offset = (uint64_t)i * cbBlock
2485 + (offBlocksStart * VHD_SECTOR_SIZE);
2486 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2487 u64Offset, pvBuf, cbBlock);
2488 if (RT_FAILURE(rc))
2489 break;
2490
2491 paBat[uBlockData] = i*(pImage->cSectorsPerDataBlock + pImage->cDataBlockBitmapSectors) + offBlocksStart;
2492
2493 /* Truncate the file but leave enough room for the footer to avoid
2494 * races if other processes fill the whole harddisk. */
2495 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2496 pImage->uCurrentEndOfFile - cbBlock + VHD_SECTOR_SIZE);
2497 if (RT_FAILURE(rc))
2498 break;
2499
2500 /* Update pointers and write footer. */
2501 pImage->uCurrentEndOfFile -= cbBlock;
2502
2503 /* We're kinda screwed if this failes. */
2504 rc = vhdUpdateFooter(pImage);
2505 if (RT_FAILURE(rc))
2506 break;
2507
2508 paBlocks[i] = uBlockData;
2509 paBlocks[uBlockUsedPos] = ~0U;
2510 cBlocksMoved++;
2511 }
2512
2513 rc = vdIfProgress(pIfProgress, (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2514 }
2515 }
2516
2517 /* Write the new BAT in any case. */
2518 rc = vhdFlushImage(pImage);
2519 } while (0);
2520
2521 if (paBlocks)
2522 RTMemTmpFree(paBlocks);
2523 if (pvParent)
2524 RTMemTmpFree(pvParent);
2525 if (pvBuf)
2526 RTMemTmpFree(pvBuf);
2527
2528 if (RT_SUCCESS(rc))
2529 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
2530
2531 LogFlowFunc(("returns %Rrc\n", rc));
2532 return rc;
2533}
2534
2535/** @interface_method_impl{VDIMAGEBACKEND,pfnResize} */
2536static DECLCALLBACK(int) vhdResize(void *pBackendData, uint64_t cbSize,
2537 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2538 unsigned uPercentStart, unsigned uPercentSpan,
2539 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2540 PVDINTERFACE pVDIfsOperation)
2541{
2542 RT_NOREF5(uPercentSpan, uPercentStart, pVDIfsDisk, pVDIfsImage, pVDIfsOperation);
2543 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2544 int rc = VINF_SUCCESS;
2545
2546 /* Making the image smaller is not supported at the moment. */
2547 if (cbSize < pImage->cbSize)
2548 rc = VERR_VD_SHRINK_NOT_SUPPORTED;
2549 else if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2550 rc = VERR_NOT_SUPPORTED;
2551 else if (cbSize > pImage->cbSize)
2552 {
2553 unsigned cBlocksAllocated = 0;
2554 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of a block including the sector bitmap. */
2555 uint32_t cBlocksNew = cbSize / pImage->cbDataBlock; /** < New number of blocks in the image after the resize */
2556 if (cbSize % pImage->cbDataBlock)
2557 cBlocksNew++;
2558
2559 uint32_t cBlocksOld = pImage->cBlockAllocationTableEntries; /** < Number of blocks before the resize. */
2560 uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE); /** < Required space for the block array after the resize. */
2561 uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
2562 uint64_t offStartDataOld = ~0ULL;
2563
2564 /* Go through the BAT and find the data start offset. */
2565 for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
2566 {
2567 if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
2568 {
2569 uint64_t offStartBlock = (uint64_t)pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
2570 if (offStartBlock < offStartDataOld)
2571 offStartDataOld = offStartBlock;
2572 cBlocksAllocated++;
2573 }
2574 }
2575
2576 if ( offStartDataOld != offStartDataNew
2577 && cBlocksAllocated > 0)
2578 {
2579 /* Calculate how many sectors nee to be relocated. */
2580 uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
2581 unsigned cBlocksReloc = (unsigned)(cbOverlapping / cbBlock);
2582 if (cbOverlapping % cbBlock)
2583 cBlocksReloc++;
2584
2585 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2586 offStartDataNew = offStartDataOld;
2587
2588 /* Do the relocation. */
2589 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2590
2591 /*
2592 * Get the blocks we need to relocate first, they are appended to the end
2593 * of the image.
2594 */
2595 void *pvBuf = NULL, *pvZero = NULL;
2596 do
2597 {
2598 /* Allocate data buffer. */
2599 pvBuf = RTMemAllocZ(cbBlock);
2600 if (!pvBuf)
2601 {
2602 rc = VERR_NO_MEMORY;
2603 break;
2604 }
2605
2606 /* Allocate buffer for overwriting with zeroes. */
2607 pvZero = RTMemAllocZ(cbBlock);
2608 if (!pvZero)
2609 {
2610 rc = VERR_NO_MEMORY;
2611 break;
2612 }
2613
2614 for (unsigned i = 0; i < cBlocksReloc; i++)
2615 {
2616 uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
2617
2618 /* Search the index in the block table. */
2619 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2620 {
2621 if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
2622 {
2623 /* Read data and append to the end of the image. */
2624 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2625 offStartDataNew, pvBuf, cbBlock);
2626 if (RT_FAILURE(rc))
2627 break;
2628
2629 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2630 pImage->uCurrentEndOfFile, pvBuf, cbBlock);
2631 if (RT_FAILURE(rc))
2632 break;
2633
2634 /* Zero out the old block area. */
2635 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2636 offStartDataNew, pvZero, cbBlock);
2637 if (RT_FAILURE(rc))
2638 break;
2639
2640 /* Update block counter. */
2641 pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2642
2643 pImage->uCurrentEndOfFile += cbBlock;
2644
2645 /* Continue with the next block. */
2646 break;
2647 }
2648 }
2649
2650 if (RT_FAILURE(rc))
2651 break;
2652
2653 offStartDataNew += cbBlock;
2654 }
2655 } while (0);
2656
2657 if (pvBuf)
2658 RTMemFree(pvBuf);
2659 if (pvZero)
2660 RTMemFree(pvZero);
2661 }
2662
2663 /*
2664 * Relocation done, expand the block array and update the header with
2665 * the new data.
2666 */
2667 if (RT_SUCCESS(rc))
2668 {
2669 uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
2670 if (paBlocksNew)
2671 {
2672 pImage->pBlockAllocationTable = paBlocksNew;
2673
2674 /* Mark the new blocks as unallocated. */
2675 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2676 pImage->pBlockAllocationTable[idxBlock] = ~0U;
2677 }
2678 else
2679 rc = VERR_NO_MEMORY;
2680
2681 if (RT_SUCCESS(rc))
2682 {
2683 /* Write the block array before updating the rest. */
2684 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2685 pImage->uBlockAllocationTableOffset,
2686 pImage->pBlockAllocationTable,
2687 cBlocksNew * sizeof(uint32_t));
2688 }
2689
2690 if (RT_SUCCESS(rc))
2691 {
2692 /* Update size and new block count. */
2693 pImage->cBlockAllocationTableEntries = cBlocksNew;
2694 pImage->cbSize = cbSize;
2695
2696 /* Update geometry. */
2697 pImage->PCHSGeometry = *pPCHSGeometry;
2698 pImage->LCHSGeometry = *pLCHSGeometry;
2699 }
2700 }
2701
2702 /* Update header information in base image file. */
2703 pImage->fDynHdrNeedsUpdate = true;
2704 vhdFlushImage(pImage);
2705 }
2706 /* Same size doesn't change the image at all. */
2707
2708 LogFlowFunc(("returns %Rrc\n", rc));
2709 return rc;
2710}
2711
2712/** @interface_method_impl{VDIMAGEBACKEND,pfnRepair} */
2713static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
2714 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
2715{
2716 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
2717 int rc;
2718 PVDINTERFACEERROR pIfError;
2719 PVDINTERFACEIOINT pIfIo;
2720 PVDIOSTORAGE pStorage;
2721 uint64_t cbFile;
2722 VHDFooter vhdFooter;
2723 VHDDynamicDiskHeader dynamicDiskHeader;
2724 uint32_t *paBat = NULL;
2725 uint32_t *pu32BlockBitmap = NULL;
2726
2727 pIfIo = VDIfIoIntGet(pVDIfsImage);
2728 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
2729
2730 pIfError = VDIfErrorGet(pVDIfsDisk);
2731
2732 do
2733 {
2734 uint64_t offDynamicDiskHeader = 0;
2735 uint64_t offBat = 0;
2736 uint64_t offFooter = 0;
2737 uint32_t cBatEntries = 0;
2738 bool fDynamic = false;
2739 bool fRepairFooter = false;
2740 bool fRepairBat = false;
2741 bool fRepairDynHeader = false;
2742
2743 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
2744 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
2745 ? VD_OPEN_FLAGS_READONLY
2746 : 0,
2747 false /* fCreate */),
2748 &pStorage);
2749 if (RT_FAILURE(rc))
2750 {
2751 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to open image \"%s\"", pszFilename);
2752 break;
2753 }
2754
2755 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
2756 if (RT_FAILURE(rc))
2757 {
2758 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to query image size");
2759 break;
2760 }
2761
2762 if (cbFile < sizeof(VHDFooter))
2763 {
2764 rc = vdIfError(pIfError, VERR_VD_INVALID_SIZE, RT_SRC_POS,
2765 "Image must be at least %u bytes (got %llu)",
2766 sizeof(VHDFooter), cbFile);
2767 break;
2768 }
2769
2770 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
2771 &vhdFooter, sizeof(VHDFooter));
2772 if (RT_FAILURE(rc))
2773 {
2774 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read footer of image");
2775 break;
2776 }
2777
2778 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
2779 {
2780 /* Dynamic images have a backup at the beginning of the image. */
2781 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
2782 &vhdFooter, sizeof(VHDFooter));
2783 if (RT_FAILURE(rc))
2784 {
2785 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read header of image");
2786 break;
2787 }
2788
2789 /*
2790 * Check for the header, if this fails the image is either completely corrupted
2791 * and impossible to repair or in another format.
2792 */
2793 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
2794 {
2795 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2796 "No valid VHD structures found");
2797 break;
2798 }
2799 else
2800 vdIfErrorMessage(pIfError, "Missing footer structure, using backup\n");
2801
2802 /* Remember to fix the footer structure. */
2803 fRepairFooter = true;
2804 }
2805
2806 offFooter = cbFile - sizeof(VHDFooter);
2807
2808 /* Verify that checksums match. */
2809 uint32_t u32ChkSumOld = RT_BE2H_U32(vhdFooter.Checksum);
2810 vhdFooter.Checksum = 0;
2811 uint32_t u32ChkSum = vhdChecksum(&vhdFooter, sizeof(VHDFooter));
2812
2813 vhdFooter.Checksum = RT_H2BE_U32(u32ChkSum);
2814
2815 if (u32ChkSumOld != u32ChkSum)
2816 {
2817 vdIfErrorMessage(pIfError, "Checksum is invalid (should be %u got %u), repairing\n",
2818 u32ChkSum, u32ChkSumOld);
2819 fRepairFooter = true;
2820 break;
2821 }
2822
2823 switch (RT_BE2H_U32(vhdFooter.DiskType))
2824 {
2825 case VHD_FOOTER_DISK_TYPE_FIXED:
2826 fDynamic = false;
2827 break;
2828 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
2829 fDynamic = true;
2830 break;
2831 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
2832 fDynamic = true;
2833 break;
2834 default:
2835 {
2836 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2837 "VHD image type %u is not supported",
2838 RT_BE2H_U32(vhdFooter.DiskType));
2839 break;
2840 }
2841 }
2842
2843 /* Load and check dynamic disk header if required. */
2844 if (fDynamic)
2845 {
2846 size_t cbBlock;
2847
2848 offDynamicDiskHeader = RT_BE2H_U64(vhdFooter.DataOffset);
2849 if (offDynamicDiskHeader + sizeof(VHDDynamicDiskHeader) > cbFile)
2850 {
2851 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2852 "VHD image type is not supported");
2853 break;
2854 }
2855
2856 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offDynamicDiskHeader,
2857 &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
2858 if (RT_FAILURE(rc))
2859 {
2860 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2861 "Failed to read dynamic disk header (at %llu), %Rrc",
2862 offDynamicDiskHeader, rc);
2863 break;
2864 }
2865
2866 /* Verify that checksums match. */
2867 u32ChkSumOld = RT_BE2H_U32(dynamicDiskHeader.Checksum);
2868 dynamicDiskHeader.Checksum = 0;
2869 u32ChkSum = vhdChecksum(&dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
2870
2871 dynamicDiskHeader.Checksum = RT_H2BE_U32(u32ChkSum);
2872
2873 if (u32ChkSumOld != u32ChkSum)
2874 {
2875 vdIfErrorMessage(pIfError, "Checksum of dynamic disk header is invalid (should be %u got %u), repairing\n",
2876 u32ChkSum, u32ChkSumOld);
2877 fRepairDynHeader = true;
2878 break;
2879 }
2880
2881 /* Read the block allocation table and fix any inconsistencies. */
2882 offBat = RT_BE2H_U64(dynamicDiskHeader.TableOffset);
2883 cBatEntries = RT_BE2H_U32(dynamicDiskHeader.MaxTableEntries);
2884 cbBlock = RT_BE2H_U32(dynamicDiskHeader.BlockSize);
2885 cbBlock += cbBlock / VHD_SECTOR_SIZE / 8;
2886
2887 if (offBat + cBatEntries * sizeof(uint32_t) > cbFile)
2888 {
2889 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2890 "Block allocation table is not inside the image");
2891 break;
2892 }
2893
2894 paBat = (uint32_t *)RTMemAllocZ(cBatEntries * sizeof(uint32_t));
2895 if (!paBat)
2896 {
2897 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2898 "Could not allocate memory for the block allocation table (%u bytes)",
2899 cBatEntries * sizeof(uint32_t));
2900 break;
2901 }
2902
2903 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offBat, paBat,
2904 cBatEntries * sizeof(uint32_t));
2905 if (RT_FAILURE(rc))
2906 {
2907 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2908 "Could not read block allocation table (at %llu), %Rrc",
2909 offBat, rc);
2910 break;
2911 }
2912
2913 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(cBatEntries / 8, 4));
2914 if (!pu32BlockBitmap)
2915 {
2916 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
2917 "Failed to allocate memory for block bitmap");
2918 break;
2919 }
2920
2921 uint32_t idxMinBlock = UINT32_C(0xffffffff);
2922 for (uint32_t i = 0; i < cBatEntries; i++)
2923 {
2924 paBat[i] = RT_BE2H_U32(paBat[i]);
2925 if (paBat[i] < idxMinBlock)
2926 idxMinBlock = paBat[i];
2927 }
2928
2929 vdIfErrorMessage(pIfError, "First data block at sector %u\n", idxMinBlock);
2930
2931 for (uint32_t i = 0; i < cBatEntries; i++)
2932 {
2933 if (paBat[i] != UINT32_C(0xffffffff))
2934 {
2935 uint64_t offBlock =(uint64_t)paBat[i] * VHD_SECTOR_SIZE;
2936
2937 /*
2938 * Check that the offsets are valid (inside of the image) and
2939 * that there are no double references.
2940 */
2941 if (offBlock + cbBlock > cbFile)
2942 {
2943 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
2944 i, offBlock);
2945 paBat[i] = UINT32_C(0xffffffff);
2946 fRepairBat = true;
2947 }
2948 else if (offBlock + cbBlock > offFooter)
2949 {
2950 vdIfErrorMessage(pIfError, "Entry %u intersects with footer, aligning footer\n",
2951 i);
2952 offFooter = offBlock + cbBlock;
2953 fRepairBat = true;
2954 }
2955
2956 if ( paBat[i] != UINT32_C(0xffffffff)
2957 && ASMBitTestAndSet(pu32BlockBitmap, (uint32_t)((paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE))))
2958 {
2959 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
2960 i);
2961 paBat[i] = UINT32_C(0xffffffff);
2962 fRepairBat = true;
2963 }
2964 }
2965 }
2966 }
2967
2968 /* Write repaired structures now. */
2969 if (!(fRepairBat || fRepairDynHeader || fRepairFooter))
2970 vdIfErrorMessage(pIfError, "VHD image is in a consistent state, no repair required\n");
2971 else if (!(fFlags & VD_REPAIR_DRY_RUN))
2972 {
2973 if (fRepairBat)
2974 {
2975 for (uint32_t i = 0; i < cBatEntries; i++)
2976 paBat[i] = RT_H2BE_U32(paBat[i]);
2977
2978 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
2979
2980 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offBat, paBat,
2981 cBatEntries * sizeof(uint32_t));
2982 if (RT_FAILURE(rc))
2983 {
2984 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
2985 "Could not write repaired block allocation table (at %llu), %Rrc",
2986 offBat, rc);
2987 break;
2988 }
2989 }
2990
2991 if (fRepairDynHeader)
2992 {
2993 Assert(fDynamic);
2994
2995 vdIfErrorMessage(pIfError, "Writing repaired dynamic disk header...\n");
2996 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offDynamicDiskHeader, &dynamicDiskHeader,
2997 sizeof(VHDDynamicDiskHeader));
2998 if (RT_FAILURE(rc))
2999 {
3000 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3001 "Could not write repaired dynamic disk header (at %llu), %Rrc",
3002 offDynamicDiskHeader, rc);
3003 break;
3004 }
3005 }
3006
3007 if (fRepairFooter)
3008 {
3009 vdIfErrorMessage(pIfError, "Writing repaired Footer...\n");
3010
3011 if (fDynamic)
3012 {
3013 /* Write backup at image beginning. */
3014 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, 0, &vhdFooter,
3015 sizeof(VHDFooter));
3016 if (RT_FAILURE(rc))
3017 {
3018 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3019 "Could not write repaired backup footer (at %llu), %Rrc",
3020 0, rc);
3021 break;
3022 }
3023 }
3024
3025 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offFooter, &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 footer (at %llu), %Rrc",
3031 cbFile - sizeof(VHDFooter), rc);
3032 break;
3033 }
3034 }
3035
3036 vdIfErrorMessage(pIfError, "Corrupted VHD image repaired successfully\n");
3037 }
3038 } while(0);
3039
3040 if (paBat)
3041 RTMemFree(paBat);
3042
3043 if (pu32BlockBitmap)
3044 RTMemFree(pu32BlockBitmap);
3045
3046 if (pStorage)
3047 {
3048 int rc2 = vdIfIoIntFileClose(pIfIo, pStorage);
3049 if (RT_SUCCESS(rc))
3050 rc = rc2; /* Propagate status code only when repairing the image was successful. */
3051 }
3052
3053 LogFlowFunc(("returns %Rrc\n", rc));
3054 return rc;
3055}
3056
3057
3058const VDIMAGEBACKEND g_VhdBackend =
3059{
3060 /* u32Version */
3061 VD_IMGBACKEND_VERSION,
3062 /* pszBackendName */
3063 "VHD",
3064 /* uBackendCaps */
3065 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
3066 VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
3067 VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_PREFERRED,
3068 /* paFileExtensions */
3069 s_aVhdFileExtensions,
3070 /* paConfigInfo */
3071 NULL,
3072 /* pfnProbe */
3073 vhdProbe,
3074 /* pfnOpen */
3075 vhdOpen,
3076 /* pfnCreate */
3077 vhdCreate,
3078 /* pfnRename */
3079 vhdRename,
3080 /* pfnClose */
3081 vhdClose,
3082 /* pfnRead */
3083 vhdRead,
3084 /* pfnWrite */
3085 vhdWrite,
3086 /* pfnFlush */
3087 vhdFlush,
3088 /* pfnDiscard */
3089 NULL,
3090 /* pfnGetVersion */
3091 vhdGetVersion,
3092 /* pfnGetFileSize */
3093 vhdGetFileSize,
3094 /* pfnGetPCHSGeometry */
3095 vhdGetPCHSGeometry,
3096 /* pfnSetPCHSGeometry */
3097 vhdSetPCHSGeometry,
3098 /* pfnGetLCHSGeometry */
3099 vhdGetLCHSGeometry,
3100 /* pfnSetLCHSGeometry */
3101 vhdSetLCHSGeometry,
3102 /* pfnQueryRegions */
3103 vhdQueryRegions,
3104 /* pfnRegionListRelease */
3105 vhdRegionListRelease,
3106 /* pfnGetImageFlags */
3107 vhdGetImageFlags,
3108 /* pfnGetOpenFlags */
3109 vhdGetOpenFlags,
3110 /* pfnSetOpenFlags */
3111 vhdSetOpenFlags,
3112 /* pfnGetComment */
3113 vhdGetComment,
3114 /* pfnSetComment */
3115 vhdSetComment,
3116 /* pfnGetUuid */
3117 vhdGetUuid,
3118 /* pfnSetUuid */
3119 vhdSetUuid,
3120 /* pfnGetModificationUuid */
3121 vhdGetModificationUuid,
3122 /* pfnSetModificationUuid */
3123 vhdSetModificationUuid,
3124 /* pfnGetParentUuid */
3125 vhdGetParentUuid,
3126 /* pfnSetParentUuid */
3127 vhdSetParentUuid,
3128 /* pfnGetParentModificationUuid */
3129 vhdGetParentModificationUuid,
3130 /* pfnSetParentModificationUuid */
3131 vhdSetParentModificationUuid,
3132 /* pfnDump */
3133 vhdDump,
3134 /* pfnGetTimestamp */
3135 vhdGetTimestamp,
3136 /* pfnGetParentTimestamp */
3137 vhdGetParentTimestamp,
3138 /* pfnSetParentTimestamp */
3139 vhdSetParentTimestamp,
3140 /* pfnGetParentFilename */
3141 vhdGetParentFilename,
3142 /* pfnSetParentFilename */
3143 vhdSetParentFilename,
3144 /* pfnComposeLocation */
3145 genericFileComposeLocation,
3146 /* pfnComposeName */
3147 genericFileComposeName,
3148 /* pfnCompact */
3149 vhdCompact,
3150 /* pfnResize */
3151 vhdResize,
3152 /* pfnRepair */
3153 vhdRepair,
3154 /* pfnTraverseMetadata */
3155 NULL,
3156 /* u32VersionEnd */
3157 VD_IMGBACKEND_VERSION
3158};
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