VirtualBox

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

Last change on this file since 74900 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette