VirtualBox

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

Last change on this file since 44233 was 44233, checked in by vboxsync, 12 years ago

Storage: Preparations for the sync/async I/O unification

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