VirtualBox

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

Last change on this file since 40739 was 40240, checked in by vboxsync, 13 years ago

Storage: Implement repair callback for VDI, fixes for the VHD repair callback and add repair command to vbox-img

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