VirtualBox

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

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

Storage: Fix regression introduced when adding VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS resulting in non working online merging of snapshots

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 125.8 KB
Line 
1/* $Id: VHD.cpp 44232 2013-01-04 14:30:20Z 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 (RT_SUCCESS(rc))
796 {
797 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
798 {
799 /*
800 * There is also a backup header at the beginning in case the image got corrupted.
801 * Such corrupted images are detected here to let the open handler repair it later.
802 */
803 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
804 &vhdFooter, sizeof(VHDFooter), NULL);
805 if (RT_SUCCESS(rc))
806 {
807 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
808 rc = VERR_VD_VHD_INVALID_HEADER;
809 else
810 rc = VERR_VD_IMAGE_CORRUPTED;
811 }
812 }
813 }
814
815 if (RT_FAILURE(rc))
816 return rc;
817
818 switch (RT_BE2H_U32(vhdFooter.DiskType))
819 {
820 case VHD_FOOTER_DISK_TYPE_FIXED:
821 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
822 break;
823 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
824 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
825 break;
826 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
827 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
828 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
829 break;
830 default:
831 return VERR_NOT_IMPLEMENTED;
832 }
833
834 pImage->cbSize = RT_BE2H_U64(vhdFooter.CurSize);
835 pImage->LCHSGeometry.cCylinders = 0;
836 pImage->LCHSGeometry.cHeads = 0;
837 pImage->LCHSGeometry.cSectors = 0;
838 pImage->PCHSGeometry.cCylinders = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
839 pImage->PCHSGeometry.cHeads = vhdFooter.DiskGeometryHeads;
840 pImage->PCHSGeometry.cSectors = vhdFooter.DiskGeometrySectors;
841
842 /*
843 * Copy of the disk footer.
844 * If we allocate new blocks in differencing disks on write access
845 * the footer is overwritten. We need to write it at the end of the file.
846 */
847 memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
848
849 /*
850 * Is there a better way?
851 */
852 memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
853
854 pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
855 LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
856
857 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
858 rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
859
860 if (RT_FAILURE(rc))
861 vhdFreeImage(pImage, false);
862 return rc;
863}
864
865/**
866 * Internal: Checks if a sector in the block bitmap is set
867 */
868DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, 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 = pImage->pu8Bitmap + iBitmap;
878
879 AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
880 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
881
882 return ASMBitTest(puBitmap, iBitInByte);
883}
884
885/**
886 * Internal: Sets the given sector in the sector bitmap.
887 */
888DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
889{
890 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
891
892 /*
893 * The index of the bit in the byte of the data block bitmap.
894 * The most significant bit stands for a lower sector number.
895 */
896 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
897 uint8_t *puBitmap = pu8Bitmap + iBitmap;
898
899 AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
900 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
901
902 return !ASMBitTestAndSet(puBitmap, iBitInByte);
903}
904
905/**
906 * Internal: Derive drive geometry from its size.
907 */
908static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
909{
910 uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
911 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
912
913 if (u64TotalSectors > 65535 * 16 * 255)
914 {
915 /* ATA disks limited to 127 GB. */
916 u64TotalSectors = 65535 * 16 * 255;
917 }
918
919 if (u64TotalSectors >= 65535 * 16 * 63)
920 {
921 u32SectorsPerTrack = 255;
922 u32Heads = 16;
923 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
924 }
925 else
926 {
927 u32SectorsPerTrack = 17;
928 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
929
930 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
931
932 if (u32Heads < 4)
933 {
934 u32Heads = 4;
935 }
936 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
937 {
938 u32SectorsPerTrack = 31;
939 u32Heads = 16;
940 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
941 }
942 if (u32CylinderTimesHeads >= (u32Heads * 1024))
943 {
944 u32SectorsPerTrack = 63;
945 u32Heads = 16;
946 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
947 }
948 }
949 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
950 pImage->PCHSGeometry.cHeads = u32Heads;
951 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
952 pImage->LCHSGeometry.cCylinders = 0;
953 pImage->LCHSGeometry.cHeads = 0;
954 pImage->LCHSGeometry.cSectors = 0;
955}
956
957
958static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
959{
960 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
961 /* Relative Windows path. */
962 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
963 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
964 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
965 u64Offset += VHD_RELATIVE_MAX_PATH;
966 pLocator++;
967 /* Absolute Windows path. */
968 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
969 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
970 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
971 u64Offset += VHD_ABSOLUTE_MAX_PATH;
972 pLocator++;
973 /* Unicode relative Windows path. */
974 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
975 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
976 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
977 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
978 pLocator++;
979 /* Unicode absolute Windows path. */
980 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
981 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
982 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
983 return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
984}
985
986/**
987 * Internal: Additional code for dynamic VHD image creation.
988 */
989static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
990{
991 int rc;
992 VHDDynamicDiskHeader DynamicDiskHeader;
993 uint32_t u32BlockAllocationTableSectors;
994 void *pvTmp = NULL;
995
996 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
997
998 pImage->u64DataOffset = sizeof(VHDFooter);
999 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1000 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1001 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1002 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1003 /* Align to sector boundary */
1004 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
1005 pImage->cDataBlockBitmapSectors++;
1006 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
1007 if (!pImage->pu8Bitmap)
1008 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1009
1010 /* Initialize BAT. */
1011 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1012 pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
1013 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1014 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1015 if (!pImage->pBlockAllocationTable)
1016 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1017
1018 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1019 {
1020 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1021 }
1022
1023 /* Round up to the sector size. */
1024 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
1025 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1026 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1027 else
1028 pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
1029
1030 /* Set dynamic image size. */
1031 pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1032 if (!pvTmp)
1033 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1034
1035 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, pvTmp,
1036 pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
1037 if (RT_FAILURE(rc))
1038 {
1039 RTMemTmpFree(pvTmp);
1040 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1041 }
1042
1043 RTMemTmpFree(pvTmp);
1044
1045 /* Initialize and write the dynamic disk header. */
1046 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1047 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1048 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1049 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1050 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1051 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1052 /* Compute and update checksum. */
1053 DynamicDiskHeader.Checksum = 0;
1054 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1055
1056 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(VHDFooter),
1057 &DynamicDiskHeader, sizeof(DynamicDiskHeader), NULL);
1058 if (RT_FAILURE(rc))
1059 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1060
1061 /* Write BAT. */
1062 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uBlockAllocationTableOffset,
1063 pImage->pBlockAllocationTable,
1064 pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
1065 NULL);
1066 if (RT_FAILURE(rc))
1067 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1068
1069 return rc;
1070}
1071
1072/**
1073 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1074 */
1075static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
1076 unsigned uImageFlags, const char *pszComment,
1077 PCVDGEOMETRY pPCHSGeometry,
1078 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1079 unsigned uOpenFlags,
1080 PFNVDPROGRESS pfnProgress, void *pvUser,
1081 unsigned uPercentStart, unsigned uPercentSpan)
1082{
1083 int rc;
1084 VHDFooter Footer;
1085 RTTIMESPEC now;
1086
1087 pImage->uOpenFlags = uOpenFlags;
1088 pImage->uImageFlags = uImageFlags;
1089
1090 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1091
1092 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1093 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
1094 true /* fCreate */),
1095 &pImage->pStorage);
1096 if (RT_FAILURE(rc))
1097 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1098
1099
1100 pImage->cbSize = cbSize;
1101 pImage->ImageUuid = *pUuid;
1102 RTUuidClear(&pImage->ParentUuid);
1103 vhdSetDiskGeometry(pImage, cbSize);
1104
1105 /* Initialize the footer. */
1106 memset(&Footer, 0, sizeof(Footer));
1107 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1108 Footer.Features = RT_H2BE_U32(0x2);
1109 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1110 Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1111 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1112 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1113#ifdef RT_OS_DARWIN
1114 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1115#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1116 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1117#endif
1118 Footer.OrigSize = RT_H2BE_U64(cbSize);
1119 Footer.CurSize = Footer.OrigSize;
1120 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1121 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1122 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1123 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1124 Footer.SavedState = 0;
1125
1126 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1127 {
1128 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1129 /*
1130 * Initialize fixed image.
1131 * "The size of the entire file is the size of the hard disk in
1132 * the guest operating system plus the size of the footer."
1133 */
1134 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1135 pImage->uCurrentEndOfFile = cbSize;
1136 /** @todo r=klaus replace this with actual data writes, see the experience
1137 * with VDI files on Windows, can cause long freezes when writing. */
1138 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
1139 pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1140 if (RT_FAILURE(rc))
1141 {
1142 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1143 goto out;
1144 }
1145 }
1146 else
1147 {
1148 /*
1149 * Initialize dynamic image.
1150 *
1151 * The overall structure of dynamic disk is:
1152 *
1153 * [Copy of hard disk footer (512 bytes)]
1154 * [Dynamic disk header (1024 bytes)]
1155 * [BAT (Block Allocation Table)]
1156 * [Parent Locators]
1157 * [Data block 1]
1158 * [Data block 2]
1159 * ...
1160 * [Data block N]
1161 * [Hard disk footer (512 bytes)]
1162 */
1163 Footer.DiskType = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
1164 ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
1165 : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1166 /* We are half way thorough with creation of image, let the caller know. */
1167 if (pfnProgress)
1168 pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
1169
1170 rc = vhdCreateDynamicImage(pImage, cbSize);
1171 if (RT_FAILURE(rc))
1172 goto out;
1173 }
1174
1175 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1176
1177 /* Compute and update the footer checksum. */
1178 Footer.Checksum = 0;
1179 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1180
1181 pImage->vhdFooterCopy = Footer;
1182
1183 /* Store the footer */
1184 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
1185 &Footer, sizeof(Footer), NULL);
1186 if (RT_FAILURE(rc))
1187 {
1188 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1189 goto out;
1190 }
1191
1192 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1193 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1194 {
1195 /* Write the copy of the footer. */
1196 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Footer, sizeof(Footer), NULL);
1197 if (RT_FAILURE(rc))
1198 {
1199 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1200 goto out;
1201 }
1202 }
1203
1204out:
1205 if (RT_SUCCESS(rc) && pfnProgress)
1206 pfnProgress(pvUser, uPercentStart + uPercentSpan);
1207
1208 if (RT_FAILURE(rc))
1209 vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1210 return rc;
1211}
1212
1213
1214/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1215static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1216 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1217{
1218 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1219 int rc;
1220 PVDIOSTORAGE pStorage;
1221 uint64_t cbFile;
1222 VHDFooter vhdFooter;
1223
1224 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1225 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1226
1227 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1228 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1229 false /* fCreate */),
1230 &pStorage);
1231 if (RT_FAILURE(rc))
1232 goto out;
1233
1234 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1235 if (RT_FAILURE(rc))
1236 {
1237 vdIfIoIntFileClose(pIfIo, pStorage);
1238 rc = VERR_VD_VHD_INVALID_HEADER;
1239 goto out;
1240 }
1241
1242 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
1243 &vhdFooter, sizeof(VHDFooter), NULL);
1244 if (RT_SUCCESS(rc))
1245 {
1246 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
1247 {
1248 /*
1249 * There is also a backup header at the beginning in case the image got corrupted.
1250 * Such corrupted images are detected here to let the open handler repair it later.
1251 */
1252 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
1253 &vhdFooter, sizeof(VHDFooter), NULL);
1254 if ( RT_FAILURE(rc)
1255 || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
1256 rc = VERR_VD_VHD_INVALID_HEADER;
1257 }
1258
1259 if (RT_SUCCESS(rc))
1260 *penmType = VDTYPE_HDD;
1261 }
1262 else
1263 rc = VERR_VD_VHD_INVALID_HEADER;
1264
1265 vdIfIoIntFileClose(pIfIo, pStorage);
1266
1267out:
1268 LogFlowFunc(("returns %Rrc\n", rc));
1269 return rc;
1270}
1271
1272/** @copydoc VBOXHDDBACKEND::pfnOpen */
1273static int vhdOpen(const char *pszFilename, unsigned uOpenFlags,
1274 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1275 VDTYPE enmType, void **ppBackendData)
1276{
1277 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
1278 int rc = VINF_SUCCESS;
1279 PVHDIMAGE pImage;
1280
1281 /* Check open flags. All valid flags are supported. */
1282 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1283 {
1284 rc = VERR_INVALID_PARAMETER;
1285 goto out;
1286 }
1287
1288 /* Check remaining arguments. */
1289 if ( !VALID_PTR(pszFilename)
1290 || !*pszFilename)
1291 {
1292 rc = VERR_INVALID_PARAMETER;
1293 goto out;
1294 }
1295
1296 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1297 if (!pImage)
1298 {
1299 rc = VERR_NO_MEMORY;
1300 goto out;
1301 }
1302
1303 pImage->pszFilename = pszFilename;
1304 pImage->pStorage = NULL;
1305 pImage->pVDIfsDisk = pVDIfsDisk;
1306 pImage->pVDIfsImage = pVDIfsImage;
1307
1308 rc = vhdOpenImage(pImage, uOpenFlags);
1309 if (RT_SUCCESS(rc))
1310 *ppBackendData = pImage;
1311 else
1312 RTMemFree(pImage);
1313
1314out:
1315 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1316 return rc;
1317}
1318
1319/** @copydoc VBOXHDDBACKEND::pfnCreate */
1320static int vhdCreate(const char *pszFilename, uint64_t cbSize,
1321 unsigned uImageFlags, const char *pszComment,
1322 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1323 PCRTUUID pUuid, unsigned uOpenFlags,
1324 unsigned uPercentStart, unsigned uPercentSpan,
1325 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1326 PVDINTERFACE pVDIfsOperation, void **ppBackendData)
1327{
1328 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));
1329 int rc = VINF_SUCCESS;
1330 PVHDIMAGE pImage;
1331
1332 PFNVDPROGRESS pfnProgress = NULL;
1333 void *pvUser = NULL;
1334 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
1335 if (pIfProgress)
1336 {
1337 pfnProgress = pIfProgress->pfnProgress;
1338 pvUser = pIfProgress->Core.pvUser;
1339 }
1340
1341 /* Check open flags. All valid flags are supported. */
1342 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1343 {
1344 rc = VERR_INVALID_PARAMETER;
1345 return rc;
1346 }
1347
1348 /* @todo Check the values of other params */
1349
1350 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1351 if (!pImage)
1352 {
1353 rc = VERR_NO_MEMORY;
1354 return rc;
1355 }
1356 pImage->pszFilename = pszFilename;
1357 pImage->pStorage = NULL;
1358 pImage->pVDIfsDisk = pVDIfsDisk;
1359 pImage->pVDIfsImage = pVDIfsImage;
1360
1361 /* Get I/O interface. */
1362 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1363 if (RT_UNLIKELY(!VALID_PTR(pImage->pIfIo)))
1364 {
1365 RTMemFree(pImage);
1366 return VERR_INVALID_PARAMETER;
1367 }
1368
1369 rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
1370 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1371 pfnProgress, pvUser, uPercentStart, uPercentSpan);
1372
1373 if (RT_SUCCESS(rc))
1374 {
1375 /* So far the image is opened in read/write mode. Make sure the
1376 * image is opened in read-only mode if the caller requested that. */
1377 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1378 {
1379 vhdFreeImage(pImage, false);
1380 rc = vhdOpenImage(pImage, uOpenFlags);
1381 if (RT_FAILURE(rc))
1382 {
1383 RTMemFree(pImage);
1384 goto out;
1385 }
1386 }
1387 *ppBackendData = pImage;
1388 }
1389 else
1390 RTMemFree(pImage);
1391
1392out:
1393 LogFlowFunc(("returns %Rrc\n", rc));
1394 return rc;
1395}
1396
1397/** @copydoc VBOXHDDBACKEND::pfnRename */
1398static int vhdRename(void *pBackendData, const char *pszFilename)
1399{
1400 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1401 int rc = VINF_SUCCESS;
1402 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1403
1404 /* Check arguments. */
1405 if ( !pImage
1406 || !pszFilename
1407 || !*pszFilename)
1408 {
1409 rc = VERR_INVALID_PARAMETER;
1410 goto out;
1411 }
1412
1413 /* Close the image. */
1414 rc = vhdFreeImage(pImage, false);
1415 if (RT_FAILURE(rc))
1416 goto out;
1417
1418 /* Rename the file. */
1419 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1420 if (RT_FAILURE(rc))
1421 {
1422 /* The move failed, try to reopen the original image. */
1423 int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
1424 if (RT_FAILURE(rc2))
1425 rc = rc2;
1426
1427 goto out;
1428 }
1429
1430 /* Update pImage with the new information. */
1431 pImage->pszFilename = pszFilename;
1432
1433 /* Open the old file with new name. */
1434 rc = vhdOpenImage(pImage, pImage->uOpenFlags);
1435 if (RT_FAILURE(rc))
1436 goto out;
1437
1438out:
1439 LogFlowFunc(("returns %Rrc\n", rc));
1440 return rc;
1441}
1442
1443/** @copydoc VBOXHDDBACKEND::pfnClose */
1444static int vhdClose(void *pBackendData, bool fDelete)
1445{
1446 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1447 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1448 int rc;
1449
1450 rc = vhdFreeImage(pImage, fDelete);
1451 RTMemFree(pImage);
1452
1453 LogFlowFunc(("returns %Rrc\n", rc));
1454 return rc;
1455}
1456
1457/** @copydoc VBOXHDDBACKEND::pfnRead */
1458static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
1459 size_t cbBuf, size_t *pcbActuallyRead)
1460{
1461 LogFlowFunc(("pBackendData=%p uOffset=%#llu pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
1462 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1463 int rc = VINF_SUCCESS;
1464
1465 if (uOffset + cbBuf > pImage->cbSize)
1466 {
1467 rc = VERR_INVALID_PARAMETER;
1468 goto out;
1469 }
1470
1471 /*
1472 * If we have a dynamic disk image, we need to find the data block and sector to read.
1473 */
1474 if (pImage->pBlockAllocationTable)
1475 {
1476 /*
1477 * Get the data block first.
1478 */
1479 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
1480 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
1481 uint64_t uVhdOffset;
1482
1483 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
1484 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
1485
1486 /*
1487 * Clip read range to remain in this data block.
1488 */
1489 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1490
1491 /*
1492 * If the block is not allocated the content of the entry is ~0
1493 */
1494 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1495 rc = VERR_VD_BLOCK_FREE;
1496 else
1497 {
1498 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1499 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1500
1501 /* Read in the block's bitmap. */
1502 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1503 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1504 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1505 NULL);
1506 if (RT_SUCCESS(rc))
1507 {
1508 uint32_t cSectors = 0;
1509
1510 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1511 {
1512 cBATEntryIndex++;
1513 cSectors = 1;
1514
1515 /*
1516 * The first sector being read is marked dirty, read as much as we
1517 * can from child. Note that only sectors that are marked dirty
1518 * must be read from child.
1519 */
1520 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1521 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1522 {
1523 cBATEntryIndex++;
1524 cSectors++;
1525 }
1526
1527 cbBuf = cSectors * VHD_SECTOR_SIZE;
1528
1529 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1530 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1531 uVhdOffset, pvBuf, cbBuf, NULL);
1532 }
1533 else
1534 {
1535 /*
1536 * The first sector being read is marked clean, so we should read from
1537 * our parent instead, but only as much as there are the following
1538 * clean sectors, because the block may still contain dirty sectors
1539 * further on. We just need to compute the number of clean sectors
1540 * and pass it to our caller along with the notification that they
1541 * should be read from the parent.
1542 */
1543 cBATEntryIndex++;
1544 cSectors = 1;
1545
1546 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1547 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1548 {
1549 cBATEntryIndex++;
1550 cSectors++;
1551 }
1552
1553 cbBuf = cSectors * VHD_SECTOR_SIZE;
1554 LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1555 rc = VERR_VD_BLOCK_FREE;
1556 }
1557 }
1558 else
1559 AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
1560 }
1561 }
1562 else
1563 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbBuf, NULL);
1564
1565 if ( RT_SUCCESS(rc)
1566 || rc == VERR_VD_BLOCK_FREE)
1567 {
1568 if (pcbActuallyRead)
1569 *pcbActuallyRead = cbBuf;
1570
1571 Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n", uOffset, pvBuf, cbBuf));
1572 }
1573
1574out:
1575 LogFlowFunc(("returns %Rrc\n", rc));
1576 return rc;
1577}
1578
1579/** @copydoc VBOXHDDBACKEND::pfnWrite */
1580static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
1581 size_t cbBuf, size_t *pcbWriteProcess,
1582 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
1583{
1584 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
1585 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1586 int rc = VINF_SUCCESS;
1587
1588 LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1589 pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
1590
1591 AssertPtr(pImage);
1592 Assert(uOffset % VHD_SECTOR_SIZE == 0);
1593 Assert(cbBuf % VHD_SECTOR_SIZE == 0);
1594
1595 if (pImage->pBlockAllocationTable)
1596 {
1597 /*
1598 * Get the data block first.
1599 */
1600 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
1601 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1602 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1603 uint64_t uVhdOffset;
1604
1605 /*
1606 * Clip write range.
1607 */
1608 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1609
1610 /*
1611 * If the block is not allocated the content of the entry is ~0
1612 * and we need to allocate a new block. Note that while blocks are
1613 * allocated with a relatively big granularity, each sector has its
1614 * own bitmap entry, indicating whether it has been written or not.
1615 * So that means for the purposes of the higher level that the
1616 * granularity is invisible. This means there's no need to return
1617 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
1618 */
1619 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1620 {
1621 /* Check if the block allocation should be suppressed. */
1622 if (fWrite & VD_WRITE_NO_ALLOC)
1623 {
1624 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
1625 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
1626
1627 if (pcbWriteProcess)
1628 *pcbWriteProcess = cbBuf;
1629 rc = VERR_VD_BLOCK_FREE;
1630 goto out;
1631 }
1632
1633 size_t cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
1634 uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
1635
1636 if (!pNewBlock)
1637 {
1638 rc = VERR_NO_MEMORY;
1639 goto out;
1640 }
1641
1642 /*
1643 * Write the new block at the current end of the file.
1644 */
1645 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, pImage->uCurrentEndOfFile,
1646 pNewBlock, cbNewBlock, NULL);
1647 AssertRC(rc);
1648
1649 /*
1650 * Set the new end of the file and link the new block into the BAT.
1651 */
1652 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
1653 pImage->uCurrentEndOfFile += cbNewBlock;
1654 RTMemFree(pNewBlock);
1655
1656 /* Write the updated BAT and the footer to remain in a consistent state. */
1657 rc = vhdFlushImage(pImage);
1658 AssertRC(rc);
1659 }
1660
1661 /*
1662 * Calculate the real offset in the file.
1663 */
1664 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1665
1666 /* Write data. */
1667 vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uVhdOffset,
1668 pvBuf, cbBuf, NULL);
1669
1670 /* Read in the block's bitmap. */
1671 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
1672 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1673 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1674 NULL);
1675 if (RT_SUCCESS(rc))
1676 {
1677 bool fChanged = false;
1678
1679 /* Set the bits for all sectors having been written. */
1680 for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
1681 {
1682 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
1683 cBATEntryIndex++;
1684 }
1685
1686 if (fChanged)
1687 {
1688 /* Write the bitmap back. */
1689 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
1690 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1691 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1692 NULL);
1693 }
1694 }
1695 }
1696 else
1697 {
1698 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbBuf, NULL);
1699 }
1700
1701 if (pcbWriteProcess)
1702 *pcbWriteProcess = cbBuf;
1703
1704 /* Stay on the safe side. Do not run the risk of confusing the higher
1705 * level, as that can be pretty lethal to image consistency. */
1706 *pcbPreRead = 0;
1707 *pcbPostRead = 0;
1708
1709out:
1710 LogFlowFunc(("returns %Rrc\n", rc));
1711 return rc;
1712}
1713
1714/** @copydoc VBOXHDDBACKEND::pfnFlush */
1715static int vhdFlush(void *pBackendData)
1716{
1717 LogFlowFunc(("pBackendData=%#p", pBackendData));
1718 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1719 int rc;
1720
1721 rc = vhdFlushImage(pImage);
1722 LogFlowFunc(("returns %Rrc\n", rc));
1723 return rc;
1724}
1725
1726/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1727static unsigned vhdGetVersion(void *pBackendData)
1728{
1729 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1730 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1731 unsigned ver = 0;
1732
1733 AssertPtr(pImage);
1734
1735 if (pImage)
1736 ver = 1; /**< @todo use correct version */
1737
1738 LogFlowFunc(("returns %u\n", ver));
1739 return ver;
1740}
1741
1742/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1743static uint64_t vhdGetSize(void *pBackendData)
1744{
1745 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1746 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1747 uint64_t cb = 0;
1748
1749 AssertPtr(pImage);
1750
1751 if (pImage && pImage->pStorage)
1752 cb = pImage->cbSize;
1753
1754 LogFlowFunc(("returns %llu\n", cb));
1755 return cb;
1756}
1757
1758/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1759static uint64_t vhdGetFileSize(void *pBackendData)
1760{
1761 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1762 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1763 uint64_t cb = 0;
1764
1765 AssertPtr(pImage);
1766
1767 if (pImage && pImage->pStorage)
1768 cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
1769
1770 LogFlowFunc(("returns %lld\n", cb));
1771 return cb;
1772}
1773
1774/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1775static int vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1776{
1777 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1778 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1779 int rc;
1780
1781 AssertPtr(pImage);
1782
1783 if (pImage)
1784 {
1785 if (pImage->PCHSGeometry.cCylinders)
1786 {
1787 *pPCHSGeometry = pImage->PCHSGeometry;
1788 rc = VINF_SUCCESS;
1789 }
1790 else
1791 rc = VERR_VD_GEOMETRY_NOT_SET;
1792 }
1793 else
1794 rc = VERR_VD_NOT_OPENED;
1795
1796 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
1797 return rc;
1798}
1799
1800/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1801static int vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1802{
1803 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1804 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1805 int rc;
1806
1807 AssertPtr(pImage);
1808
1809 if (pImage)
1810 {
1811 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1812 {
1813 rc = VERR_VD_IMAGE_READ_ONLY;
1814 goto out;
1815 }
1816
1817 pImage->PCHSGeometry = *pPCHSGeometry;
1818 rc = VINF_SUCCESS;
1819 }
1820 else
1821 rc = VERR_VD_NOT_OPENED;
1822
1823out:
1824 LogFlowFunc(("returns %Rrc\n", rc));
1825 return rc;
1826}
1827
1828/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1829static int vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
1830{
1831LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1832 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1833 int rc;
1834
1835 AssertPtr(pImage);
1836
1837 if (pImage)
1838 {
1839 if (pImage->LCHSGeometry.cCylinders)
1840 {
1841 *pLCHSGeometry = pImage->LCHSGeometry;
1842 rc = VINF_SUCCESS;
1843 }
1844 else
1845 rc = VERR_VD_GEOMETRY_NOT_SET;
1846 }
1847 else
1848 rc = VERR_VD_NOT_OPENED;
1849
1850 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
1851 return rc;
1852}
1853
1854/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1855static int vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
1856{
1857 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1858 int rc;
1859
1860 AssertPtr(pImage);
1861
1862 if (pImage)
1863 {
1864 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1865 {
1866 rc = VERR_VD_IMAGE_READ_ONLY;
1867 goto out;
1868 }
1869
1870 pImage->LCHSGeometry = *pLCHSGeometry;
1871 rc = VINF_SUCCESS;
1872 }
1873 else
1874 rc = VERR_VD_NOT_OPENED;
1875
1876out:
1877 LogFlowFunc(("returns %Rrc\n", rc));
1878 return rc;
1879}
1880
1881/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1882static unsigned vhdGetImageFlags(void *pBackendData)
1883{
1884 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1885 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1886 unsigned uImageFlags;
1887
1888 AssertPtr(pImage);
1889
1890 if (pImage)
1891 uImageFlags = pImage->uImageFlags;
1892 else
1893 uImageFlags = 0;
1894
1895 LogFlowFunc(("returns %#x\n", uImageFlags));
1896 return uImageFlags;
1897}
1898
1899/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1900static unsigned vhdGetOpenFlags(void *pBackendData)
1901{
1902 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1903 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1904 unsigned uOpenFlags;
1905
1906 AssertPtr(pImage);
1907
1908 if (pImage)
1909 uOpenFlags = pImage->uOpenFlags;
1910 else
1911 uOpenFlags = 0;
1912
1913 LogFlowFunc(("returns %#x\n", uOpenFlags));
1914 return uOpenFlags;
1915}
1916
1917/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1918static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1919{
1920 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1921 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1922 int rc;
1923
1924 /* Image must be opened and the new flags must be valid. */
1925 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
1926 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
1927 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
1928 {
1929 rc = VERR_INVALID_PARAMETER;
1930 goto out;
1931 }
1932
1933 /* Implement this operation via reopening the image. */
1934 rc = vhdFreeImage(pImage, false);
1935 if (RT_FAILURE(rc))
1936 goto out;
1937 rc = vhdOpenImage(pImage, uOpenFlags);
1938
1939out:
1940 LogFlowFunc(("returns %Rrc\n", rc));
1941 return rc;
1942}
1943
1944/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1945static int vhdGetComment(void *pBackendData, char *pszComment,
1946 size_t cbComment)
1947{
1948 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1949 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1950 int rc;
1951
1952 AssertPtr(pImage);
1953
1954 if (pImage)
1955 rc = VERR_NOT_SUPPORTED;
1956 else
1957 rc = VERR_VD_NOT_OPENED;
1958
1959 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
1960 return rc;
1961}
1962
1963/** @copydoc VBOXHDDBACKEND::pfnSetComment */
1964static int vhdSetComment(void *pBackendData, const char *pszComment)
1965{
1966 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1967 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1968 int rc;
1969
1970 AssertPtr(pImage);
1971
1972 if (pImage)
1973 {
1974 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1975 rc = VERR_VD_IMAGE_READ_ONLY;
1976 else
1977 rc = VERR_NOT_SUPPORTED;
1978 }
1979 else
1980 rc = VERR_VD_NOT_OPENED;
1981
1982 LogFlowFunc(("returns %Rrc\n", rc));
1983 return rc;
1984}
1985
1986/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
1987static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
1988{
1989 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1990 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1991 int rc;
1992
1993 AssertPtr(pImage);
1994
1995 if (pImage)
1996 {
1997 *pUuid = pImage->ImageUuid;
1998 rc = VINF_SUCCESS;
1999 }
2000 else
2001 rc = VERR_VD_NOT_OPENED;
2002
2003 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2004 return rc;
2005}
2006
2007/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2008static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
2009{
2010 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2011 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2012 int rc;
2013
2014 AssertPtr(pImage);
2015
2016 if (pImage)
2017 {
2018 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2019 {
2020 pImage->ImageUuid = *pUuid;
2021 /* Update the footer copy. It will get written to disk when the image is closed. */
2022 memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
2023 /* Update checksum. */
2024 pImage->vhdFooterCopy.Checksum = 0;
2025 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
2026
2027 /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
2028 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2029 pImage->fDynHdrNeedsUpdate = true;
2030 rc = VINF_SUCCESS;
2031 }
2032 else
2033 rc = VERR_VD_IMAGE_READ_ONLY;
2034 }
2035 else
2036 rc = VERR_VD_NOT_OPENED;
2037
2038 LogFlowFunc(("returns %Rrc\n", rc));
2039 return rc;
2040}
2041
2042/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2043static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2044{
2045 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2046 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2047 int rc;
2048
2049 AssertPtr(pImage);
2050
2051 if (pImage)
2052 rc = VERR_NOT_SUPPORTED;
2053 else
2054 rc = VERR_VD_NOT_OPENED;
2055
2056 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2057 return rc;
2058}
2059
2060/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2061static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2062{
2063 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2064 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2065 int rc;
2066
2067 AssertPtr(pImage);
2068
2069 if (pImage)
2070 {
2071 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2072 rc = VERR_NOT_SUPPORTED;
2073 else
2074 rc = VERR_VD_IMAGE_READ_ONLY;
2075 }
2076 else
2077 rc = VERR_VD_NOT_OPENED;
2078
2079 LogFlowFunc(("returns %Rrc\n", rc));
2080 return rc;
2081}
2082
2083/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2084static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
2085{
2086 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2087 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2088 int rc;
2089
2090 AssertPtr(pImage);
2091
2092 if (pImage)
2093 {
2094 *pUuid = pImage->ParentUuid;
2095 rc = VINF_SUCCESS;
2096 }
2097 else
2098 rc = VERR_VD_NOT_OPENED;
2099
2100 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2101 return rc;
2102}
2103
2104/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2105static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2106{
2107 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2108 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2109 int rc = VINF_SUCCESS;
2110
2111 AssertPtr(pImage);
2112
2113 if (pImage && pImage->pStorage)
2114 {
2115 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2116 {
2117 pImage->ParentUuid = *pUuid;
2118 pImage->fDynHdrNeedsUpdate = true;
2119 }
2120 else
2121 rc = VERR_VD_IMAGE_READ_ONLY;
2122 }
2123 else
2124 rc = VERR_VD_NOT_OPENED;
2125
2126 LogFlowFunc(("returns %Rrc\n", rc));
2127 return rc;
2128}
2129
2130/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2131static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2132{
2133 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2134 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2135 int rc;
2136
2137 AssertPtr(pImage);
2138
2139 if (pImage)
2140 rc = VERR_NOT_SUPPORTED;
2141 else
2142 rc = VERR_VD_NOT_OPENED;
2143
2144 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2145 return rc;
2146}
2147
2148/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2149static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2150{
2151 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2152 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2153 int rc;
2154
2155 AssertPtr(pImage);
2156
2157 if (pImage)
2158 {
2159 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2160 rc = VERR_NOT_SUPPORTED;
2161 else
2162 rc = VERR_VD_IMAGE_READ_ONLY;
2163 }
2164 else
2165 rc = VERR_VD_NOT_OPENED;
2166
2167 LogFlowFunc(("returns %Rrc\n", rc));
2168 return rc;
2169}
2170
2171/** @copydoc VBOXHDDBACKEND::pfnDump */
2172static void vhdDump(void *pBackendData)
2173{
2174 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2175
2176 AssertPtr(pImage);
2177 if (pImage)
2178 {
2179 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
2180 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2181 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2182 VHD_SECTOR_SIZE);
2183 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
2184 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
2185 }
2186}
2187
2188/** @copydoc VBOXHDDBACKEND::pfnGetTimestamp */
2189static int vhdGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2190{
2191 int rc = VINF_SUCCESS;
2192 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2193
2194 AssertPtr(pImage);
2195
2196 if (pImage)
2197 rc = vdIfIoIntFileGetModificationTime(pImage->pIfIo, pImage->pszFilename, pTimeStamp);
2198 else
2199 rc = VERR_VD_NOT_OPENED;
2200
2201 LogFlowFunc(("returns %Rrc\n", rc));
2202 return rc;
2203}
2204
2205/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
2206static int vhdGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2207{
2208 int rc = VINF_SUCCESS;
2209 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2210
2211 AssertPtr(pImage);
2212
2213 if (pImage)
2214 vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
2215 else
2216 rc = VERR_VD_NOT_OPENED;
2217
2218 LogFlowFunc(("returns %Rrc\n", rc));
2219 return rc;
2220}
2221
2222/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
2223static int vhdSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
2224{
2225 int rc = VINF_SUCCESS;
2226 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2227
2228 AssertPtr(pImage);
2229 if (pImage)
2230 {
2231 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2232 rc = VERR_VD_IMAGE_READ_ONLY;
2233 else
2234 {
2235 pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
2236 pImage->fDynHdrNeedsUpdate = true;
2237 }
2238 }
2239 else
2240 rc = VERR_VD_NOT_OPENED;
2241
2242 LogFlowFunc(("returns %Rrc\n", rc));
2243 return rc;
2244}
2245
2246/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
2247static int vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
2248{
2249 int rc = VINF_SUCCESS;
2250 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2251
2252 AssertPtr(pImage);
2253 if (pImage)
2254 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
2255 else
2256 rc = VERR_VD_NOT_OPENED;
2257
2258 LogFlowFunc(("returns %Rrc\n", rc));
2259 return rc;
2260}
2261
2262/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
2263static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
2264{
2265 int rc = VINF_SUCCESS;
2266 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2267
2268 AssertPtr(pImage);
2269 if (pImage)
2270 {
2271 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2272 rc = VERR_VD_IMAGE_READ_ONLY;
2273 else
2274 {
2275 if (pImage->pszParentFilename)
2276 RTStrFree(pImage->pszParentFilename);
2277 pImage->pszParentFilename = RTStrDup(pszParentFilename);
2278 if (!pImage->pszParentFilename)
2279 rc = VERR_NO_MEMORY;
2280 else
2281 pImage->fDynHdrNeedsUpdate = true;
2282 }
2283 }
2284 else
2285 rc = VERR_VD_NOT_OPENED;
2286
2287 LogFlowFunc(("returns %Rrc\n", rc));
2288 return rc;
2289}
2290
2291/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
2292static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
2293 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
2294{
2295 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2296 int rc = VINF_SUCCESS;
2297
2298 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
2299
2300 if (uOffset + cbRead > pImage->cbSize)
2301 return VERR_INVALID_PARAMETER;
2302
2303 /*
2304 * If we have a dynamic disk image, we need to find the data block and sector to read.
2305 */
2306 if (pImage->pBlockAllocationTable)
2307 {
2308 /*
2309 * Get the data block first.
2310 */
2311 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
2312 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
2313 uint64_t uVhdOffset;
2314
2315 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
2316 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
2317
2318 /*
2319 * Clip read range to remain in this data block.
2320 */
2321 cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2322
2323 /*
2324 * If the block is not allocated the content of the entry is ~0
2325 */
2326 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2327 rc = VERR_VD_BLOCK_FREE;
2328 else
2329 {
2330 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2331 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2332
2333 /* Read in the block's bitmap. */
2334 PVDMETAXFER pMetaXfer;
2335 rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
2336 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2337 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
2338 pIoCtx, &pMetaXfer, NULL, NULL);
2339
2340 if (RT_SUCCESS(rc))
2341 {
2342 uint32_t cSectors = 0;
2343
2344 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
2345 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2346 {
2347 cBATEntryIndex++;
2348 cSectors = 1;
2349
2350 /*
2351 * The first sector being read is marked dirty, read as much as we
2352 * can from child. Note that only sectors that are marked dirty
2353 * must be read from child.
2354 */
2355 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2356 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2357 {
2358 cBATEntryIndex++;
2359 cSectors++;
2360 }
2361
2362 cbRead = cSectors * VHD_SECTOR_SIZE;
2363
2364 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2365 rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage,
2366 uVhdOffset, pIoCtx, cbRead);
2367 }
2368 else
2369 {
2370 /*
2371 * The first sector being read is marked clean, so we should read from
2372 * our parent instead, but only as much as there are the following
2373 * clean sectors, because the block may still contain dirty sectors
2374 * further on. We just need to compute the number of clean sectors
2375 * and pass it to our caller along with the notification that they
2376 * should be read from the parent.
2377 */
2378 cBATEntryIndex++;
2379 cSectors = 1;
2380
2381 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2382 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2383 {
2384 cBATEntryIndex++;
2385 cSectors++;
2386 }
2387
2388 cbRead = cSectors * VHD_SECTOR_SIZE;
2389 LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2390 rc = VERR_VD_BLOCK_FREE;
2391 }
2392 }
2393 else
2394 AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
2395 }
2396 }
2397 else
2398 rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, uOffset, pIoCtx, cbRead);
2399
2400 if (pcbActuallyRead)
2401 *pcbActuallyRead = cbRead;
2402
2403 LogFlowFunc(("returns rc=%Rrc\n", rc));
2404 return rc;
2405}
2406
2407/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
2408static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
2409 PVDIOCTX pIoCtx,
2410 size_t *pcbWriteProcess, size_t *pcbPreRead,
2411 size_t *pcbPostRead, unsigned fWrite)
2412{
2413 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2414 int rc = VINF_SUCCESS;
2415
2416 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
2417 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
2418
2419 AssertPtr(pImage);
2420 Assert(uOffset % VHD_SECTOR_SIZE == 0);
2421 Assert(cbWrite % VHD_SECTOR_SIZE == 0);
2422
2423 if (pImage->pBlockAllocationTable)
2424 {
2425 /*
2426 * Get the data block first.
2427 */
2428 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
2429 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
2430 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
2431 uint64_t uVhdOffset;
2432
2433 /*
2434 * Clip write range.
2435 */
2436 cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2437
2438 /*
2439 * If the block is not allocated the content of the entry is ~0
2440 * and we need to allocate a new block. Note that while blocks are
2441 * allocated with a relatively big granularity, each sector has its
2442 * own bitmap entry, indicating whether it has been written or not.
2443 * So that means for the purposes of the higher level that the
2444 * granularity is invisible. This means there's no need to return
2445 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
2446 */
2447 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2448 {
2449 /* Check if the block allocation should be suppressed. */
2450 if (fWrite & VD_WRITE_NO_ALLOC)
2451 {
2452 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
2453 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
2454
2455 if (pcbWriteProcess)
2456 *pcbWriteProcess = cbWrite;
2457 return VERR_VD_BLOCK_FREE;
2458 }
2459
2460 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
2461 bool fIoInProgress = false;
2462
2463 if (!pExpand)
2464 return VERR_NO_MEMORY;
2465
2466 pExpand->cbEofOld = pImage->uCurrentEndOfFile;
2467 pExpand->idxBatAllocated = cBlockAllocationTableEntry;
2468 pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
2469
2470 /* Set the bits for all sectors having been written. */
2471 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2472 {
2473 /* No need to check for a changed value because this is an initial write. */
2474 vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
2475 cBATEntryIndex++;
2476 }
2477
2478 do
2479 {
2480 /*
2481 * Start with the sector bitmap.
2482 */
2483 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
2484 pImage->uCurrentEndOfFile,
2485 pExpand->au8Bitmap,
2486 pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE, pIoCtx,
2487 vhdAsyncExpansionDataBlockBitmapComplete,
2488 pExpand);
2489 if (RT_SUCCESS(rc))
2490 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2491 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2492 fIoInProgress = true;
2493 else
2494 {
2495 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2496 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2497 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2498 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2499 break;
2500 }
2501
2502
2503 /*
2504 * Write the new block at the current end of the file.
2505 */
2506 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2507 pImage->uCurrentEndOfFile + pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE,
2508 pIoCtx, cbWrite,
2509 vhdAsyncExpansionDataComplete,
2510 pExpand);
2511 if (RT_SUCCESS(rc))
2512 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2513 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2514 fIoInProgress = true;
2515 else
2516 {
2517 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2518 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2519 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2520 break;
2521 }
2522
2523 /*
2524 * Write entry in the BAT.
2525 */
2526 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
2527 pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
2528 &pExpand->idxBlockBe,
2529 sizeof(uint32_t), pIoCtx,
2530 vhdAsyncExpansionBatUpdateComplete,
2531 pExpand);
2532 if (RT_SUCCESS(rc))
2533 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2534 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2535 fIoInProgress = true;
2536 else
2537 {
2538 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2539 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2540 break;
2541 }
2542
2543 /*
2544 * Set the new end of the file and link the new block into the BAT.
2545 */
2546 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2547 pImage->uCurrentEndOfFile += pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE + pImage->cbDataBlock;
2548
2549 /* Update the footer. */
2550 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
2551 pImage->uCurrentEndOfFile,
2552 &pImage->vhdFooterCopy,
2553 sizeof(VHDFooter), pIoCtx,
2554 vhdAsyncExpansionFooterUpdateComplete,
2555 pExpand);
2556 if (RT_SUCCESS(rc))
2557 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2558 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2559 fIoInProgress = true;
2560 else
2561 {
2562 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2563 break;
2564 }
2565
2566 } while (0);
2567
2568 if (!fIoInProgress)
2569 vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
2570 else
2571 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2572 }
2573 else
2574 {
2575 /*
2576 * Calculate the real offset in the file.
2577 */
2578 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2579
2580 /* Read in the block's bitmap. */
2581 PVDMETAXFER pMetaXfer;
2582 rc = vdIfIoIntFileReadMetaAsync(pImage->pIfIo, pImage->pStorage,
2583 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2584 pImage->pu8Bitmap,
2585 pImage->cbDataBlockBitmap, pIoCtx,
2586 &pMetaXfer, NULL, NULL);
2587 if (RT_SUCCESS(rc))
2588 {
2589 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
2590
2591 /* Write data. */
2592 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2593 uVhdOffset, pIoCtx, cbWrite,
2594 NULL, NULL);
2595 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2596 {
2597 bool fChanged = false;
2598
2599 /* Set the bits for all sectors having been written. */
2600 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2601 {
2602 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
2603 cBATEntryIndex++;
2604 }
2605
2606 /* Only write the bitmap if it was changed. */
2607 if (fChanged)
2608 {
2609 /*
2610 * Write the bitmap back.
2611 *
2612 * @note We don't have a completion callback here because we
2613 * can't do anything if the write fails for some reason.
2614 * The error will propagated to the device/guest
2615 * by the generic VD layer already and we don't need
2616 * to rollback anything here.
2617 */
2618 rc = vdIfIoIntFileWriteMetaAsync(pImage->pIfIo, pImage->pStorage,
2619 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2620 pImage->pu8Bitmap,
2621 pImage->cbDataBlockBitmap,
2622 pIoCtx, NULL, NULL);
2623 }
2624 }
2625 }
2626 }
2627 }
2628 else
2629 rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
2630 uOffset, pIoCtx, cbWrite, NULL, NULL);
2631
2632 if (pcbWriteProcess)
2633 *pcbWriteProcess = cbWrite;
2634
2635 /* Stay on the safe side. Do not run the risk of confusing the higher
2636 * level, as that can be pretty lethal to image consistency. */
2637 *pcbPreRead = 0;
2638 *pcbPostRead = 0;
2639
2640 return rc;
2641}
2642
2643/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
2644static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
2645{
2646 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2647
2648 /* No need to write anything here. Data is always updated on a write. */
2649 return vdIfIoIntFileFlushAsync(pImage->pIfIo, pImage->pStorage,
2650 pIoCtx, NULL, NULL);
2651}
2652
2653/** @copydoc VBOXHDDBACKEND::pfnCompact */
2654static int vhdCompact(void *pBackendData, unsigned uPercentStart,
2655 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
2656 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation)
2657{
2658 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2659 int rc = VINF_SUCCESS;
2660 void *pvBuf = NULL, *pvReplace = NULL;
2661 uint32_t *paBlocks = NULL;
2662
2663 int (*pfnParentRead)(void *, uint64_t, void *, size_t) = NULL;
2664 void *pvParent = NULL;
2665 PVDINTERFACEPARENTSTATE pIfParentState = VDIfParentStateGet(pVDIfsOperation);
2666 if (pIfParentState)
2667 {
2668 pfnParentRead = pIfParentState->pfnParentRead;
2669 pvParent = pIfParentState->Core.pvUser;
2670 }
2671
2672 PFNVDPROGRESS pfnProgress = NULL;
2673 void *pvUser = NULL;
2674 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2675 if (pIfProgress)
2676 {
2677 pfnProgress = pIfProgress->pfnProgress;
2678 pvUser = pIfProgress->Core.pvUser;
2679 }
2680
2681 do
2682 {
2683 AssertBreakStmt(pImage, rc = VERR_INVALID_PARAMETER);
2684
2685 AssertBreakStmt(!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY),
2686 rc = VERR_VD_IMAGE_READ_ONLY);
2687
2688 /* Reject fixed images as they don't have a BAT. */
2689 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2690 {
2691 rc = VERR_NOT_SUPPORTED;
2692 break;
2693 }
2694
2695 if (pfnParentRead)
2696 {
2697 pvParent = RTMemTmpAlloc(pImage->cbDataBlock);
2698 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2699 }
2700 pvBuf = RTMemTmpAlloc(pImage->cbDataBlock);
2701 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2702
2703 unsigned cBlocksAllocated = 0;
2704 unsigned cBlocksToMove = 0;
2705 unsigned cBlocks = pImage->cBlockAllocationTableEntries;
2706 uint32_t offBlocksStart = ~0U; /* Start offset of data blocks in sectors. */
2707 uint32_t *paBat = pImage->pBlockAllocationTable;
2708
2709 /* Count the number of allocated blocks and find the start offset for the data blocks. */
2710 for (unsigned i = 0; i < cBlocks; i++)
2711 if (paBat[i] != ~0U)
2712 {
2713 cBlocksAllocated++;
2714 if (paBat[i] < offBlocksStart)
2715 offBlocksStart = paBat[i];
2716 }
2717
2718 if (!cBlocksAllocated)
2719 {
2720 /* Nothing to do. */
2721 rc = VINF_SUCCESS;
2722 break;
2723 }
2724
2725 paBlocks = (uint32_t *)RTMemTmpAllocZ(cBlocksAllocated * sizeof(uint32_t));
2726 AssertBreakStmt(VALID_PTR(paBlocks), rc = VERR_NO_MEMORY);
2727
2728 /* Invalidate the back resolving array. */
2729 for (unsigned i = 0; i < cBlocksAllocated; i++)
2730 paBlocks[i] = ~0U;
2731
2732 /* Fill the back resolving table. */
2733 for (unsigned i = 0; i < cBlocks; i++)
2734 if (paBat[i] != ~0U)
2735 {
2736 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2737 if ( idxBlock < cBlocksAllocated
2738 && paBlocks[idxBlock] == ~0U)
2739 paBlocks[idxBlock] = i;
2740 else
2741 {
2742 /* The image is in an inconsistent state. Don't go further. */
2743 rc = VERR_INVALID_STATE;
2744 break;
2745 }
2746 }
2747
2748 if (RT_FAILURE(rc))
2749 break;
2750
2751 /* Find redundant information and update the block pointers
2752 * accordingly, creating bubbles. Keep disk up to date, as this
2753 * enables cancelling. */
2754 for (unsigned i = 0; i < cBlocks; i++)
2755 {
2756 if (paBat[i] != ~0U)
2757 {
2758 unsigned idxBlock = (paBat[i] - offBlocksStart) / pImage->cSectorsPerDataBlock;
2759
2760 /* Block present in image file, read relevant data. */
2761 uint64_t u64Offset = ((uint64_t)paBat[i] + pImage->cDataBlockBitmapSectors) * VHD_SECTOR_SIZE;
2762 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2763 u64Offset, pvBuf, pImage->cbDataBlock, NULL);
2764 if (RT_FAILURE(rc))
2765 break;
2766
2767 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)pImage->cbDataBlock * 8) == -1)
2768 {
2769 paBat[i] = ~0;
2770 paBlocks[idxBlock] = ~0U;
2771 /* Adjust progress info, one block to be relocated. */
2772 cBlocksToMove++;
2773 }
2774 else if (pfnParentRead)
2775 {
2776 rc = pfnParentRead(pvParent, (uint64_t)i * pImage->cbDataBlock, pvParent, pImage->cbDataBlock);
2777 if (RT_FAILURE(rc))
2778 break;
2779 if (!memcmp(pvParent, pvBuf, pImage->cbDataBlock))
2780 {
2781 paBat[i] = ~0U;
2782 paBlocks[idxBlock] = ~0U;
2783 /* Adjust progress info, one block to be relocated. */
2784 cBlocksToMove++;
2785 }
2786 }
2787 }
2788
2789 if (pIfProgress && pIfProgress->pfnProgress)
2790 {
2791 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2792 (uint64_t)i * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2793 if (RT_FAILURE(rc))
2794 break;
2795 }
2796 }
2797
2798 if (RT_SUCCESS(rc))
2799 {
2800 /* Fill bubbles with other data (if available). */
2801 unsigned cBlocksMoved = 0;
2802 unsigned uBlockUsedPos = cBlocksAllocated;
2803 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of whole block containing the bitmap and the user data. */
2804
2805 /* Allocate data buffer to hold the data block and allocation bitmap in front of the actual data. */
2806 RTMemTmpFree(pvBuf);
2807 pvBuf = RTMemTmpAllocZ(cbBlock);
2808 AssertBreakStmt(VALID_PTR(pvBuf), rc = VERR_NO_MEMORY);
2809
2810 for (unsigned i = 0; i < cBlocksAllocated; i++)
2811 {
2812 unsigned uBlock = paBlocks[i];
2813 if (uBlock == ~0U)
2814 {
2815 unsigned uBlockData = ~0U;
2816 while (uBlockUsedPos > i && uBlockData == ~0U)
2817 {
2818 uBlockUsedPos--;
2819 uBlockData = paBlocks[uBlockUsedPos];
2820 }
2821 /* Terminate early if there is no block which needs copying. */
2822 if (uBlockUsedPos == i)
2823 break;
2824 uint64_t u64Offset = (uint64_t)uBlockUsedPos * cbBlock
2825 + (offBlocksStart * VHD_SECTOR_SIZE);
2826 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2827 u64Offset, pvBuf, cbBlock, NULL);
2828 if (RT_FAILURE(rc))
2829 break;
2830
2831 u64Offset = (uint64_t)i * cbBlock
2832 + (offBlocksStart * VHD_SECTOR_SIZE);
2833 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2834 u64Offset, pvBuf, cbBlock, NULL);
2835 if (RT_FAILURE(rc))
2836 break;
2837
2838 paBat[uBlockData] = i*(pImage->cSectorsPerDataBlock + pImage->cDataBlockBitmapSectors) + offBlocksStart;
2839
2840 /* Truncate the file but leave enough room for the footer to avoid
2841 * races if other processes fill the whole harddisk. */
2842 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage,
2843 pImage->uCurrentEndOfFile - cbBlock + VHD_SECTOR_SIZE);
2844 if (RT_FAILURE(rc))
2845 break;
2846
2847 /* Update pointers and write footer. */
2848 pImage->uCurrentEndOfFile -= cbBlock;
2849
2850 /* We're kinda screwed if this failes. */
2851 rc = vhdUpdateFooter(pImage);
2852 if (RT_FAILURE(rc))
2853 break;
2854
2855 paBlocks[i] = uBlockData;
2856 paBlocks[uBlockUsedPos] = ~0U;
2857 cBlocksMoved++;
2858 }
2859
2860 if (pIfProgress && pIfProgress->pfnProgress)
2861 {
2862 rc = pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2863 (uint64_t)(cBlocks + cBlocksMoved) * uPercentSpan / (cBlocks + cBlocksToMove) + uPercentStart);
2864
2865 if (RT_FAILURE(rc))
2866 break;
2867 }
2868 }
2869 }
2870
2871 /* Write the new BAT in any case. */
2872 rc = vhdFlushImage(pImage);
2873 } while (0);
2874
2875 if (paBlocks)
2876 RTMemTmpFree(paBlocks);
2877 if (pvParent)
2878 RTMemTmpFree(pvParent);
2879 if (pvBuf)
2880 RTMemTmpFree(pvBuf);
2881
2882 if (RT_SUCCESS(rc) && pIfProgress && pIfProgress->pfnProgress)
2883 {
2884 pIfProgress->pfnProgress(pIfProgress->Core.pvUser,
2885 uPercentStart + uPercentSpan);
2886 }
2887
2888 LogFlowFunc(("returns %Rrc\n", rc));
2889 return rc;
2890}
2891
2892/** @copydoc VBOXHDDBACKEND::pfnResize */
2893static int vhdResize(void *pBackendData, uint64_t cbSize,
2894 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2895 unsigned uPercentStart, unsigned uPercentSpan,
2896 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2897 PVDINTERFACE pVDIfsOperation)
2898{
2899 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2900 int rc = VINF_SUCCESS;
2901
2902 PFNVDPROGRESS pfnProgress = NULL;
2903 void *pvUser = NULL;
2904 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
2905 if (pIfProgress)
2906 {
2907 pfnProgress = pIfProgress->pfnProgress;
2908 pvUser = pIfProgress->Core.pvUser;
2909 }
2910
2911 /* Making the image smaller is not supported at the moment. */
2912 if ( cbSize < pImage->cbSize
2913 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2914 rc = VERR_NOT_SUPPORTED;
2915 else if (cbSize > pImage->cbSize)
2916 {
2917 unsigned cBlocksAllocated = 0;
2918 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of a block including the sector bitmap. */
2919 uint32_t cBlocksNew = cbSize / pImage->cbDataBlock; /** < New number of blocks in the image after the resize */
2920 if (cbSize % pImage->cbDataBlock)
2921 cBlocksNew++;
2922
2923 uint32_t cBlocksOld = pImage->cBlockAllocationTableEntries; /** < Number of blocks before the resize. */
2924 uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE); /** < Required space for the block array after the resize. */
2925 uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
2926 uint64_t offStartDataOld = ~0ULL;
2927
2928 /* Go through the BAT and find the data start offset. */
2929 for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
2930 {
2931 if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
2932 {
2933 uint64_t offStartBlock = pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
2934 if (offStartBlock < offStartDataOld)
2935 offStartDataOld = offStartBlock;
2936 cBlocksAllocated++;
2937 }
2938 }
2939
2940 if ( offStartDataOld != offStartDataNew
2941 && cBlocksAllocated > 0)
2942 {
2943 /* Calculate how many sectors nee to be relocated. */
2944 uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
2945 unsigned cBlocksReloc = (unsigned)(cbOverlapping / cbBlock);
2946 if (cbOverlapping % cbBlock)
2947 cBlocksReloc++;
2948
2949 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2950 offStartDataNew = offStartDataOld;
2951
2952 /* Do the relocation. */
2953 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2954
2955 /*
2956 * Get the blocks we need to relocate first, they are appended to the end
2957 * of the image.
2958 */
2959 void *pvBuf = NULL, *pvZero = NULL;
2960 do
2961 {
2962 /* Allocate data buffer. */
2963 pvBuf = RTMemAllocZ(cbBlock);
2964 if (!pvBuf)
2965 {
2966 rc = VERR_NO_MEMORY;
2967 break;
2968 }
2969
2970 /* Allocate buffer for overwriting with zeroes. */
2971 pvZero = RTMemAllocZ(cbBlock);
2972 if (!pvZero)
2973 {
2974 rc = VERR_NO_MEMORY;
2975 break;
2976 }
2977
2978 for (unsigned i = 0; i < cBlocksReloc; i++)
2979 {
2980 uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
2981
2982 /* Search the index in the block table. */
2983 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2984 {
2985 if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
2986 {
2987 /* Read data and append to the end of the image. */
2988 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
2989 offStartDataNew, pvBuf, cbBlock, NULL);
2990 if (RT_FAILURE(rc))
2991 break;
2992
2993 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
2994 pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
2995 if (RT_FAILURE(rc))
2996 break;
2997
2998 /* Zero out the old block area. */
2999 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
3000 offStartDataNew, pvZero, cbBlock, NULL);
3001 if (RT_FAILURE(rc))
3002 break;
3003
3004 /* Update block counter. */
3005 pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
3006
3007 pImage->uCurrentEndOfFile += cbBlock;
3008
3009 /* Continue with the next block. */
3010 break;
3011 }
3012 }
3013
3014 if (RT_FAILURE(rc))
3015 break;
3016
3017 offStartDataNew += cbBlock;
3018 }
3019 } while (0);
3020
3021 if (pvBuf)
3022 RTMemFree(pvBuf);
3023 if (pvZero)
3024 RTMemFree(pvZero);
3025 }
3026
3027 /*
3028 * Relocation done, expand the block array and update the header with
3029 * the new data.
3030 */
3031 if (RT_SUCCESS(rc))
3032 {
3033 uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
3034 if (paBlocksNew)
3035 {
3036 pImage->pBlockAllocationTable = paBlocksNew;
3037
3038 /* Mark the new blocks as unallocated. */
3039 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
3040 pImage->pBlockAllocationTable[idxBlock] = ~0U;
3041 }
3042 else
3043 rc = VERR_NO_MEMORY;
3044
3045 if (RT_SUCCESS(rc))
3046 {
3047 /* Write the block array before updating the rest. */
3048 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
3049 pImage->uBlockAllocationTableOffset,
3050 pImage->pBlockAllocationTable,
3051 cBlocksNew * sizeof(uint32_t), NULL);
3052 }
3053
3054 if (RT_SUCCESS(rc))
3055 {
3056 /* Update size and new block count. */
3057 pImage->cBlockAllocationTableEntries = cBlocksNew;
3058 pImage->cbSize = cbSize;
3059
3060 /* Update geometry. */
3061 pImage->PCHSGeometry = *pPCHSGeometry;
3062 pImage->LCHSGeometry = *pLCHSGeometry;
3063 }
3064 }
3065
3066 /* Update header information in base image file. */
3067 pImage->fDynHdrNeedsUpdate = true;
3068 vhdFlush(pImage);
3069 }
3070 /* Same size doesn't change the image at all. */
3071
3072 LogFlowFunc(("returns %Rrc\n", rc));
3073 return rc;
3074}
3075
3076/** @copydoc VBOXHDDBACKEND::pfnRepair */
3077static DECLCALLBACK(int) vhdRepair(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
3078 PVDINTERFACE pVDIfsImage, uint32_t fFlags)
3079{
3080 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
3081 int rc;
3082 PVDINTERFACEERROR pIfError;
3083 PVDINTERFACEIOINT pIfIo;
3084 PVDIOSTORAGE pStorage;
3085 uint64_t cbFile;
3086 VHDFooter vhdFooter;
3087 VHDDynamicDiskHeader dynamicDiskHeader;
3088 uint32_t *paBat = NULL;
3089 uint32_t *pu32BlockBitmap = NULL;
3090
3091 pIfIo = VDIfIoIntGet(pVDIfsImage);
3092 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
3093
3094 pIfError = VDIfErrorGet(pVDIfsDisk);
3095
3096 do
3097 {
3098 uint64_t offDynamicDiskHeader = 0;
3099 uint64_t offBat = 0;
3100 uint64_t offFooter = 0;
3101 uint32_t cBatEntries = 0;
3102 bool fDynamic = false;
3103 bool fRepairFooter = false;
3104 bool fRepairBat = false;
3105 bool fRepairDynHeader = false;
3106
3107 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
3108 VDOpenFlagsToFileOpenFlags( fFlags & VD_REPAIR_DRY_RUN
3109 ? VD_OPEN_FLAGS_READONLY
3110 : 0,
3111 false /* fCreate */),
3112 &pStorage);
3113 if (RT_FAILURE(rc))
3114 {
3115 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to open image \"%s\"", pszFilename);
3116 break;
3117 }
3118
3119 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
3120 if (RT_FAILURE(rc))
3121 {
3122 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to query image size");
3123 break;
3124 }
3125
3126 if (cbFile < sizeof(VHDFooter))
3127 {
3128 rc = vdIfError(pIfError, VERR_VD_INVALID_SIZE, RT_SRC_POS,
3129 "Image must be at least %u bytes (got %llu)",
3130 sizeof(VHDFooter), cbFile);
3131 break;
3132 }
3133
3134 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, cbFile - sizeof(VHDFooter),
3135 &vhdFooter, sizeof(VHDFooter), NULL);
3136 if (RT_FAILURE(rc))
3137 {
3138 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read footer of image");
3139 break;
3140 }
3141
3142 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
3143 {
3144 /* Dynamic images have a backup at the beginning of the image. */
3145 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0,
3146 &vhdFooter, sizeof(VHDFooter), NULL);
3147 if (RT_FAILURE(rc))
3148 {
3149 rc = vdIfError(pIfError, rc, RT_SRC_POS, "Failed to read header of image");
3150 break;
3151 }
3152
3153 /*
3154 * Check for the header, if this fails the image is either completely corrupted
3155 * and impossible to repair or in another format.
3156 */
3157 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
3158 {
3159 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3160 "No valid VHD structures found");
3161 break;
3162 }
3163 else
3164 vdIfErrorMessage(pIfError, "Missing footer structure, using backup\n");
3165
3166 /* Remember to fix the footer structure. */
3167 fRepairFooter = true;
3168 }
3169
3170 offFooter = cbFile - sizeof(VHDFooter);
3171
3172 /* Verify that checksums match. */
3173 uint32_t u32ChkSumOld = RT_BE2H_U32(vhdFooter.Checksum);
3174 vhdFooter.Checksum = 0;
3175 uint32_t u32ChkSum = vhdChecksum(&vhdFooter, sizeof(VHDFooter));
3176
3177 vhdFooter.Checksum = RT_H2BE_U32(u32ChkSum);
3178
3179 if (u32ChkSumOld != u32ChkSum)
3180 {
3181 vdIfErrorMessage(pIfError, "Checksum is invalid (should be %u got %u), repairing\n",
3182 u32ChkSum, u32ChkSumOld);
3183 fRepairFooter = true;
3184 break;
3185 }
3186
3187 switch (RT_BE2H_U32(vhdFooter.DiskType))
3188 {
3189 case VHD_FOOTER_DISK_TYPE_FIXED:
3190 fDynamic = false;
3191 break;
3192 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
3193 fDynamic = true;
3194 break;
3195 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
3196 fDynamic = true;
3197 break;
3198 default:
3199 {
3200 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3201 "VHD image type %u is not supported",
3202 RT_BE2H_U32(vhdFooter.DiskType));
3203 break;
3204 }
3205 }
3206
3207 /* Load and check dynamic disk header if required. */
3208 if (fDynamic)
3209 {
3210 size_t cbBlock;
3211
3212 offDynamicDiskHeader = RT_BE2H_U64(vhdFooter.DataOffset);
3213 if (offDynamicDiskHeader + sizeof(VHDDynamicDiskHeader) > cbFile)
3214 {
3215 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3216 "VHD image type is not supported");
3217 break;
3218 }
3219
3220 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offDynamicDiskHeader,
3221 &dynamicDiskHeader, sizeof(VHDDynamicDiskHeader), NULL);
3222 if (RT_FAILURE(rc))
3223 {
3224 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3225 "Failed to read dynamic disk header (at %llu), %Rrc",
3226 offDynamicDiskHeader, rc);
3227 break;
3228 }
3229
3230 /* Verify that checksums match. */
3231 u32ChkSumOld = RT_BE2H_U32(dynamicDiskHeader.Checksum);
3232 dynamicDiskHeader.Checksum = 0;
3233 u32ChkSum = vhdChecksum(&dynamicDiskHeader, sizeof(VHDDynamicDiskHeader));
3234
3235 dynamicDiskHeader.Checksum = RT_H2BE_U32(u32ChkSum);
3236
3237 if (u32ChkSumOld != u32ChkSum)
3238 {
3239 vdIfErrorMessage(pIfError, "Checksum of dynamic disk header is invalid (should be %u got %u), repairing\n",
3240 u32ChkSum, u32ChkSumOld);
3241 fRepairDynHeader = true;
3242 break;
3243 }
3244
3245 /* Read the block allocation table and fix any inconsistencies. */
3246 offBat = RT_BE2H_U64(dynamicDiskHeader.TableOffset);
3247 cBatEntries = RT_BE2H_U32(dynamicDiskHeader.MaxTableEntries);
3248 cbBlock = RT_BE2H_U32(dynamicDiskHeader.BlockSize);
3249 cbBlock += cbBlock / VHD_SECTOR_SIZE / 8;
3250
3251 if (offBat + cBatEntries * sizeof(uint32_t) > cbFile)
3252 {
3253 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3254 "Block allocation table is not inside the image");
3255 break;
3256 }
3257
3258 paBat = (uint32_t *)RTMemAllocZ(cBatEntries * sizeof(uint32_t));
3259 if (!paBat)
3260 {
3261 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3262 "Could not allocate memory for the block allocation table (%u bytes)",
3263 cBatEntries * sizeof(uint32_t));
3264 break;
3265 }
3266
3267 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, offBat, paBat,
3268 cBatEntries * sizeof(uint32_t), NULL);
3269 if (RT_FAILURE(rc))
3270 {
3271 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3272 "Could not read block allocation table (at %llu), %Rrc",
3273 offBat, rc);
3274 break;
3275 }
3276
3277 pu32BlockBitmap = (uint32_t *)RTMemAllocZ(RT_ALIGN_Z(cBatEntries / 8, 4));
3278 if (!pu32BlockBitmap)
3279 {
3280 rc = vdIfError(pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3281 "Failed to allocate memory for block bitmap");
3282 break;
3283 }
3284
3285 uint32_t idxMinBlock = UINT32_C(0xffffffff);
3286 for (uint32_t i = 0; i < cBatEntries; i++)
3287 {
3288 paBat[i] = RT_BE2H_U32(paBat[i]);
3289 if (paBat[i] < idxMinBlock)
3290 idxMinBlock = paBat[i];
3291 }
3292
3293 vdIfErrorMessage(pIfError, "First data block at sector %u\n", idxMinBlock);
3294
3295 for (uint32_t i = 0; i < cBatEntries; i++)
3296 {
3297 if (paBat[i] != UINT32_C(0xffffffff))
3298 {
3299 uint64_t offBlock =(uint64_t)paBat[i] * VHD_SECTOR_SIZE;
3300
3301 /*
3302 * Check that the offsets are valid (inside of the image) and
3303 * that there are no double references.
3304 */
3305 if (offBlock + cbBlock > cbFile)
3306 {
3307 vdIfErrorMessage(pIfError, "Entry %u points to invalid offset %llu, clearing\n",
3308 i, offBlock);
3309 paBat[i] = UINT32_C(0xffffffff);
3310 fRepairBat = true;
3311 }
3312 else if (offBlock + cbBlock > offFooter)
3313 {
3314 vdIfErrorMessage(pIfError, "Entry %u intersects with footer, aligning footer\n",
3315 i);
3316 offFooter = offBlock + cbBlock;
3317 fRepairBat = true;
3318 }
3319
3320 if ( paBat[i] != UINT32_C(0xffffffff)
3321 && ASMBitTestAndSet(pu32BlockBitmap, (paBat[i] - idxMinBlock) / (cbBlock / VHD_SECTOR_SIZE)))
3322 {
3323 vdIfErrorMessage(pIfError, "Entry %u points to an already referenced data block, clearing\n",
3324 i);
3325 paBat[i] = UINT32_C(0xffffffff);
3326 fRepairBat = true;
3327 }
3328 }
3329 }
3330 }
3331
3332 /* Write repaired structures now. */
3333 if (!(fRepairBat || fRepairDynHeader || fRepairFooter))
3334 vdIfErrorMessage(pIfError, "VHD image is in a consistent state, no repair required\n");
3335 else if (!(fFlags & VD_REPAIR_DRY_RUN))
3336 {
3337 if (fRepairBat)
3338 {
3339 for (uint32_t i = 0; i < cBatEntries; i++)
3340 paBat[i] = RT_H2BE_U32(paBat[i]);
3341
3342 vdIfErrorMessage(pIfError, "Writing repaired block allocation table...\n");
3343
3344 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offBat, paBat,
3345 cBatEntries * sizeof(uint32_t), NULL);
3346 if (RT_FAILURE(rc))
3347 {
3348 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3349 "Could not write repaired block allocation table (at %llu), %Rrc",
3350 offBat, rc);
3351 break;
3352 }
3353 }
3354
3355 if (fRepairDynHeader)
3356 {
3357 Assert(fDynamic);
3358
3359 vdIfErrorMessage(pIfError, "Writing repaired dynamic disk header...\n");
3360 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offDynamicDiskHeader, &dynamicDiskHeader,
3361 sizeof(VHDDynamicDiskHeader), NULL);
3362 if (RT_FAILURE(rc))
3363 {
3364 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3365 "Could not write repaired dynamic disk header (at %llu), %Rrc",
3366 offDynamicDiskHeader, rc);
3367 break;
3368 }
3369 }
3370
3371 if (fRepairFooter)
3372 {
3373 vdIfErrorMessage(pIfError, "Writing repaired Footer...\n");
3374
3375 if (fDynamic)
3376 {
3377 /* Write backup at image beginning. */
3378 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, 0, &vhdFooter,
3379 sizeof(VHDFooter), NULL);
3380 if (RT_FAILURE(rc))
3381 {
3382 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3383 "Could not write repaired backup footer (at %llu), %Rrc",
3384 0, rc);
3385 break;
3386 }
3387 }
3388
3389 rc = vdIfIoIntFileWriteSync(pIfIo, pStorage, offFooter, &vhdFooter,
3390 sizeof(VHDFooter), NULL);
3391 if (RT_FAILURE(rc))
3392 {
3393 rc = vdIfError(pIfError, VERR_VD_IMAGE_REPAIR_IMPOSSIBLE, RT_SRC_POS,
3394 "Could not write repaired footer (at %llu), %Rrc",
3395 cbFile - sizeof(VHDFooter), rc);
3396 break;
3397 }
3398 }
3399
3400 vdIfErrorMessage(pIfError, "Corrupted VHD image repaired successfully\n");
3401 }
3402 } while(0);
3403
3404 if (paBat)
3405 RTMemFree(paBat);
3406
3407 if (pu32BlockBitmap)
3408 RTMemFree(pu32BlockBitmap);
3409
3410 if (pStorage)
3411 vdIfIoIntFileClose(pIfIo, pStorage);
3412
3413 LogFlowFunc(("returns %Rrc\n", rc));
3414 return rc;
3415}
3416
3417
3418VBOXHDDBACKEND g_VhdBackend =
3419{
3420 /* pszBackendName */
3421 "VHD",
3422 /* cbSize */
3423 sizeof(VBOXHDDBACKEND),
3424 /* uBackendCaps */
3425 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
3426 VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
3427 VD_CAP_ASYNC | VD_CAP_VFS,
3428 /* paFileExtensions */
3429 s_aVhdFileExtensions,
3430 /* paConfigInfo */
3431 NULL,
3432 /* hPlugin */
3433 NIL_RTLDRMOD,
3434 /* pfnCheckIfValid */
3435 vhdCheckIfValid,
3436 /* pfnOpen */
3437 vhdOpen,
3438 /* pfnCreate */
3439 vhdCreate,
3440 /* pfnRename */
3441 vhdRename,
3442 /* pfnClose */
3443 vhdClose,
3444 /* pfnRead */
3445 vhdRead,
3446 /* pfnWrite */
3447 vhdWrite,
3448 /* pfnFlush */
3449 vhdFlush,
3450 /* pfnGetVersion */
3451 vhdGetVersion,
3452 /* pfnGetSize */
3453 vhdGetSize,
3454 /* pfnGetFileSize */
3455 vhdGetFileSize,
3456 /* pfnGetPCHSGeometry */
3457 vhdGetPCHSGeometry,
3458 /* pfnSetPCHSGeometry */
3459 vhdSetPCHSGeometry,
3460 /* pfnGetLCHSGeometry */
3461 vhdGetLCHSGeometry,
3462 /* pfnSetLCHSGeometry */
3463 vhdSetLCHSGeometry,
3464 /* pfnGetImageFlags */
3465 vhdGetImageFlags,
3466 /* pfnGetOpenFlags */
3467 vhdGetOpenFlags,
3468 /* pfnSetOpenFlags */
3469 vhdSetOpenFlags,
3470 /* pfnGetComment */
3471 vhdGetComment,
3472 /* pfnSetComment */
3473 vhdSetComment,
3474 /* pfnGetUuid */
3475 vhdGetUuid,
3476 /* pfnSetUuid */
3477 vhdSetUuid,
3478 /* pfnGetModificationUuid */
3479 vhdGetModificationUuid,
3480 /* pfnSetModificationUuid */
3481 vhdSetModificationUuid,
3482 /* pfnGetParentUuid */
3483 vhdGetParentUuid,
3484 /* pfnSetParentUuid */
3485 vhdSetParentUuid,
3486 /* pfnGetParentModificationUuid */
3487 vhdGetParentModificationUuid,
3488 /* pfnSetParentModificationUuid */
3489 vhdSetParentModificationUuid,
3490 /* pfnDump */
3491 vhdDump,
3492 /* pfnGetTimeStamp */
3493 vhdGetTimeStamp,
3494 /* pfnGetParentTimeStamp */
3495 vhdGetParentTimeStamp,
3496 /* pfnSetParentTimeStamp */
3497 vhdSetParentTimeStamp,
3498 /* pfnGetParentFilename */
3499 vhdGetParentFilename,
3500 /* pfnSetParentFilename */
3501 vhdSetParentFilename,
3502 /* pfnAsyncRead */
3503 vhdAsyncRead,
3504 /* pfnAsyncWrite */
3505 vhdAsyncWrite,
3506 /* pfnAsyncFlush */
3507 vhdAsyncFlush,
3508 /* pfnComposeLocation */
3509 genericFileComposeLocation,
3510 /* pfnComposeName */
3511 genericFileComposeName,
3512 /* pfnCompact */
3513 vhdCompact,
3514 /* pfnResize */
3515 vhdResize,
3516 /* pfnDiscard */
3517 NULL,
3518 /* pfnAsyncDiscard */
3519 NULL,
3520 /* pfnRepair */
3521 vhdRepair
3522};
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