VirtualBox

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

Last change on this file since 82781 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

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

© 2023 Oracle
ContactPrivacy policyTerms of Use