VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use