VirtualBox

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

Last change on this file since 63265 was 62794, checked in by vboxsync, 8 years ago

Storage/VHD: Fix todo about uninitialized variable

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette