VirtualBox

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

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

Storage/VBoxHDD: replace custom open flags with regular IPRT file open flags, introduce user-providable filesystem access interface, eliminate dependency on PGM geometry structure, change pvBuffer/cbBuffer parameter ordering to the usual conventions, eliminate the remains of the old I/O code, make more plugin methods optional to reduce redundancy, lots of cleanups

Storage/DrvVD+testcases,Main/Medium+Frontends: adapt to VBoxHDD changes, logging fixes

Storage/VDI+VMDK+DMG+Raw+VHD+Parallels+VCI: made as similar to each other as possible, added inline VFS wrappers to improve readability, full VFS support, VDI files are now 4K aligned, eliminate the remains of the old I/O code, various more or less severe bugfixes, code sort

Storage/iSCSI: support disks bigger than 2T, streamline the code to be more similar to the file-based backends, memory leak fix, error code usage like file-based backends, code sort

log+err: added new error codes/log groups and eliminated unused old ones

  • 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 32536 2010-09-15 18:25:32Z 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 PVDINTERFACEIO 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_IO);
950 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
951 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(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_IO);
1389 AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
1390 PVDINTERFACEIO pInterfaceIOCallbacks = VDGetInterfaceIO(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_IO);
1519 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
1520 pImage->pInterfaceIOCallbacks = VDGetInterfaceIO(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. Just readonly and
2083 * info flags are supported. */
2084 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE)))
2085 {
2086 rc = VERR_INVALID_PARAMETER;
2087 goto out;
2088 }
2089
2090 /* Implement this operation via reopening the image. */
2091 rc = vhdFreeImage(pImage, false);
2092 if (RT_FAILURE(rc))
2093 goto out;
2094 rc = vhdOpenImage(pImage, uOpenFlags);
2095
2096out:
2097 LogFlowFunc(("returns %Rrc\n", rc));
2098 return rc;
2099}
2100
2101/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2102static int vhdGetComment(void *pBackendData, char *pszComment,
2103 size_t cbComment)
2104{
2105 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2106 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2107 int rc;
2108
2109 AssertPtr(pImage);
2110
2111 if (pImage)
2112 rc = VERR_NOT_SUPPORTED;
2113 else
2114 rc = VERR_VD_NOT_OPENED;
2115
2116 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2117 return rc;
2118}
2119
2120/** @copydoc VBOXHDDBACKEND::pfnSetComment */
2121static int vhdSetComment(void *pBackendData, const char *pszComment)
2122{
2123 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2124 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2125 int rc;
2126
2127 AssertPtr(pImage);
2128
2129 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2130 {
2131 rc = VERR_VD_IMAGE_READ_ONLY;
2132 goto out;
2133 }
2134
2135 if (pImage)
2136 rc = VERR_NOT_SUPPORTED;
2137 else
2138 rc = VERR_VD_NOT_OPENED;
2139
2140out:
2141 LogFlowFunc(("returns %Rrc\n", rc));
2142 return rc;
2143}
2144
2145/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2146static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
2147{
2148 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2149 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2150 int rc;
2151
2152 AssertPtr(pImage);
2153
2154 if (pImage)
2155 {
2156 *pUuid = pImage->ImageUuid;
2157 rc = VINF_SUCCESS;
2158 }
2159 else
2160 rc = VERR_VD_NOT_OPENED;
2161
2162 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2163 return rc;
2164}
2165
2166/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2167static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
2168{
2169 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2170 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2171 int rc;
2172
2173 AssertPtr(pImage);
2174
2175 if (pImage)
2176 {
2177 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2178 {
2179 pImage->ImageUuid = *pUuid;
2180 /* Update the footer copy. It will get written to disk when the image is closed. */
2181 memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
2182 /* Update checksum. */
2183 pImage->vhdFooterCopy.Checksum = 0;
2184 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
2185
2186 /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
2187 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2188 pImage->fDynHdrNeedsUpdate = true;
2189 rc = VINF_SUCCESS;
2190 }
2191 else
2192 rc = VERR_VD_IMAGE_READ_ONLY;
2193 }
2194 else
2195 rc = VERR_VD_NOT_OPENED;
2196
2197 LogFlowFunc(("returns %Rrc\n", rc));
2198 return rc;
2199}
2200
2201/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2202static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2203{
2204 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2205 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2206 int rc;
2207
2208 AssertPtr(pImage);
2209
2210 if (pImage)
2211 rc = VERR_NOT_SUPPORTED;
2212 else
2213 rc = VERR_VD_NOT_OPENED;
2214
2215 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2216 return rc;
2217}
2218
2219/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2220static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2221{
2222 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2223 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2224 int rc;
2225
2226 AssertPtr(pImage);
2227
2228 if (pImage)
2229 {
2230 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2231 rc = VERR_NOT_SUPPORTED;
2232 else
2233 rc = VERR_VD_IMAGE_READ_ONLY;
2234 }
2235 else
2236 rc = VERR_VD_NOT_OPENED;
2237
2238 LogFlowFunc(("returns %Rrc\n", rc));
2239 return rc;
2240}
2241
2242/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2243static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
2244{
2245 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2246 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2247 int rc;
2248
2249 AssertPtr(pImage);
2250
2251 if (pImage)
2252 {
2253 *pUuid = pImage->ParentUuid;
2254 rc = VINF_SUCCESS;
2255 }
2256 else
2257 rc = VERR_VD_NOT_OPENED;
2258
2259 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2260 return rc;
2261}
2262
2263/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2264static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2265{
2266 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2267 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2268 int rc = VINF_SUCCESS;
2269
2270 AssertPtr(pImage);
2271
2272 if (pImage && pImage->pStorage)
2273 {
2274 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2275 {
2276 pImage->ParentUuid = *pUuid;
2277 pImage->fDynHdrNeedsUpdate = true;
2278 }
2279 else
2280 rc = VERR_VD_IMAGE_READ_ONLY;
2281 }
2282 else
2283 rc = VERR_VD_NOT_OPENED;
2284
2285 LogFlowFunc(("returns %Rrc\n", rc));
2286 return rc;
2287}
2288
2289/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2290static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2291{
2292 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2293 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2294 int rc;
2295
2296 AssertPtr(pImage);
2297
2298 if (pImage)
2299 rc = VERR_NOT_SUPPORTED;
2300 else
2301 rc = VERR_VD_NOT_OPENED;
2302
2303 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2304 return rc;
2305}
2306
2307/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2308static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2309{
2310 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2311 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2312 int rc;
2313
2314 AssertPtr(pImage);
2315
2316 if (pImage)
2317 {
2318 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2319 rc = VERR_NOT_SUPPORTED;
2320 else
2321 rc = VERR_VD_IMAGE_READ_ONLY;
2322 }
2323 else
2324 rc = VERR_VD_NOT_OPENED;
2325
2326 LogFlowFunc(("returns %Rrc\n", rc));
2327 return rc;
2328}
2329
2330/** @copydoc VBOXHDDBACKEND::pfnDump */
2331static void vhdDump(void *pBackendData)
2332{
2333 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2334
2335 AssertPtr(pImage);
2336 if (pImage)
2337 {
2338 vhdMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
2339 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2340 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2341 VHD_SECTOR_SIZE);
2342 vhdMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
2343 vhdMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
2344 }
2345}
2346
2347/** @copydoc VBOXHDDBACKEND::pfnGetTimestamp */
2348static int vhdGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2349{
2350 int rc = VINF_SUCCESS;
2351 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2352
2353 AssertPtr(pImage);
2354
2355 if (pImage)
2356 rc = vhdFileGetModificationTime(pImage, pImage->pszFilename, pTimeStamp);
2357 else
2358 rc = VERR_VD_NOT_OPENED;
2359
2360 LogFlowFunc(("returns %Rrc\n", rc));
2361 return rc;
2362}
2363
2364/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
2365static int vhdGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2366{
2367 int rc = VINF_SUCCESS;
2368 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2369
2370 AssertPtr(pImage);
2371
2372 if (pImage)
2373 vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
2374 else
2375 rc = VERR_VD_NOT_OPENED;
2376
2377 LogFlowFunc(("returns %Rrc\n", rc));
2378 return rc;
2379}
2380
2381/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
2382static int vhdSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
2383{
2384 int rc = VINF_SUCCESS;
2385 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2386
2387 AssertPtr(pImage);
2388 if (pImage)
2389 {
2390 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2391 rc = VERR_VD_IMAGE_READ_ONLY;
2392 else
2393 {
2394 pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
2395 pImage->fDynHdrNeedsUpdate = true;
2396 }
2397 }
2398 else
2399 rc = VERR_VD_NOT_OPENED;
2400
2401 LogFlowFunc(("returns %Rrc\n", rc));
2402 return rc;
2403}
2404
2405/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
2406static int vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
2407{
2408 int rc = VINF_SUCCESS;
2409 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2410
2411 AssertPtr(pImage);
2412 if (pImage)
2413 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
2414 else
2415 rc = VERR_VD_NOT_OPENED;
2416
2417 LogFlowFunc(("returns %Rrc\n", rc));
2418 return rc;
2419}
2420
2421/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
2422static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
2423{
2424 int rc = VINF_SUCCESS;
2425 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2426
2427 AssertPtr(pImage);
2428 if (pImage)
2429 {
2430 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2431 rc = VERR_VD_IMAGE_READ_ONLY;
2432 else
2433 {
2434 if (pImage->pszParentFilename)
2435 RTStrFree(pImage->pszParentFilename);
2436 pImage->pszParentFilename = RTStrDup(pszParentFilename);
2437 if (!pImage->pszParentFilename)
2438 rc = VERR_NO_MEMORY;
2439 else
2440 pImage->fDynHdrNeedsUpdate = true;
2441 }
2442 }
2443 else
2444 rc = VERR_VD_NOT_OPENED;
2445
2446 LogFlowFunc(("returns %Rrc\n", rc));
2447 return rc;
2448}
2449
2450/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
2451static bool vhdIsAsyncIOSupported(void *pBackendData)
2452{
2453 return true;
2454}
2455
2456/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
2457static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
2458 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
2459{
2460 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2461 int rc = VINF_SUCCESS;
2462
2463 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
2464
2465 if (uOffset + cbRead > pImage->cbSize)
2466 return VERR_INVALID_PARAMETER;
2467
2468 /*
2469 * If we have a dynamic disk image, we need to find the data block and sector to read.
2470 */
2471 if (pImage->pBlockAllocationTable)
2472 {
2473 /*
2474 * Get the data block first.
2475 */
2476 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
2477 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
2478 uint64_t uVhdOffset;
2479
2480 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
2481 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
2482
2483 /*
2484 * If the block is not allocated the content of the entry is ~0
2485 */
2486 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2487 {
2488 /* Return block size as read. */
2489 *pcbActuallyRead = RT_MIN(cbRead, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
2490 return VERR_VD_BLOCK_FREE;
2491 }
2492
2493 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2494 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2495
2496 /*
2497 * Clip read range to remain in this data block.
2498 */
2499 cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2500
2501 /* Read in the block's bitmap. */
2502 PVDMETAXFER pMetaXfer;
2503 rc = vhdFileReadMetaAsync(pImage,
2504 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2505 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
2506 pIoCtx, &pMetaXfer, NULL, NULL);
2507
2508 if (RT_SUCCESS(rc))
2509 {
2510 uint32_t cSectors = 0;
2511
2512 vhdFileMetaXferRelease(pImage, pMetaXfer);
2513 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2514 {
2515 cBATEntryIndex++;
2516 cSectors = 1;
2517
2518 /*
2519 * The first sector being read is marked dirty, read as much as we
2520 * can from child. Note that only sectors that are marked dirty
2521 * must be read from child.
2522 */
2523 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2524 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2525 {
2526 cBATEntryIndex++;
2527 cSectors++;
2528 }
2529
2530 cbRead = cSectors * VHD_SECTOR_SIZE;
2531
2532 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2533 rc = vhdFileReadUserAsync(pImage, uVhdOffset, pIoCtx, cbRead);
2534 }
2535 else
2536 {
2537 /*
2538 * The first sector being read is marked clean, so we should read from
2539 * our parent instead, but only as much as there are the following
2540 * clean sectors, because the block may still contain dirty sectors
2541 * further on. We just need to compute the number of clean sectors
2542 * and pass it to our caller along with the notification that they
2543 * should be read from the parent.
2544 */
2545 cBATEntryIndex++;
2546 cSectors = 1;
2547
2548 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2549 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2550 {
2551 cBATEntryIndex++;
2552 cSectors++;
2553 }
2554
2555 cbRead = cSectors * VHD_SECTOR_SIZE;
2556 LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2557 rc = VERR_VD_BLOCK_FREE;
2558 }
2559 }
2560 else
2561 AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
2562 }
2563 else
2564 {
2565 rc = vhdFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
2566 }
2567
2568 if (pcbActuallyRead)
2569 *pcbActuallyRead = cbRead;
2570
2571 LogFlowFunc(("returns rc=%Rrc\n", rc));
2572 return rc;
2573}
2574
2575/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
2576static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
2577 PVDIOCTX pIoCtx,
2578 size_t *pcbWriteProcess, size_t *pcbPreRead,
2579 size_t *pcbPostRead, unsigned fWrite)
2580{
2581 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2582 int rc = VINF_SUCCESS;
2583
2584 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
2585 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
2586
2587 AssertPtr(pImage);
2588 Assert(uOffset % VHD_SECTOR_SIZE == 0);
2589 Assert(cbWrite % VHD_SECTOR_SIZE == 0);
2590
2591 if (pImage->pBlockAllocationTable)
2592 {
2593 /*
2594 * Get the data block first.
2595 */
2596 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
2597 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
2598 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
2599 uint64_t uVhdOffset;
2600
2601 /*
2602 * Clip write range.
2603 */
2604 cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2605
2606 /*
2607 * If the block is not allocated the content of the entry is ~0
2608 * and we need to allocate a new block. Note that while blocks are
2609 * allocated with a relatively big granularity, each sector has its
2610 * own bitmap entry, indicating whether it has been written or not.
2611 * So that means for the purposes of the higher level that the
2612 * granularity is invisible. This means there's no need to return
2613 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
2614 */
2615 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2616 {
2617 /* Check if the block allocation should be suppressed. */
2618 if (fWrite & VD_WRITE_NO_ALLOC)
2619 {
2620 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
2621 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
2622
2623 if (pcbWriteProcess)
2624 *pcbWriteProcess = cbWrite;
2625 return VERR_VD_BLOCK_FREE;
2626 }
2627
2628 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
2629 bool fIoInProgress = false;
2630
2631 if (!pExpand)
2632 return VERR_NO_MEMORY;
2633
2634 pExpand->cbEofOld = pImage->uCurrentEndOfFile;
2635 pExpand->idxBatAllocated = cBlockAllocationTableEntry;
2636 pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
2637
2638 /* Set the bits for all sectors having been written. */
2639 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2640 {
2641 /* No need to check for a changed value because this is an initial write. */
2642 vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
2643 cBATEntryIndex++;
2644 }
2645
2646 do
2647 {
2648 /*
2649 * Start with the sector bitmap.
2650 */
2651 rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
2652 pExpand->au8Bitmap,
2653 pImage->cbDataBlockBitmap, pIoCtx,
2654 vhdAsyncExpansionDataBlockBitmapComplete,
2655 pExpand);
2656 if (RT_SUCCESS(rc))
2657 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2658 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2659 fIoInProgress = true;
2660 else
2661 {
2662 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2663 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2664 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2665 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2666 break;
2667 }
2668
2669
2670 /*
2671 * Write the new block at the current end of the file.
2672 */
2673 rc = vhdFileWriteUserAsync(pImage,
2674 pImage->uCurrentEndOfFile + pImage->cbDataBlockBitmap,
2675 pIoCtx, cbWrite,
2676 vhdAsyncExpansionDataComplete,
2677 pExpand);
2678 if (RT_SUCCESS(rc))
2679 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2680 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2681 fIoInProgress = true;
2682 else
2683 {
2684 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2685 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2686 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2687 break;
2688 }
2689
2690 /*
2691 * Write entry in the BAT.
2692 */
2693 rc = vhdFileWriteMetaAsync(pImage,
2694 pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
2695 &pExpand->idxBlockBe,
2696 sizeof(uint32_t), pIoCtx,
2697 vhdAsyncExpansionBatUpdateComplete,
2698 pExpand);
2699 if (RT_SUCCESS(rc))
2700 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2701 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2702 fIoInProgress = true;
2703 else
2704 {
2705 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2706 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2707 break;
2708 }
2709
2710 /*
2711 * Set the new end of the file and link the new block into the BAT.
2712 */
2713 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2714 pImage->uCurrentEndOfFile += pImage->cbDataBlockBitmap + pImage->cbDataBlock;
2715
2716 /* Update the footer. */
2717 rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
2718 &pImage->vhdFooterCopy,
2719 sizeof(VHDFooter), pIoCtx,
2720 vhdAsyncExpansionFooterUpdateComplete,
2721 pExpand);
2722 if (RT_SUCCESS(rc))
2723 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2724 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2725 fIoInProgress = true;
2726 else
2727 {
2728 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2729 break;
2730 }
2731
2732 } while (0);
2733
2734 if (!fIoInProgress)
2735 vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
2736 else
2737 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2738 }
2739 else
2740 {
2741 /*
2742 * Calculate the real offset in the file.
2743 */
2744 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2745
2746 /* Read in the block's bitmap. */
2747 PVDMETAXFER pMetaXfer;
2748 rc = vhdFileReadMetaAsync(pImage,
2749 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2750 pImage->pu8Bitmap,
2751 pImage->cbDataBlockBitmap, pIoCtx,
2752 &pMetaXfer, NULL, NULL);
2753 if (RT_SUCCESS(rc))
2754 {
2755 vhdFileMetaXferRelease(pImage, pMetaXfer);
2756
2757 /* Write data. */
2758 rc = vhdFileWriteUserAsync(pImage, uVhdOffset, pIoCtx, cbWrite,
2759 NULL, NULL);
2760 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2761 {
2762 bool fChanged = false;
2763
2764 /* Set the bits for all sectors having been written. */
2765 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2766 {
2767 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
2768 cBATEntryIndex++;
2769 }
2770
2771 /* Only write the bitmap if it was changed. */
2772 if (fChanged)
2773 {
2774 /*
2775 * Write the bitmap back.
2776 *
2777 * @note We don't have a completion callback here because we
2778 * can't do anything if the write fails for some reason.
2779 * The error will propagated to the device/guest
2780 * by the generic VD layer already and we don't need
2781 * to rollback anything here.
2782 */
2783 rc = vhdFileWriteMetaAsync(pImage,
2784 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2785 pImage->pu8Bitmap,
2786 pImage->cbDataBlockBitmap,
2787 pIoCtx, NULL, NULL);
2788 }
2789 }
2790 }
2791 }
2792 }
2793 else
2794 {
2795 rc = vhdFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
2796 }
2797
2798 if (pcbWriteProcess)
2799 *pcbWriteProcess = cbWrite;
2800
2801 /* Stay on the safe side. Do not run the risk of confusing the higher
2802 * level, as that can be pretty lethal to image consistency. */
2803 *pcbPreRead = 0;
2804 *pcbPostRead = 0;
2805
2806 return rc;
2807}
2808
2809/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
2810static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
2811{
2812 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2813
2814 /* No need to write anything here. Data is always updated on a write. */
2815 return vhdFileFlushAsync(pImage, pIoCtx, NULL, NULL);
2816}
2817
2818/** @copydoc VBOXHDDBACKEND::pfnResize */
2819static int vhdResize(void *pBackendData, uint64_t cbSize,
2820 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2821 unsigned uPercentStart, unsigned uPercentSpan,
2822 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2823 PVDINTERFACE pVDIfsOperation)
2824{
2825 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2826 int rc = VINF_SUCCESS;
2827
2828 PFNVDPROGRESS pfnProgress = NULL;
2829 void *pvUser = NULL;
2830 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
2831 VDINTERFACETYPE_PROGRESS);
2832 PVDINTERFACEPROGRESS pCbProgress = NULL;
2833 if (pIfProgress)
2834 {
2835 pCbProgress = VDGetInterfaceProgress(pIfProgress);
2836 if (pCbProgress)
2837 pfnProgress = pCbProgress->pfnProgress;
2838 pvUser = pIfProgress->pvUser;
2839 }
2840
2841 /* Making the image smaller is not supported at the moment. */
2842 if ( cbSize < pImage->cbSize
2843 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2844 rc = VERR_NOT_SUPPORTED;
2845 else if (cbSize > pImage->cbSize)
2846 {
2847 unsigned cBlocksAllocated = 0;
2848 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of a block including the sector bitmap. */
2849 uint32_t cBlocksNew = cbSize / pImage->cbDataBlock; /** < New number of blocks in the image after the resize */
2850 if (cbSize % pImage->cbDataBlock)
2851 cBlocksNew++;
2852
2853 uint32_t cBlocksOld = pImage->cBlockAllocationTableEntries; /** < Number of blocks before the resize. */
2854 uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE); /** < Required space for the block array after the resize. */
2855 uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
2856 uint64_t offStartDataOld = ~0ULL;
2857
2858 /* Go through the BAT and finde the data start offset. */
2859 for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
2860 {
2861 if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
2862 {
2863 uint64_t offStartBlock = pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
2864 if (offStartBlock < offStartDataOld)
2865 offStartDataOld = offStartBlock;
2866 cBlocksAllocated++;
2867 }
2868 }
2869
2870 if ( offStartDataOld != offStartDataNew
2871 && cBlocksAllocated > 0)
2872 {
2873 /* Calculate how many sectors nee to be relocated. */
2874 uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
2875 unsigned cBlocksReloc = cbOverlapping / cbBlock;
2876 if (cbOverlapping % cbBlock)
2877 cBlocksReloc++;
2878
2879 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2880 offStartDataNew = offStartDataOld;
2881
2882 /* Do the relocation. */
2883 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2884
2885 /*
2886 * Get the blocks we need to relocate first, they are appended to the end
2887 * of the image.
2888 */
2889 void *pvBuf = NULL, *pvZero = NULL;
2890 do
2891 {
2892 /* Allocate data buffer. */
2893 pvBuf = RTMemAllocZ(cbBlock);
2894 if (!pvBuf)
2895 {
2896 rc = VERR_NO_MEMORY;
2897 break;
2898 }
2899
2900 /* Allocate buffer for overwrting with zeroes. */
2901 pvZero = RTMemAllocZ(cbBlock);
2902 if (!pvZero)
2903 {
2904 rc = VERR_NO_MEMORY;
2905 break;
2906 }
2907
2908 for (unsigned i = 0; i < cBlocksReloc; i++)
2909 {
2910 uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
2911
2912 /* Search the index in the block table. */
2913 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2914 {
2915 if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
2916 {
2917 /* Read data and append to the end of the image. */
2918 rc = vhdFileReadSync(pImage, offStartDataNew, pvBuf, cbBlock, NULL);
2919 if (RT_FAILURE(rc))
2920 break;
2921
2922 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
2923 if (RT_FAILURE(rc))
2924 break;
2925
2926 /* Zero out the old block area. */
2927 rc = vhdFileWriteSync(pImage, offStartDataNew, pvZero, cbBlock, NULL);
2928 if (RT_FAILURE(rc))
2929 break;
2930
2931 /* Update block counter. */
2932 pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2933
2934 pImage->uCurrentEndOfFile += cbBlock;
2935
2936 /* Continue with the next block. */
2937 break;
2938 }
2939 }
2940
2941 if (RT_FAILURE(rc))
2942 break;
2943
2944 offStartDataNew += cbBlock;
2945 }
2946 } while (0);
2947
2948 if (pvBuf)
2949 RTMemFree(pvBuf);
2950 if (pvZero)
2951 RTMemFree(pvZero);
2952 }
2953
2954 /*
2955 * Relocation done, expand the block array and update the header with
2956 * the new data.
2957 */
2958 if (RT_SUCCESS(rc))
2959 {
2960 uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
2961 if (paBlocksNew)
2962 {
2963 pImage->pBlockAllocationTable = paBlocksNew;
2964
2965 /* Mark the new blocks as unallocated. */
2966 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2967 pImage->pBlockAllocationTable[idxBlock] = ~0U;
2968 }
2969 else
2970 rc = VERR_NO_MEMORY;
2971
2972 if (RT_SUCCESS(rc))
2973 {
2974 /* Write the block array before updating the rest. */
2975 rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset, pImage->pBlockAllocationTable,
2976 cBlocksNew * sizeof(uint32_t), NULL);
2977 }
2978
2979 if (RT_SUCCESS(rc))
2980 {
2981 /* Update size and new block count. */
2982 pImage->cBlockAllocationTableEntries = cBlocksNew;
2983 pImage->cbSize = cbSize;
2984
2985 /* Update geometry. */
2986 pImage->PCHSGeometry = *pPCHSGeometry;
2987 pImage->LCHSGeometry = *pLCHSGeometry;
2988 }
2989 }
2990
2991 /* Update header information in base image file. */
2992 pImage->fDynHdrNeedsUpdate = true;
2993 vhdFlush(pImage);
2994 }
2995 /* Same size doesn't change the image at all. */
2996
2997 LogFlowFunc(("returns %Rrc\n", rc));
2998 return rc;
2999}
3000
3001
3002VBOXHDDBACKEND g_VhdBackend =
3003{
3004 /* pszBackendName */
3005 "VHD",
3006 /* cbSize */
3007 sizeof(VBOXHDDBACKEND),
3008 /* uBackendCaps */
3009 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
3010 VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
3011 VD_CAP_ASYNC | VD_CAP_VFS,
3012 /* papszFileExtensions */
3013 s_apszVhdFileExtensions,
3014 /* paConfigInfo */
3015 NULL,
3016 /* hPlugin */
3017 NIL_RTLDRMOD,
3018 /* pfnCheckIfValid */
3019 vhdCheckIfValid,
3020 /* pfnOpen */
3021 vhdOpen,
3022 /* pfnCreate */
3023 vhdCreate,
3024 /* pfnRename */
3025 vhdRename,
3026 /* pfnClose */
3027 vhdClose,
3028 /* pfnRead */
3029 vhdRead,
3030 /* pfnWrite */
3031 vhdWrite,
3032 /* pfnFlush */
3033 vhdFlush,
3034 /* pfnGetVersion */
3035 vhdGetVersion,
3036 /* pfnGetSize */
3037 vhdGetSize,
3038 /* pfnGetFileSize */
3039 vhdGetFileSize,
3040 /* pfnGetPCHSGeometry */
3041 vhdGetPCHSGeometry,
3042 /* pfnSetPCHSGeometry */
3043 vhdSetPCHSGeometry,
3044 /* pfnGetLCHSGeometry */
3045 vhdGetLCHSGeometry,
3046 /* pfnSetLCHSGeometry */
3047 vhdSetLCHSGeometry,
3048 /* pfnGetImageFlags */
3049 vhdGetImageFlags,
3050 /* pfnGetOpenFlags */
3051 vhdGetOpenFlags,
3052 /* pfnSetOpenFlags */
3053 vhdSetOpenFlags,
3054 /* pfnGetComment */
3055 vhdGetComment,
3056 /* pfnSetComment */
3057 vhdSetComment,
3058 /* pfnGetUuid */
3059 vhdGetUuid,
3060 /* pfnSetUuid */
3061 vhdSetUuid,
3062 /* pfnGetModificationUuid */
3063 vhdGetModificationUuid,
3064 /* pfnSetModificationUuid */
3065 vhdSetModificationUuid,
3066 /* pfnGetParentUuid */
3067 vhdGetParentUuid,
3068 /* pfnSetParentUuid */
3069 vhdSetParentUuid,
3070 /* pfnGetParentModificationUuid */
3071 vhdGetParentModificationUuid,
3072 /* pfnSetParentModificationUuid */
3073 vhdSetParentModificationUuid,
3074 /* pfnDump */
3075 vhdDump,
3076 /* pfnGetTimeStamp */
3077 vhdGetTimeStamp,
3078 /* pfnGetParentTimeStamp */
3079 vhdGetParentTimeStamp,
3080 /* pfnSetParentTimeStamp */
3081 vhdSetParentTimeStamp,
3082 /* pfnGetParentFilename */
3083 vhdGetParentFilename,
3084 /* pfnSetParentFilename */
3085 vhdSetParentFilename,
3086 /* pfnIsAsyncIOSupported */
3087 vhdIsAsyncIOSupported,
3088 /* pfnAsyncRead */
3089 vhdAsyncRead,
3090 /* pfnAsyncWrite */
3091 vhdAsyncWrite,
3092 /* pfnAsyncFlush */
3093 vhdAsyncFlush,
3094 /* pfnComposeLocation */
3095 genericFileComposeLocation,
3096 /* pfnComposeName */
3097 genericFileComposeName,
3098 /* pfnCompact */
3099 NULL,
3100 /* pfnResize */
3101 vhdResize
3102};
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