VirtualBox

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

Last change on this file since 76531 was 76408, checked in by vboxsync, 6 years ago

iprt/string.h: Dropped including utf16.h and let those who need it include it themselves. bugref:9344

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