VirtualBox

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

Last change on this file since 57358 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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