VirtualBox

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

Last change on this file since 106212 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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