VirtualBox

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

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

vhdLocatorUpdate: TODO: unused variable

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