VirtualBox

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

Last change on this file since 51770 was 51091, checked in by vboxsync, 11 years ago

Storage/VHD: Don't clear parent locator entries if there is no parent filename, just leave it as is

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