VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VHDHDDCore.cpp@ 33223

Last change on this file since 33223 was 33182, checked in by vboxsync, 14 years ago

Devices/Storage: implement sequential reading of streamOptimized VMDK images

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