VirtualBox

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

Last change on this file since 55180 was 54430, checked in by vboxsync, 10 years ago

Storage/VD: make use of the image type (hdd/dvd/floppy) for sanity checking when creating disk images

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