VirtualBox

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

Last change on this file since 63942 was 63905, checked in by vboxsync, 8 years ago

Storage/VD: Add proper versioning of the backend structures instead of just relying on the structure size to make changing callback signatures possible in the future and still being able to reject incompatible plugins

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