VirtualBox

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

Last change on this file since 68787 was 68787, checked in by vboxsync, 7 years ago

Storage/VDI+VHD+VMDK: Preserve the error code (instead of ignoring) when deleting the image failed. It's confusing to report success but still have the file in place.

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