VirtualBox

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

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

Storage: Implement offical support for other disk types like DVD and floppy images. DMG images can be used now without hacks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.7 KB
Line 
1/* $Id: VHDHDDCore.cpp 33524 2010-10-27 16:44:37Z vboxsync $ */
2/** @file
3 * VHD Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD_VHD
22#include <VBox/VBoxHDD-Plugin.h>
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <VBox/version.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/mem.h>
30#include <iprt/uuid.h>
31#include <iprt/path.h>
32#include <iprt/string.h>
33
34#define VHD_RELATIVE_MAX_PATH 512
35#define VHD_ABSOLUTE_MAX_PATH 512
36
37#define VHD_SECTOR_SIZE 512
38#define VHD_BLOCK_SIZE (2 * _1M)
39
40/* This is common to all VHD disk types and is located at the end of the image */
41#pragma pack(1)
42typedef struct VHDFooter
43{
44 char Cookie[8];
45 uint32_t Features;
46 uint32_t Version;
47 uint64_t DataOffset;
48 uint32_t TimeStamp;
49 uint8_t CreatorApp[4];
50 uint32_t CreatorVer;
51 uint32_t CreatorOS;
52 uint64_t OrigSize;
53 uint64_t CurSize;
54 uint16_t DiskGeometryCylinder;
55 uint8_t DiskGeometryHeads;
56 uint8_t DiskGeometrySectors;
57 uint32_t DiskType;
58 uint32_t Checksum;
59 char UniqueID[16];
60 uint8_t SavedState;
61 uint8_t Reserved[427];
62} VHDFooter;
63#pragma pack()
64
65#define VHD_FOOTER_COOKIE "conectix"
66#define VHD_FOOTER_COOKIE_SIZE 8
67
68#define VHD_FOOTER_FEATURES_NOT_ENABLED 0
69#define VHD_FOOTER_FEATURES_TEMPORARY 1
70#define VHD_FOOTER_FEATURES_RESERVED 2
71
72#define VHD_FOOTER_FILE_FORMAT_VERSION 0x00010000
73#define VHD_FOOTER_DATA_OFFSET_FIXED UINT64_C(0xffffffffffffffff)
74#define VHD_FOOTER_DISK_TYPE_FIXED 2
75#define VHD_FOOTER_DISK_TYPE_DYNAMIC 3
76#define VHD_FOOTER_DISK_TYPE_DIFFERENCING 4
77
78#define VHD_MAX_LOCATOR_ENTRIES 8
79#define VHD_PLATFORM_CODE_NONE 0
80#define VHD_PLATFORM_CODE_WI2R 0x57693272
81#define VHD_PLATFORM_CODE_WI2K 0x5769326B
82#define VHD_PLATFORM_CODE_W2RU 0x57327275
83#define VHD_PLATFORM_CODE_W2KU 0x57326B75
84#define VHD_PLATFORM_CODE_MAC 0x4D163220
85#define VHD_PLATFORM_CODE_MACX 0x4D163258
86
87/* Header for expanding disk images. */
88#pragma pack(1)
89typedef struct VHDParentLocatorEntry
90{
91 uint32_t u32Code;
92 uint32_t u32DataSpace;
93 uint32_t u32DataLength;
94 uint32_t u32Reserved;
95 uint64_t u64DataOffset;
96} VHDPLE, *PVHDPLE;
97
98typedef struct VHDDynamicDiskHeader
99{
100 char Cookie[8];
101 uint64_t DataOffset;
102 uint64_t TableOffset;
103 uint32_t HeaderVersion;
104 uint32_t MaxTableEntries;
105 uint32_t BlockSize;
106 uint32_t Checksum;
107 uint8_t ParentUuid[16];
108 uint32_t ParentTimeStamp;
109 uint32_t Reserved0;
110 uint16_t ParentUnicodeName[256];
111 VHDPLE ParentLocatorEntry[VHD_MAX_LOCATOR_ENTRIES];
112 uint8_t Reserved1[256];
113} VHDDynamicDiskHeader;
114#pragma pack()
115
116#define VHD_DYNAMIC_DISK_HEADER_COOKIE "cxsparse"
117#define VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE 8
118#define VHD_DYNAMIC_DISK_HEADER_VERSION 0x00010000
119
120/**
121 * Complete VHD image data structure.
122 */
123typedef struct VHDIMAGE
124{
125 /** Image file name. */
126 const char *pszFilename;
127 /** Opaque storage handle. */
128 PVDIOSTORAGE pStorage;
129
130 /** I/O interface. */
131 PVDINTERFACE pInterfaceIO;
132 /** I/O interface callbacks. */
133 PVDINTERFACEIOINT pInterfaceIOCallbacks;
134
135 /** Pointer to the per-disk VD interface list. */
136 PVDINTERFACE pVDIfsDisk;
137 /** Pointer to the per-image VD interface list. */
138 PVDINTERFACE pVDIfsImage;
139 /** Error interface. */
140 PVDINTERFACE pInterfaceError;
141 /** Error interface callback table. */
142 PVDINTERFACEERROR pInterfaceErrorCallbacks;
143
144 /** Open flags passed by VBoxHDD layer. */
145 unsigned uOpenFlags;
146 /** Image flags defined during creation or determined during open. */
147 unsigned uImageFlags;
148 /** Total size of the image. */
149 uint64_t cbSize;
150
151 /** Physical geometry of this image. */
152 VDGEOMETRY PCHSGeometry;
153 /** Logical geometry of this image. */
154 VDGEOMETRY LCHSGeometry;
155
156 /** Image UUID. */
157 RTUUID ImageUuid;
158 /** Parent image UUID. */
159 RTUUID ParentUuid;
160
161 /** Parent's time stamp at the time of image creation. */
162 uint32_t u32ParentTimeStamp;
163 /** Relative path to the parent image. */
164 char *pszParentFilename;
165
166 /** The Block Allocation Table. */
167 uint32_t *pBlockAllocationTable;
168 /** Number of entries in the table. */
169 uint32_t cBlockAllocationTableEntries;
170
171 /** Size of one data block. */
172 uint32_t cbDataBlock;
173 /** Sectors per data block. */
174 uint32_t cSectorsPerDataBlock;
175 /** Length of the sector bitmap in bytes. */
176 uint32_t cbDataBlockBitmap;
177 /** A copy of the disk footer. */
178 VHDFooter vhdFooterCopy;
179 /** Current end offset of the file (without the disk footer). */
180 uint64_t uCurrentEndOfFile;
181 /** Size of the data block bitmap in sectors. */
182 uint32_t cDataBlockBitmapSectors;
183 /** Start of the block allocation table. */
184 uint64_t uBlockAllocationTableOffset;
185 /** Buffer to hold block's bitmap for bit search operations. */
186 uint8_t *pu8Bitmap;
187 /** Offset to the next data structure (dynamic disk header). */
188 uint64_t u64DataOffset;
189 /** Flag to force dynamic disk header update. */
190 bool fDynHdrNeedsUpdate;
191} VHDIMAGE, *PVHDIMAGE;
192
193/**
194 * Structure tracking the expansion process of the image
195 * for async access.
196 */
197typedef struct VHDIMAGEEXPAND
198{
199 /** Flag indicating the status of each step. */
200 volatile uint32_t fFlags;
201 /** The index in the block allocation table which is written. */
202 uint32_t idxBatAllocated;
203 /** Big endian representation of the block index
204 * which is written in the BAT. */
205 uint32_t idxBlockBe;
206 /** Old end of the file - used for rollback in case of an error. */
207 uint64_t cbEofOld;
208 /** Sector bitmap written to the new block - variable in size. */
209 uint8_t au8Bitmap[1];
210} VHDIMAGEEXPAND, *PVHDIMAGEEXPAND;
211
212/**
213 * Flag defines
214 */
215#define VHDIMAGEEXPAND_STEP_IN_PROGRESS (0x0)
216#define VHDIMAGEEXPAND_STEP_FAILED (0x2)
217#define VHDIMAGEEXPAND_STEP_SUCCESS (0x3)
218/** All steps completed successfully. */
219#define VHDIMAGEEXPAND_ALL_SUCCESS (0xff)
220/** All steps completed (no success indicator) */
221#define VHDIMAGEEXPAND_ALL_COMPLETE (0xaa)
222
223/** Every status field has 2 bits so we can encode 4 steps in one byte. */
224#define VHDIMAGEEXPAND_STATUS_MASK 0x03
225#define VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT 0x00
226#define VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT 0x02
227#define VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT 0x04
228#define VHDIMAGEEXPAND_BAT_STATUS_SHIFT 0x06
229
230/**
231 * Helper macros to get and set the status field.
232 */
233#define VHDIMAGEEXPAND_STATUS_GET(fFlags, cShift) \
234 (((fFlags) >> (cShift)) & VHDIMAGEEXPAND_STATUS_MASK)
235#define VHDIMAGEEXPAND_STATUS_SET(fFlags, cShift, uVal) \
236 ASMAtomicOrU32(&(fFlags), ((uVal) & VHDIMAGEEXPAND_STATUS_MASK) << (cShift))
237
238/*******************************************************************************
239* Static Variables *
240*******************************************************************************/
241
242/** NULL-terminated array of supported file extensions. */
243static const VDFILEEXTENSION s_aVhdFileExtensions[] =
244{
245 {"vhd", VDTYPE_HDD},
246 {NULL, VDTYPE_INVALID}
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 (!pszTmp)
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 {
925 RTMemFree(pBlockAllocationTable);
926 return VERR_NO_MEMORY;
927 }
928
929 for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
930 pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
931
932 RTMemFree(pBlockAllocationTable);
933
934 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
935 memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
936
937 return rc;
938}
939
940static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
941{
942 uint64_t FileSize;
943 VHDFooter vhdFooter;
944
945 pImage->uOpenFlags = uOpenFlags;
946
947 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
948 if (pImage->pInterfaceError)
949 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
950
951 /* Get I/O interface. */
952 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
953 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
954 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
955 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
956
957 /*
958 * Open the image.
959 */
960 int rc = vhdFileOpen(pImage, pImage->pszFilename,
961 VDOpenFlagsToFileOpenFlags(uOpenFlags,
962 false /* fCreate */));
963 if (RT_FAILURE(rc))
964 {
965 /* Do NOT signal an appropriate error here, as the VD layer has the
966 * choice of retrying the open if it failed. */
967 return rc;
968 }
969
970 rc = vhdFileGetSize(pImage, &FileSize);
971 pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
972
973 rc = vhdFileReadSync(pImage, pImage->uCurrentEndOfFile,
974 &vhdFooter, sizeof(VHDFooter), NULL);
975 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
976 return VERR_VD_VHD_INVALID_HEADER;
977
978 switch (RT_BE2H_U32(vhdFooter.DiskType))
979 {
980 case VHD_FOOTER_DISK_TYPE_FIXED:
981 {
982 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
983 }
984 break;
985 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
986 {
987 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
988 }
989 break;
990 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
991 {
992 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
993 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
994 }
995 break;
996 default:
997 return VERR_NOT_IMPLEMENTED;
998 }
999
1000 pImage->cbSize = RT_BE2H_U64(vhdFooter.CurSize);
1001 pImage->LCHSGeometry.cCylinders = 0;
1002 pImage->LCHSGeometry.cHeads = 0;
1003 pImage->LCHSGeometry.cSectors = 0;
1004 pImage->PCHSGeometry.cCylinders = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
1005 pImage->PCHSGeometry.cHeads = vhdFooter.DiskGeometryHeads;
1006 pImage->PCHSGeometry.cSectors = vhdFooter.DiskGeometrySectors;
1007
1008 /*
1009 * Copy of the disk footer.
1010 * If we allocate new blocks in differencing disks on write access
1011 * the footer is overwritten. We need to write it at the end of the file.
1012 */
1013 memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
1014
1015 /*
1016 * Is there a better way?
1017 */
1018 memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
1019
1020 pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
1021 LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
1022
1023 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
1024 rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
1025
1026 if (RT_FAILURE(rc))
1027 vhdFreeImage(pImage, false);
1028 return rc;
1029}
1030
1031/**
1032 * Internal: Checks if a sector in the block bitmap is set
1033 */
1034DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
1035{
1036 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
1037
1038 /*
1039 * The index of the bit in the byte of the data block bitmap.
1040 * The most signifcant bit stands for a lower sector number.
1041 */
1042 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
1043 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
1044
1045 AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
1046 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
1047
1048 return ASMBitTest(puBitmap, iBitInByte);
1049}
1050
1051/**
1052 * Internal: Sets the given sector in the sector bitmap.
1053 */
1054DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
1055{
1056 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
1057
1058 /*
1059 * The index of the bit in the byte of the data block bitmap.
1060 * The most significant bit stands for a lower sector number.
1061 */
1062 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
1063 uint8_t *puBitmap = pu8Bitmap + iBitmap;
1064
1065 AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
1066 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
1067
1068 return !ASMBitTestAndSet(puBitmap, iBitInByte);
1069}
1070
1071/**
1072 * Internal: Derive drive geometry from its size.
1073 */
1074static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
1075{
1076 uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
1077 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
1078
1079 if (u64TotalSectors > 65535 * 16 * 255)
1080 {
1081 /* ATA disks limited to 127 GB. */
1082 u64TotalSectors = 65535 * 16 * 255;
1083 }
1084
1085 if (u64TotalSectors >= 65535 * 16 * 63)
1086 {
1087 u32SectorsPerTrack = 255;
1088 u32Heads = 16;
1089 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1090 }
1091 else
1092 {
1093 u32SectorsPerTrack = 17;
1094 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1095
1096 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
1097
1098 if (u32Heads < 4)
1099 {
1100 u32Heads = 4;
1101 }
1102 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
1103 {
1104 u32SectorsPerTrack = 31;
1105 u32Heads = 16;
1106 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1107 }
1108 if (u32CylinderTimesHeads >= (u32Heads * 1024))
1109 {
1110 u32SectorsPerTrack = 63;
1111 u32Heads = 16;
1112 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1113 }
1114 }
1115 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
1116 pImage->PCHSGeometry.cHeads = u32Heads;
1117 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
1118 pImage->LCHSGeometry.cCylinders = 0;
1119 pImage->LCHSGeometry.cHeads = 0;
1120 pImage->LCHSGeometry.cSectors = 0;
1121}
1122
1123
1124static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
1125{
1126 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
1127 /* Relative Windows path. */
1128 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
1129 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
1130 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1131 u64Offset += VHD_RELATIVE_MAX_PATH;
1132 pLocator++;
1133 /* Absolute Windows path. */
1134 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
1135 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
1136 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1137 u64Offset += VHD_ABSOLUTE_MAX_PATH;
1138 pLocator++;
1139 /* Unicode relative Windows path. */
1140 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
1141 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1142 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1143 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
1144 pLocator++;
1145 /* Unicode absolute Windows path. */
1146 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
1147 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1148 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1149 return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
1150}
1151
1152/**
1153 * Internal: Additional code for dynamic VHD image creation.
1154 */
1155static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
1156{
1157 int rc;
1158 VHDDynamicDiskHeader DynamicDiskHeader;
1159 uint32_t u32BlockAllocationTableSectors;
1160 void *pvTmp = NULL;
1161
1162 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
1163
1164 pImage->u64DataOffset = sizeof(VHDFooter);
1165 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1166 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1167 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1168 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1169 /* Align to sector boundary */
1170 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
1171 pImage->cDataBlockBitmapSectors++;
1172 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
1173 if (!pImage->pu8Bitmap)
1174 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1175
1176 /* Initialize BAT. */
1177 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1178 pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
1179 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1180 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1181 if (!pImage->pBlockAllocationTable)
1182 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1183
1184 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1185 {
1186 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1187 }
1188
1189 /* Round up to the sector size. */
1190 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
1191 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1192 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1193 else
1194 pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
1195
1196 /* Set dynamic image size. */
1197 pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1198 if (!pvTmp)
1199 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1200
1201 rc = vhdFileWriteSync(pImage, 0, pvTmp,
1202 pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
1203 if (RT_FAILURE(rc))
1204 {
1205 RTMemTmpFree(pvTmp);
1206 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1207 }
1208
1209 RTMemTmpFree(pvTmp);
1210
1211 /* Initialize and write the dynamic disk header. */
1212 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1213 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1214 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1215 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1216 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1217 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1218 /* Compute and update checksum. */
1219 DynamicDiskHeader.Checksum = 0;
1220 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1221
1222 rc = vhdFileWriteSync(pImage, sizeof(VHDFooter), &DynamicDiskHeader,
1223 sizeof(DynamicDiskHeader), NULL);
1224 if (RT_FAILURE(rc))
1225 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1226
1227 /* Write BAT. */
1228 rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
1229 pImage->pBlockAllocationTable,
1230 pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
1231 NULL);
1232 if (RT_FAILURE(rc))
1233 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1234
1235 return rc;
1236}
1237
1238/**
1239 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1240 */
1241static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
1242 unsigned uImageFlags, const char *pszComment,
1243 PCVDGEOMETRY pPCHSGeometry,
1244 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1245 unsigned uOpenFlags,
1246 PFNVDPROGRESS pfnProgress, void *pvUser,
1247 unsigned uPercentStart, unsigned uPercentSpan)
1248{
1249 int rc;
1250 VHDFooter Footer;
1251 RTTIMESPEC now;
1252
1253 pImage->uOpenFlags = uOpenFlags;
1254 pImage->uImageFlags = uImageFlags;
1255
1256 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
1257 if (pImage->pInterfaceError)
1258 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
1259
1260 rc = vhdFileOpen(pImage, pImage->pszFilename,
1261 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
1262 true /* fCreate */));
1263 if (RT_FAILURE(rc))
1264 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1265
1266
1267 pImage->cbSize = cbSize;
1268 pImage->ImageUuid = *pUuid;
1269 RTUuidClear(&pImage->ParentUuid);
1270 vhdSetDiskGeometry(pImage, cbSize);
1271
1272 /* Initialize the footer. */
1273 memset(&Footer, 0, sizeof(Footer));
1274 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1275 Footer.Features = RT_H2BE_U32(0x2);
1276 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1277 Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1278 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1279 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1280#ifdef RT_OS_DARWIN
1281 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1282#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1283 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1284#endif
1285 Footer.OrigSize = RT_H2BE_U64(cbSize);
1286 Footer.CurSize = Footer.OrigSize;
1287 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1288 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1289 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1290 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1291 Footer.SavedState = 0;
1292
1293 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1294 {
1295 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1296 /*
1297 * Initialize fixed image.
1298 * "The size of the entire file is the size of the hard disk in
1299 * the guest operating system plus the size of the footer."
1300 */
1301 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1302 pImage->uCurrentEndOfFile = cbSize;
1303 /** @todo r=klaus replace this with actual data writes, see the experience
1304 * with VDI files on Windows, can cause long freezes when writing. */
1305 rc = vhdFileSetSize(pImage, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1306 if (RT_FAILURE(rc))
1307 {
1308 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1309 goto out;
1310 }
1311 }
1312 else
1313 {
1314 /*
1315 * Initialize dynamic image.
1316 *
1317 * The overall structure of dynamic disk is:
1318 *
1319 * [Copy of hard disk footer (512 bytes)]
1320 * [Dynamic disk header (1024 bytes)]
1321 * [BAT (Block Allocation Table)]
1322 * [Parent Locators]
1323 * [Data block 1]
1324 * [Data block 2]
1325 * ...
1326 * [Data block N]
1327 * [Hard disk footer (512 bytes)]
1328 */
1329 Footer.DiskType = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
1330 ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
1331 : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1332 /* We are half way thourgh with creation of image, let the caller know. */
1333 if (pfnProgress)
1334 pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
1335
1336 rc = vhdCreateDynamicImage(pImage, cbSize);
1337 if (RT_FAILURE(rc))
1338 goto out;
1339 }
1340
1341 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1342
1343 /* Compute and update the footer checksum. */
1344 Footer.Checksum = 0;
1345 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1346
1347 pImage->vhdFooterCopy = Footer;
1348
1349 /* Store the footer */
1350 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, &Footer,
1351 sizeof(Footer), NULL);
1352 if (RT_FAILURE(rc))
1353 {
1354 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1355 goto out;
1356 }
1357
1358 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1359 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1360 {
1361 /* Write the copy of the footer. */
1362 rc = vhdFileWriteSync(pImage, 0, &Footer, sizeof(Footer), NULL);
1363 if (RT_FAILURE(rc))
1364 {
1365 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1366 goto out;
1367 }
1368 }
1369
1370out:
1371 if (RT_SUCCESS(rc) && pfnProgress)
1372 pfnProgress(pvUser, uPercentStart + uPercentSpan);
1373
1374 if (RT_FAILURE(rc))
1375 vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1376 return rc;
1377}
1378
1379
1380/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1381static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1382 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1383{
1384 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1385 int rc;
1386 PVDIOSTORAGE pStorage;
1387 uint64_t cbFile;
1388 VHDFooter vhdFooter;
1389
1390 /* Get I/O interface. */
1391 PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
1392 AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
1393 PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
1394 AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
1395
1396 rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
1397 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1398 false /* fCreate */),
1399 &pStorage);
1400 if (RT_FAILURE(rc))
1401 goto out;
1402
1403 rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
1404 &cbFile);
1405 if (RT_FAILURE(rc))
1406 {
1407 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
1408 rc = VERR_VD_VHD_INVALID_HEADER;
1409 goto out;
1410 }
1411
1412 rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
1413 cbFile - sizeof(VHDFooter),
1414 &vhdFooter, sizeof(VHDFooter), NULL);
1415 if (RT_FAILURE(rc) || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
1416 rc = VERR_VD_VHD_INVALID_HEADER;
1417 else
1418 {
1419 *penmType = VDTYPE_HDD;
1420 rc = VINF_SUCCESS;
1421 }
1422
1423 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
1424
1425out:
1426 LogFlowFunc(("returns %Rrc\n", rc));
1427 return rc;
1428}
1429
1430/** @copydoc VBOXHDDBACKEND::pfnOpen */
1431static int vhdOpen(const char *pszFilename, unsigned uOpenFlags,
1432 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1433 VDTYPE enmType, void **ppBackendData)
1434{
1435 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
1436 int rc = VINF_SUCCESS;
1437 PVHDIMAGE pImage;
1438
1439 /* Check open flags. All valid flags are supported. */
1440 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1441 {
1442 rc = VERR_INVALID_PARAMETER;
1443 goto out;
1444 }
1445
1446 /* Check remaining arguments. */
1447 if ( !VALID_PTR(pszFilename)
1448 || !*pszFilename)
1449 {
1450 rc = VERR_INVALID_PARAMETER;
1451 goto out;
1452 }
1453
1454 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1455 if (!pImage)
1456 {
1457 rc = VERR_NO_MEMORY;
1458 goto out;
1459 }
1460
1461 pImage->pszFilename = pszFilename;
1462 pImage->pStorage = NULL;
1463 pImage->pVDIfsDisk = pVDIfsDisk;
1464 pImage->pVDIfsImage = pVDIfsImage;
1465
1466 rc = vhdOpenImage(pImage, uOpenFlags);
1467 if (RT_SUCCESS(rc))
1468 *ppBackendData = pImage;
1469 else
1470 RTMemFree(pImage);
1471
1472out:
1473 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1474 return rc;
1475}
1476
1477/** @copydoc VBOXHDDBACKEND::pfnCreate */
1478static int vhdCreate(const char *pszFilename, uint64_t cbSize,
1479 unsigned uImageFlags, const char *pszComment,
1480 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1481 PCRTUUID pUuid, unsigned uOpenFlags,
1482 unsigned uPercentStart, unsigned uPercentSpan,
1483 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1484 PVDINTERFACE pVDIfsOperation, void **ppBackendData)
1485{
1486 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));
1487 int rc = VINF_SUCCESS;
1488 PVHDIMAGE pImage;
1489
1490 PFNVDPROGRESS pfnProgress = NULL;
1491 void *pvUser = NULL;
1492 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1493 VDINTERFACETYPE_PROGRESS);
1494 PVDINTERFACEPROGRESS pCbProgress = NULL;
1495 if (pIfProgress)
1496 {
1497 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1498 if (pCbProgress)
1499 pfnProgress = pCbProgress->pfnProgress;
1500 pvUser = pIfProgress->pvUser;
1501 }
1502
1503 /* Check open flags. All valid flags are supported. */
1504 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1505 {
1506 rc = VERR_INVALID_PARAMETER;
1507 return rc;
1508 }
1509
1510 /* @todo Check the values of other params */
1511
1512 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1513 if (!pImage)
1514 {
1515 rc = VERR_NO_MEMORY;
1516 return rc;
1517 }
1518 pImage->pszFilename = pszFilename;
1519 pImage->pStorage = NULL;
1520 pImage->pVDIfsDisk = pVDIfsDisk;
1521 pImage->pVDIfsImage = pVDIfsImage;
1522
1523 /* Get I/O interface. */
1524 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
1525 if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIO)))
1526 {
1527 RTMemFree(pImage);
1528 return VERR_INVALID_PARAMETER;
1529 }
1530 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
1531 if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIOCallbacks)))
1532 {
1533 RTMemFree(pImage);
1534 return VERR_INVALID_PARAMETER;
1535 }
1536
1537 rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
1538 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1539 pfnProgress, pvUser, uPercentStart, uPercentSpan);
1540
1541 if (RT_SUCCESS(rc))
1542 {
1543 /* So far the image is opened in read/write mode. Make sure the
1544 * image is opened in read-only mode if the caller requested that. */
1545 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1546 {
1547 vhdFreeImage(pImage, false);
1548 rc = vhdOpenImage(pImage, uOpenFlags);
1549 if (RT_FAILURE(rc))
1550 {
1551 RTMemFree(pImage);
1552 goto out;
1553 }
1554 }
1555 *ppBackendData = pImage;
1556 }
1557 else
1558 RTMemFree(pImage);
1559
1560out:
1561 LogFlowFunc(("returns %Rrc\n", rc));
1562 return rc;
1563}
1564
1565/** @copydoc VBOXHDDBACKEND::pfnRename */
1566static int vhdRename(void *pBackendData, const char *pszFilename)
1567{
1568 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1569 int rc = VINF_SUCCESS;
1570 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1571
1572 /* Check arguments. */
1573 if ( !pImage
1574 || !pszFilename
1575 || !*pszFilename)
1576 {
1577 rc = VERR_INVALID_PARAMETER;
1578 goto out;
1579 }
1580
1581 /* Close the image. */
1582 rc = vhdFreeImage(pImage, false);
1583 if (RT_FAILURE(rc))
1584 goto out;
1585
1586 /* Rename the file. */
1587 rc = vhdFileMove(pImage, pImage->pszFilename, pszFilename, 0);
1588 if (RT_FAILURE(rc))
1589 {
1590 /* The move failed, try to reopen the original image. */
1591 int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
1592 if (RT_FAILURE(rc2))
1593 rc = rc2;
1594
1595 goto out;
1596 }
1597
1598 /* Update pImage with the new information. */
1599 pImage->pszFilename = pszFilename;
1600
1601 /* Open the old file with new name. */
1602 rc = vhdOpenImage(pImage, pImage->uOpenFlags);
1603 if (RT_FAILURE(rc))
1604 goto out;
1605
1606out:
1607 LogFlowFunc(("returns %Rrc\n", rc));
1608 return rc;
1609}
1610
1611/** @copydoc VBOXHDDBACKEND::pfnClose */
1612static int vhdClose(void *pBackendData, bool fDelete)
1613{
1614 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1615 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1616 int rc;
1617
1618 rc = vhdFreeImage(pImage, fDelete);
1619 RTMemFree(pImage);
1620
1621 LogFlowFunc(("returns %Rrc\n", rc));
1622 return rc;
1623}
1624
1625/** @copydoc VBOXHDDBACKEND::pfnRead */
1626static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
1627 size_t cbBuf, size_t *pcbActuallyRead)
1628{
1629 LogFlowFunc(("pBackendData=%p uOffset=%#llx pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
1630 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1631 int rc = VINF_SUCCESS;
1632
1633 if (uOffset + cbBuf > pImage->cbSize)
1634 {
1635 rc = VERR_INVALID_PARAMETER;
1636 goto out;
1637 }
1638
1639 /*
1640 * If we have a dynamic disk image, we need to find the data block and sector to read.
1641 */
1642 if (pImage->pBlockAllocationTable)
1643 {
1644 /*
1645 * Get the data block first.
1646 */
1647 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
1648 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
1649 uint64_t uVhdOffset;
1650
1651 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
1652 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
1653
1654 /*
1655 * If the block is not allocated the content of the entry is ~0
1656 */
1657 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1658 {
1659 /* Return block size as read. */
1660 *pcbActuallyRead = RT_MIN(cbBuf, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
1661 rc = VERR_VD_BLOCK_FREE;
1662 goto out;
1663 }
1664
1665 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1666 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1667
1668 /*
1669 * Clip read range to remain in this data block.
1670 */
1671 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1672
1673 /* Read in the block's bitmap. */
1674 rc = vhdFileReadSync(pImage,
1675 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1676 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1677 NULL);
1678 if (RT_SUCCESS(rc))
1679 {
1680 uint32_t cSectors = 0;
1681
1682 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1683 {
1684 cBATEntryIndex++;
1685 cSectors = 1;
1686
1687 /*
1688 * The first sector being read is marked dirty, read as much as we
1689 * can from child. Note that only sectors that are marked dirty
1690 * must be read from child.
1691 */
1692 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1693 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1694 {
1695 cBATEntryIndex++;
1696 cSectors++;
1697 }
1698
1699 cbBuf = cSectors * VHD_SECTOR_SIZE;
1700
1701 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1702 rc = vhdFileReadSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
1703 }
1704 else
1705 {
1706 /*
1707 * The first sector being read is marked clean, so we should read from
1708 * our parent instead, but only as much as there are the following
1709 * clean sectors, because the block may still contain dirty sectors
1710 * further on. We just need to compute the number of clean sectors
1711 * and pass it to our caller along with the notification that they
1712 * should be read from the parent.
1713 */
1714 cBATEntryIndex++;
1715 cSectors = 1;
1716
1717 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1718 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1719 {
1720 cBATEntryIndex++;
1721 cSectors++;
1722 }
1723
1724 cbBuf = cSectors * VHD_SECTOR_SIZE;
1725 LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1726 rc = VERR_VD_BLOCK_FREE;
1727 }
1728 }
1729 else
1730 AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
1731 }
1732 else
1733 {
1734 rc = vhdFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
1735 }
1736
1737 if (RT_SUCCESS(rc))
1738 {
1739 if (pcbActuallyRead)
1740 *pcbActuallyRead = cbBuf;
1741
1742 Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n"
1743 "%.*Rhxd\n",
1744 uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
1745 }
1746
1747out:
1748 LogFlowFunc(("returns %Rrc\n", rc));
1749 return rc;
1750}
1751
1752/** @copydoc VBOXHDDBACKEND::pfnWrite */
1753static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
1754 size_t cbBuf, size_t *pcbWriteProcess,
1755 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
1756{
1757 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
1758 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1759 int rc = VINF_SUCCESS;
1760
1761 LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1762 pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
1763
1764 AssertPtr(pImage);
1765 Assert(uOffset % VHD_SECTOR_SIZE == 0);
1766 Assert(cbBuf % VHD_SECTOR_SIZE == 0);
1767
1768 if (pImage->pBlockAllocationTable)
1769 {
1770 /*
1771 * Get the data block first.
1772 */
1773 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
1774 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1775 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1776 uint64_t uVhdOffset;
1777
1778 /*
1779 * Clip write range.
1780 */
1781 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1782
1783 /*
1784 * If the block is not allocated the content of the entry is ~0
1785 * and we need to allocate a new block. Note that while blocks are
1786 * allocated with a relatively big granularity, each sector has its
1787 * own bitmap entry, indicating whether it has been written or not.
1788 * So that means for the purposes of the higher level that the
1789 * granularity is invisible. This means there's no need to return
1790 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
1791 */
1792 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1793 {
1794 /* Check if the block allocation should be suppressed. */
1795 if (fWrite & VD_WRITE_NO_ALLOC)
1796 {
1797 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
1798 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
1799
1800 if (pcbWriteProcess)
1801 *pcbWriteProcess = cbBuf;
1802 rc = VERR_VD_BLOCK_FREE;
1803 goto out;
1804 }
1805
1806 size_t cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
1807 uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
1808
1809 if (!pNewBlock)
1810 {
1811 rc = VERR_NO_MEMORY;
1812 goto out;
1813 }
1814
1815 /*
1816 * Write the new block at the current end of the file.
1817 */
1818 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
1819 pNewBlock, cbNewBlock, NULL);
1820 AssertRC(rc);
1821
1822 /*
1823 * Set the new end of the file and link the new block into the BAT.
1824 */
1825 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
1826 pImage->uCurrentEndOfFile += cbNewBlock;
1827 RTMemFree(pNewBlock);
1828
1829 /* Write the updated BAT and the footer to remain in a consistent state. */
1830 rc = vhdFlushImage(pImage);
1831 AssertRC(rc);
1832 }
1833
1834 /*
1835 * Calculate the real offset in the file.
1836 */
1837 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1838
1839 /* Write data. */
1840 vhdFileWriteSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
1841
1842 /* Read in the block's bitmap. */
1843 rc = vhdFileReadSync(pImage,
1844 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1845 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1846 NULL);
1847 if (RT_SUCCESS(rc))
1848 {
1849 bool fChanged = false;
1850
1851 /* Set the bits for all sectors having been written. */
1852 for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
1853 {
1854 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
1855 cBATEntryIndex++;
1856 }
1857
1858 if (fChanged)
1859 {
1860 /* Write the bitmap back. */
1861 rc = vhdFileWriteSync(pImage,
1862 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1863 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1864 NULL);
1865 }
1866 }
1867 }
1868 else
1869 {
1870 rc = vhdFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
1871 }
1872
1873 if (pcbWriteProcess)
1874 *pcbWriteProcess = cbBuf;
1875
1876 /* Stay on the safe side. Do not run the risk of confusing the higher
1877 * level, as that can be pretty lethal to image consistency. */
1878 *pcbPreRead = 0;
1879 *pcbPostRead = 0;
1880
1881out:
1882 LogFlowFunc(("returns %Rrc\n", rc));
1883 return rc;
1884}
1885
1886/** @copydoc VBOXHDDBACKEND::pfnFlush */
1887static int vhdFlush(void *pBackendData)
1888{
1889 LogFlowFunc(("pBackendData=%#p", pBackendData));
1890 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1891 int rc;
1892
1893 rc = vhdFlushImage(pImage);
1894 LogFlowFunc(("returns %Rrc\n", rc));
1895 return rc;
1896}
1897
1898/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1899static unsigned vhdGetVersion(void *pBackendData)
1900{
1901 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1902 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1903 unsigned ver = 0;
1904
1905 AssertPtr(pImage);
1906
1907 if (pImage)
1908 ver = 1; /**< @todo use correct version */
1909
1910 LogFlowFunc(("returns %u\n", ver));
1911 return ver;
1912}
1913
1914/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1915static uint64_t vhdGetSize(void *pBackendData)
1916{
1917 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1918 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1919 uint64_t cb = 0;
1920
1921 AssertPtr(pImage);
1922
1923 if (pImage && pImage->pStorage)
1924 cb = pImage->cbSize;
1925
1926 LogFlowFunc(("returns %llu\n", cb));
1927 return cb;
1928}
1929
1930/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1931static uint64_t vhdGetFileSize(void *pBackendData)
1932{
1933 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1934 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1935 uint64_t cb = 0;
1936
1937 AssertPtr(pImage);
1938
1939 if (pImage && pImage->pStorage)
1940 cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
1941
1942 LogFlowFunc(("returns %lld\n", cb));
1943 return cb;
1944}
1945
1946/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1947static int vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1948{
1949 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1950 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1951 int rc;
1952
1953 AssertPtr(pImage);
1954
1955 if (pImage)
1956 {
1957 if (pImage->PCHSGeometry.cCylinders)
1958 {
1959 *pPCHSGeometry = pImage->PCHSGeometry;
1960 rc = VINF_SUCCESS;
1961 }
1962 else
1963 rc = VERR_VD_GEOMETRY_NOT_SET;
1964 }
1965 else
1966 rc = VERR_VD_NOT_OPENED;
1967
1968 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
1969 return rc;
1970}
1971
1972/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1973static int vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1974{
1975 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1976 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1977 int rc;
1978
1979 AssertPtr(pImage);
1980
1981 if (pImage)
1982 {
1983 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1984 {
1985 rc = VERR_VD_IMAGE_READ_ONLY;
1986 goto out;
1987 }
1988
1989 pImage->PCHSGeometry = *pPCHSGeometry;
1990 rc = VINF_SUCCESS;
1991 }
1992 else
1993 rc = VERR_VD_NOT_OPENED;
1994
1995out:
1996 LogFlowFunc(("returns %Rrc\n", rc));
1997 return rc;
1998}
1999
2000/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
2001static int vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
2002{
2003LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2004 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2005 int rc;
2006
2007 AssertPtr(pImage);
2008
2009 if (pImage)
2010 {
2011 if (pImage->LCHSGeometry.cCylinders)
2012 {
2013 *pLCHSGeometry = pImage->LCHSGeometry;
2014 rc = VINF_SUCCESS;
2015 }
2016 else
2017 rc = VERR_VD_GEOMETRY_NOT_SET;
2018 }
2019 else
2020 rc = VERR_VD_NOT_OPENED;
2021
2022 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
2023 return rc;
2024}
2025
2026/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
2027static int vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
2028{
2029 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2030 int rc;
2031
2032 AssertPtr(pImage);
2033
2034 if (pImage)
2035 {
2036 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2037 {
2038 rc = VERR_VD_IMAGE_READ_ONLY;
2039 goto out;
2040 }
2041
2042 pImage->LCHSGeometry = *pLCHSGeometry;
2043 rc = VINF_SUCCESS;
2044 }
2045 else
2046 rc = VERR_VD_NOT_OPENED;
2047
2048out:
2049 LogFlowFunc(("returns %Rrc\n", rc));
2050 return rc;
2051}
2052
2053/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
2054static unsigned vhdGetImageFlags(void *pBackendData)
2055{
2056 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2057 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2058 unsigned uImageFlags;
2059
2060 AssertPtr(pImage);
2061
2062 if (pImage)
2063 uImageFlags = pImage->uImageFlags;
2064 else
2065 uImageFlags = 0;
2066
2067 LogFlowFunc(("returns %#x\n", uImageFlags));
2068 return uImageFlags;
2069}
2070
2071/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
2072static unsigned vhdGetOpenFlags(void *pBackendData)
2073{
2074 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2075 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2076 unsigned uOpenFlags;
2077
2078 AssertPtr(pImage);
2079
2080 if (pImage)
2081 uOpenFlags = pImage->uOpenFlags;
2082 else
2083 uOpenFlags = 0;
2084
2085 LogFlowFunc(("returns %#x\n", uOpenFlags));
2086 return uOpenFlags;
2087}
2088
2089/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
2090static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2091{
2092 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2093 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2094 int rc;
2095
2096 /* Image must be opened and the new flags must be valid. */
2097 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
2098 {
2099 rc = VERR_INVALID_PARAMETER;
2100 goto out;
2101 }
2102
2103 /* Implement this operation via reopening the image. */
2104 rc = vhdFreeImage(pImage, false);
2105 if (RT_FAILURE(rc))
2106 goto out;
2107 rc = vhdOpenImage(pImage, uOpenFlags);
2108
2109out:
2110 LogFlowFunc(("returns %Rrc\n", rc));
2111 return rc;
2112}
2113
2114/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2115static int vhdGetComment(void *pBackendData, char *pszComment,
2116 size_t cbComment)
2117{
2118 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2119 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2120 int rc;
2121
2122 AssertPtr(pImage);
2123
2124 if (pImage)
2125 rc = VERR_NOT_SUPPORTED;
2126 else
2127 rc = VERR_VD_NOT_OPENED;
2128
2129 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2130 return rc;
2131}
2132
2133/** @copydoc VBOXHDDBACKEND::pfnSetComment */
2134static int vhdSetComment(void *pBackendData, const char *pszComment)
2135{
2136 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2137 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2138 int rc;
2139
2140 AssertPtr(pImage);
2141
2142 if (pImage)
2143 {
2144 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2145 rc = VERR_VD_IMAGE_READ_ONLY;
2146 else
2147 rc = VERR_NOT_SUPPORTED;
2148 }
2149 else
2150 rc = VERR_VD_NOT_OPENED;
2151
2152out:
2153 LogFlowFunc(("returns %Rrc\n", rc));
2154 return rc;
2155}
2156
2157/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2158static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
2159{
2160 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2161 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2162 int rc;
2163
2164 AssertPtr(pImage);
2165
2166 if (pImage)
2167 {
2168 *pUuid = pImage->ImageUuid;
2169 rc = VINF_SUCCESS;
2170 }
2171 else
2172 rc = VERR_VD_NOT_OPENED;
2173
2174 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2175 return rc;
2176}
2177
2178/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2179static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
2180{
2181 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2182 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2183 int rc;
2184
2185 AssertPtr(pImage);
2186
2187 if (pImage)
2188 {
2189 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2190 {
2191 pImage->ImageUuid = *pUuid;
2192 /* Update the footer copy. It will get written to disk when the image is closed. */
2193 memcpy(&pImage->vhdFooterCopy.UniqueID, pUuid, 16);
2194 /* Update checksum. */
2195 pImage->vhdFooterCopy.Checksum = 0;
2196 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
2197
2198 /* Need to update the dynamic disk header to update the disk footer copy at the beginning. */
2199 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2200 pImage->fDynHdrNeedsUpdate = true;
2201 rc = VINF_SUCCESS;
2202 }
2203 else
2204 rc = VERR_VD_IMAGE_READ_ONLY;
2205 }
2206 else
2207 rc = VERR_VD_NOT_OPENED;
2208
2209 LogFlowFunc(("returns %Rrc\n", rc));
2210 return rc;
2211}
2212
2213/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2214static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2215{
2216 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2217 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2218 int rc;
2219
2220 AssertPtr(pImage);
2221
2222 if (pImage)
2223 rc = VERR_NOT_SUPPORTED;
2224 else
2225 rc = VERR_VD_NOT_OPENED;
2226
2227 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2228 return rc;
2229}
2230
2231/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2232static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2233{
2234 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2235 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2236 int rc;
2237
2238 AssertPtr(pImage);
2239
2240 if (pImage)
2241 {
2242 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2243 rc = VERR_NOT_SUPPORTED;
2244 else
2245 rc = VERR_VD_IMAGE_READ_ONLY;
2246 }
2247 else
2248 rc = VERR_VD_NOT_OPENED;
2249
2250 LogFlowFunc(("returns %Rrc\n", rc));
2251 return rc;
2252}
2253
2254/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2255static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
2256{
2257 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2258 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2259 int rc;
2260
2261 AssertPtr(pImage);
2262
2263 if (pImage)
2264 {
2265 *pUuid = pImage->ParentUuid;
2266 rc = VINF_SUCCESS;
2267 }
2268 else
2269 rc = VERR_VD_NOT_OPENED;
2270
2271 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2272 return rc;
2273}
2274
2275/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2276static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2277{
2278 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2279 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2280 int rc = VINF_SUCCESS;
2281
2282 AssertPtr(pImage);
2283
2284 if (pImage && pImage->pStorage)
2285 {
2286 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
2287 {
2288 pImage->ParentUuid = *pUuid;
2289 pImage->fDynHdrNeedsUpdate = true;
2290 }
2291 else
2292 rc = VERR_VD_IMAGE_READ_ONLY;
2293 }
2294 else
2295 rc = VERR_VD_NOT_OPENED;
2296
2297 LogFlowFunc(("returns %Rrc\n", rc));
2298 return rc;
2299}
2300
2301/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2302static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2303{
2304 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2305 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2306 int rc;
2307
2308 AssertPtr(pImage);
2309
2310 if (pImage)
2311 rc = VERR_NOT_SUPPORTED;
2312 else
2313 rc = VERR_VD_NOT_OPENED;
2314
2315 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2316 return rc;
2317}
2318
2319/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2320static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2321{
2322 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2323 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2324 int rc;
2325
2326 AssertPtr(pImage);
2327
2328 if (pImage)
2329 {
2330 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2331 rc = VERR_NOT_SUPPORTED;
2332 else
2333 rc = VERR_VD_IMAGE_READ_ONLY;
2334 }
2335 else
2336 rc = VERR_VD_NOT_OPENED;
2337
2338 LogFlowFunc(("returns %Rrc\n", rc));
2339 return rc;
2340}
2341
2342/** @copydoc VBOXHDDBACKEND::pfnDump */
2343static void vhdDump(void *pBackendData)
2344{
2345 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2346
2347 AssertPtr(pImage);
2348 if (pImage)
2349 {
2350 vhdMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
2351 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2352 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2353 VHD_SECTOR_SIZE);
2354 vhdMessage(pImage, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
2355 vhdMessage(pImage, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
2356 }
2357}
2358
2359/** @copydoc VBOXHDDBACKEND::pfnGetTimestamp */
2360static int vhdGetTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2361{
2362 int rc = VINF_SUCCESS;
2363 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2364
2365 AssertPtr(pImage);
2366
2367 if (pImage)
2368 rc = vhdFileGetModificationTime(pImage, pImage->pszFilename, pTimeStamp);
2369 else
2370 rc = VERR_VD_NOT_OPENED;
2371
2372 LogFlowFunc(("returns %Rrc\n", rc));
2373 return rc;
2374}
2375
2376/** @copydoc VBOXHDDBACKEND::pfnGetParentTimeStamp */
2377static int vhdGetParentTimeStamp(void *pBackendData, PRTTIMESPEC pTimeStamp)
2378{
2379 int rc = VINF_SUCCESS;
2380 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2381
2382 AssertPtr(pImage);
2383
2384 if (pImage)
2385 vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
2386 else
2387 rc = VERR_VD_NOT_OPENED;
2388
2389 LogFlowFunc(("returns %Rrc\n", rc));
2390 return rc;
2391}
2392
2393/** @copydoc VBOXHDDBACKEND::pfnSetParentTimeStamp */
2394static int vhdSetParentTimeStamp(void *pBackendData, PCRTTIMESPEC pTimeStamp)
2395{
2396 int rc = VINF_SUCCESS;
2397 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2398
2399 AssertPtr(pImage);
2400 if (pImage)
2401 {
2402 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2403 rc = VERR_VD_IMAGE_READ_ONLY;
2404 else
2405 {
2406 pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
2407 pImage->fDynHdrNeedsUpdate = true;
2408 }
2409 }
2410 else
2411 rc = VERR_VD_NOT_OPENED;
2412
2413 LogFlowFunc(("returns %Rrc\n", rc));
2414 return rc;
2415}
2416
2417/** @copydoc VBOXHDDBACKEND::pfnGetParentFilename */
2418static int vhdGetParentFilename(void *pBackendData, char **ppszParentFilename)
2419{
2420 int rc = VINF_SUCCESS;
2421 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2422
2423 AssertPtr(pImage);
2424 if (pImage)
2425 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
2426 else
2427 rc = VERR_VD_NOT_OPENED;
2428
2429 LogFlowFunc(("returns %Rrc\n", rc));
2430 return rc;
2431}
2432
2433/** @copydoc VBOXHDDBACKEND::pfnSetParentFilename */
2434static int vhdSetParentFilename(void *pBackendData, const char *pszParentFilename)
2435{
2436 int rc = VINF_SUCCESS;
2437 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2438
2439 AssertPtr(pImage);
2440 if (pImage)
2441 {
2442 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2443 rc = VERR_VD_IMAGE_READ_ONLY;
2444 else
2445 {
2446 if (pImage->pszParentFilename)
2447 RTStrFree(pImage->pszParentFilename);
2448 pImage->pszParentFilename = RTStrDup(pszParentFilename);
2449 if (!pImage->pszParentFilename)
2450 rc = VERR_NO_MEMORY;
2451 else
2452 pImage->fDynHdrNeedsUpdate = true;
2453 }
2454 }
2455 else
2456 rc = VERR_VD_NOT_OPENED;
2457
2458 LogFlowFunc(("returns %Rrc\n", rc));
2459 return rc;
2460}
2461
2462/** @copydoc VBOXHDDBACKEND::pfnIsAsyncIOSupported */
2463static bool vhdIsAsyncIOSupported(void *pBackendData)
2464{
2465 return true;
2466}
2467
2468/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
2469static int vhdAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbRead,
2470 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
2471{
2472 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2473 int rc = VINF_SUCCESS;
2474
2475 LogFlowFunc(("pBackendData=%p uOffset=%#llx pIoCtx=%#p cbRead=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pIoCtx, cbRead, pcbActuallyRead));
2476
2477 if (uOffset + cbRead > pImage->cbSize)
2478 return VERR_INVALID_PARAMETER;
2479
2480 /*
2481 * If we have a dynamic disk image, we need to find the data block and sector to read.
2482 */
2483 if (pImage->pBlockAllocationTable)
2484 {
2485 /*
2486 * Get the data block first.
2487 */
2488 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
2489 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
2490 uint64_t uVhdOffset;
2491
2492 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
2493 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
2494
2495 /*
2496 * If the block is not allocated the content of the entry is ~0
2497 */
2498 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2499 {
2500 /* Return block size as read. */
2501 *pcbActuallyRead = RT_MIN(cbRead, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
2502 return VERR_VD_BLOCK_FREE;
2503 }
2504
2505 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2506 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2507
2508 /*
2509 * Clip read range to remain in this data block.
2510 */
2511 cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2512
2513 /* Read in the block's bitmap. */
2514 PVDMETAXFER pMetaXfer;
2515 rc = vhdFileReadMetaAsync(pImage,
2516 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2517 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
2518 pIoCtx, &pMetaXfer, NULL, NULL);
2519
2520 if (RT_SUCCESS(rc))
2521 {
2522 uint32_t cSectors = 0;
2523
2524 vhdFileMetaXferRelease(pImage, pMetaXfer);
2525 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2526 {
2527 cBATEntryIndex++;
2528 cSectors = 1;
2529
2530 /*
2531 * The first sector being read is marked dirty, read as much as we
2532 * can from child. Note that only sectors that are marked dirty
2533 * must be read from child.
2534 */
2535 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2536 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2537 {
2538 cBATEntryIndex++;
2539 cSectors++;
2540 }
2541
2542 cbRead = cSectors * VHD_SECTOR_SIZE;
2543
2544 LogFlowFunc(("uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2545 rc = vhdFileReadUserAsync(pImage, uVhdOffset, pIoCtx, cbRead);
2546 }
2547 else
2548 {
2549 /*
2550 * The first sector being read is marked clean, so we should read from
2551 * our parent instead, but only as much as there are the following
2552 * clean sectors, because the block may still contain dirty sectors
2553 * further on. We just need to compute the number of clean sectors
2554 * and pass it to our caller along with the notification that they
2555 * should be read from the parent.
2556 */
2557 cBATEntryIndex++;
2558 cSectors = 1;
2559
2560 while ( (cSectors < (cbRead / VHD_SECTOR_SIZE))
2561 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
2562 {
2563 cBATEntryIndex++;
2564 cSectors++;
2565 }
2566
2567 cbRead = cSectors * VHD_SECTOR_SIZE;
2568 LogFunc(("Sectors free: uVhdOffset=%llu cbRead=%u\n", uVhdOffset, cbRead));
2569 rc = VERR_VD_BLOCK_FREE;
2570 }
2571 }
2572 else
2573 AssertMsg(rc == VERR_VD_NOT_ENOUGH_METADATA, ("Reading block bitmap failed rc=%Rrc\n", rc));
2574 }
2575 else
2576 {
2577 rc = vhdFileReadUserAsync(pImage, uOffset, pIoCtx, cbRead);
2578 }
2579
2580 if (pcbActuallyRead)
2581 *pcbActuallyRead = cbRead;
2582
2583 LogFlowFunc(("returns rc=%Rrc\n", rc));
2584 return rc;
2585}
2586
2587/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
2588static int vhdAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbWrite,
2589 PVDIOCTX pIoCtx,
2590 size_t *pcbWriteProcess, size_t *pcbPreRead,
2591 size_t *pcbPostRead, unsigned fWrite)
2592{
2593 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2594 int rc = VINF_SUCCESS;
2595
2596 LogFlowFunc(("pBackendData=%p uOffset=%llu pIoCtx=%#p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
2597 pBackendData, uOffset, pIoCtx, cbWrite, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
2598
2599 AssertPtr(pImage);
2600 Assert(uOffset % VHD_SECTOR_SIZE == 0);
2601 Assert(cbWrite % VHD_SECTOR_SIZE == 0);
2602
2603 if (pImage->pBlockAllocationTable)
2604 {
2605 /*
2606 * Get the data block first.
2607 */
2608 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
2609 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
2610 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
2611 uint64_t uVhdOffset;
2612
2613 /*
2614 * Clip write range.
2615 */
2616 cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
2617
2618 /*
2619 * If the block is not allocated the content of the entry is ~0
2620 * and we need to allocate a new block. Note that while blocks are
2621 * allocated with a relatively big granularity, each sector has its
2622 * own bitmap entry, indicating whether it has been written or not.
2623 * So that means for the purposes of the higher level that the
2624 * granularity is invisible. This means there's no need to return
2625 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
2626 */
2627 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
2628 {
2629 /* Check if the block allocation should be suppressed. */
2630 if (fWrite & VD_WRITE_NO_ALLOC)
2631 {
2632 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
2633 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbWrite - *pcbPreRead;
2634
2635 if (pcbWriteProcess)
2636 *pcbWriteProcess = cbWrite;
2637 return VERR_VD_BLOCK_FREE;
2638 }
2639
2640 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)RTMemAllocZ(RT_OFFSETOF(VHDIMAGEEXPAND, au8Bitmap[pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE]));
2641 bool fIoInProgress = false;
2642
2643 if (!pExpand)
2644 return VERR_NO_MEMORY;
2645
2646 pExpand->cbEofOld = pImage->uCurrentEndOfFile;
2647 pExpand->idxBatAllocated = cBlockAllocationTableEntry;
2648 pExpand->idxBlockBe = RT_H2BE_U32(pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE);
2649
2650 /* Set the bits for all sectors having been written. */
2651 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2652 {
2653 /* No need to check for a changed value because this is an initial write. */
2654 vhdBlockBitmapSectorSet(pImage, pExpand->au8Bitmap, cBATEntryIndex);
2655 cBATEntryIndex++;
2656 }
2657
2658 do
2659 {
2660 /*
2661 * Start with the sector bitmap.
2662 */
2663 rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
2664 pExpand->au8Bitmap,
2665 pImage->cbDataBlockBitmap, pIoCtx,
2666 vhdAsyncExpansionDataBlockBitmapComplete,
2667 pExpand);
2668 if (RT_SUCCESS(rc))
2669 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2670 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2671 fIoInProgress = true;
2672 else
2673 {
2674 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2675 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2676 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2677 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2678 break;
2679 }
2680
2681
2682 /*
2683 * Write the new block at the current end of the file.
2684 */
2685 rc = vhdFileWriteUserAsync(pImage,
2686 pImage->uCurrentEndOfFile + pImage->cbDataBlockBitmap,
2687 pIoCtx, cbWrite,
2688 vhdAsyncExpansionDataComplete,
2689 pExpand);
2690 if (RT_SUCCESS(rc))
2691 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2692 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2693 fIoInProgress = true;
2694 else
2695 {
2696 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2697 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2698 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2699 break;
2700 }
2701
2702 /*
2703 * Write entry in the BAT.
2704 */
2705 rc = vhdFileWriteMetaAsync(pImage,
2706 pImage->uBlockAllocationTableOffset + cBlockAllocationTableEntry * sizeof(uint32_t),
2707 &pExpand->idxBlockBe,
2708 sizeof(uint32_t), pIoCtx,
2709 vhdAsyncExpansionBatUpdateComplete,
2710 pExpand);
2711 if (RT_SUCCESS(rc))
2712 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2713 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2714 fIoInProgress = true;
2715 else
2716 {
2717 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2718 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2719 break;
2720 }
2721
2722 /*
2723 * Set the new end of the file and link the new block into the BAT.
2724 */
2725 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2726 pImage->uCurrentEndOfFile += pImage->cbDataBlockBitmap + pImage->cbDataBlock;
2727
2728 /* Update the footer. */
2729 rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
2730 &pImage->vhdFooterCopy,
2731 sizeof(VHDFooter), pIoCtx,
2732 vhdAsyncExpansionFooterUpdateComplete,
2733 pExpand);
2734 if (RT_SUCCESS(rc))
2735 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_SUCCESS);
2736 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2737 fIoInProgress = true;
2738 else
2739 {
2740 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT, VHDIMAGEEXPAND_STEP_FAILED);
2741 break;
2742 }
2743
2744 } while (0);
2745
2746 if (!fIoInProgress)
2747 vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
2748 else
2749 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2750 }
2751 else
2752 {
2753 /*
2754 * Calculate the real offset in the file.
2755 */
2756 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
2757
2758 /* Read in the block's bitmap. */
2759 PVDMETAXFER pMetaXfer;
2760 rc = vhdFileReadMetaAsync(pImage,
2761 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2762 pImage->pu8Bitmap,
2763 pImage->cbDataBlockBitmap, pIoCtx,
2764 &pMetaXfer, NULL, NULL);
2765 if (RT_SUCCESS(rc))
2766 {
2767 vhdFileMetaXferRelease(pImage, pMetaXfer);
2768
2769 /* Write data. */
2770 rc = vhdFileWriteUserAsync(pImage, uVhdOffset, pIoCtx, cbWrite,
2771 NULL, NULL);
2772 if (RT_SUCCESS(rc) || rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2773 {
2774 bool fChanged = false;
2775
2776 /* Set the bits for all sectors having been written. */
2777 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
2778 {
2779 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
2780 cBATEntryIndex++;
2781 }
2782
2783 /* Only write the bitmap if it was changed. */
2784 if (fChanged)
2785 {
2786 /*
2787 * Write the bitmap back.
2788 *
2789 * @note We don't have a completion callback here because we
2790 * can't do anything if the write fails for some reason.
2791 * The error will propagated to the device/guest
2792 * by the generic VD layer already and we don't need
2793 * to rollback anything here.
2794 */
2795 rc = vhdFileWriteMetaAsync(pImage,
2796 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
2797 pImage->pu8Bitmap,
2798 pImage->cbDataBlockBitmap,
2799 pIoCtx, NULL, NULL);
2800 }
2801 }
2802 }
2803 }
2804 }
2805 else
2806 {
2807 rc = vhdFileWriteUserAsync(pImage, uOffset, pIoCtx, cbWrite, NULL, NULL);
2808 }
2809
2810 if (pcbWriteProcess)
2811 *pcbWriteProcess = cbWrite;
2812
2813 /* Stay on the safe side. Do not run the risk of confusing the higher
2814 * level, as that can be pretty lethal to image consistency. */
2815 *pcbPreRead = 0;
2816 *pcbPostRead = 0;
2817
2818 return rc;
2819}
2820
2821/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
2822static int vhdAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
2823{
2824 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2825
2826 /* No need to write anything here. Data is always updated on a write. */
2827 return vhdFileFlushAsync(pImage, pIoCtx, NULL, NULL);
2828}
2829
2830/** @copydoc VBOXHDDBACKEND::pfnResize */
2831static int vhdResize(void *pBackendData, uint64_t cbSize,
2832 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
2833 unsigned uPercentStart, unsigned uPercentSpan,
2834 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
2835 PVDINTERFACE pVDIfsOperation)
2836{
2837 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2838 int rc = VINF_SUCCESS;
2839
2840 PFNVDPROGRESS pfnProgress = NULL;
2841 void *pvUser = NULL;
2842 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
2843 VDINTERFACETYPE_PROGRESS);
2844 PVDINTERFACEPROGRESS pCbProgress = NULL;
2845 if (pIfProgress)
2846 {
2847 pCbProgress = VDGetInterfaceProgress(pIfProgress);
2848 if (pCbProgress)
2849 pfnProgress = pCbProgress->pfnProgress;
2850 pvUser = pIfProgress->pvUser;
2851 }
2852
2853 /* Making the image smaller is not supported at the moment. */
2854 if ( cbSize < pImage->cbSize
2855 || pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
2856 rc = VERR_NOT_SUPPORTED;
2857 else if (cbSize > pImage->cbSize)
2858 {
2859 unsigned cBlocksAllocated = 0;
2860 size_t cbBlock = pImage->cbDataBlock + pImage->cbDataBlockBitmap; /** < Size of a block including the sector bitmap. */
2861 uint32_t cBlocksNew = cbSize / pImage->cbDataBlock; /** < New number of blocks in the image after the resize */
2862 if (cbSize % pImage->cbDataBlock)
2863 cBlocksNew++;
2864
2865 uint32_t cBlocksOld = pImage->cBlockAllocationTableEntries; /** < Number of blocks before the resize. */
2866 uint64_t cbBlockspaceNew = RT_ALIGN_32(cBlocksNew * sizeof(uint32_t), VHD_SECTOR_SIZE); /** < Required space for the block array after the resize. */
2867 uint64_t offStartDataNew = RT_ALIGN_32(pImage->uBlockAllocationTableOffset + cbBlockspaceNew, VHD_SECTOR_SIZE); /** < New start offset for block data after the resize */
2868 uint64_t offStartDataOld = ~0ULL;
2869
2870 /* Go through the BAT and finde the data start offset. */
2871 for (unsigned idxBlock = 0; idxBlock < pImage->cBlockAllocationTableEntries; idxBlock++)
2872 {
2873 if (pImage->pBlockAllocationTable[idxBlock] != ~0U)
2874 {
2875 uint64_t offStartBlock = pImage->pBlockAllocationTable[idxBlock] * VHD_SECTOR_SIZE;
2876 if (offStartBlock < offStartDataOld)
2877 offStartDataOld = offStartBlock;
2878 cBlocksAllocated++;
2879 }
2880 }
2881
2882 if ( offStartDataOld != offStartDataNew
2883 && cBlocksAllocated > 0)
2884 {
2885 /* Calculate how many sectors nee to be relocated. */
2886 uint64_t cbOverlapping = offStartDataNew - offStartDataOld;
2887 unsigned cBlocksReloc = cbOverlapping / cbBlock;
2888 if (cbOverlapping % cbBlock)
2889 cBlocksReloc++;
2890
2891 cBlocksReloc = RT_MIN(cBlocksReloc, cBlocksAllocated);
2892 offStartDataNew = offStartDataOld;
2893
2894 /* Do the relocation. */
2895 LogFlow(("Relocating %u blocks\n", cBlocksReloc));
2896
2897 /*
2898 * Get the blocks we need to relocate first, they are appended to the end
2899 * of the image.
2900 */
2901 void *pvBuf = NULL, *pvZero = NULL;
2902 do
2903 {
2904 /* Allocate data buffer. */
2905 pvBuf = RTMemAllocZ(cbBlock);
2906 if (!pvBuf)
2907 {
2908 rc = VERR_NO_MEMORY;
2909 break;
2910 }
2911
2912 /* Allocate buffer for overwrting with zeroes. */
2913 pvZero = RTMemAllocZ(cbBlock);
2914 if (!pvZero)
2915 {
2916 rc = VERR_NO_MEMORY;
2917 break;
2918 }
2919
2920 for (unsigned i = 0; i < cBlocksReloc; i++)
2921 {
2922 uint32_t uBlock = offStartDataNew / VHD_SECTOR_SIZE;
2923
2924 /* Search the index in the block table. */
2925 for (unsigned idxBlock = 0; idxBlock < cBlocksOld; idxBlock++)
2926 {
2927 if (pImage->pBlockAllocationTable[idxBlock] == uBlock)
2928 {
2929 /* Read data and append to the end of the image. */
2930 rc = vhdFileReadSync(pImage, offStartDataNew, pvBuf, cbBlock, NULL);
2931 if (RT_FAILURE(rc))
2932 break;
2933
2934 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, pvBuf, cbBlock, NULL);
2935 if (RT_FAILURE(rc))
2936 break;
2937
2938 /* Zero out the old block area. */
2939 rc = vhdFileWriteSync(pImage, offStartDataNew, pvZero, cbBlock, NULL);
2940 if (RT_FAILURE(rc))
2941 break;
2942
2943 /* Update block counter. */
2944 pImage->pBlockAllocationTable[idxBlock] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
2945
2946 pImage->uCurrentEndOfFile += cbBlock;
2947
2948 /* Continue with the next block. */
2949 break;
2950 }
2951 }
2952
2953 if (RT_FAILURE(rc))
2954 break;
2955
2956 offStartDataNew += cbBlock;
2957 }
2958 } while (0);
2959
2960 if (pvBuf)
2961 RTMemFree(pvBuf);
2962 if (pvZero)
2963 RTMemFree(pvZero);
2964 }
2965
2966 /*
2967 * Relocation done, expand the block array and update the header with
2968 * the new data.
2969 */
2970 if (RT_SUCCESS(rc))
2971 {
2972 uint32_t *paBlocksNew = (uint32_t *)RTMemRealloc(pImage->pBlockAllocationTable, cBlocksNew * sizeof(uint32_t));
2973 if (paBlocksNew)
2974 {
2975 pImage->pBlockAllocationTable = paBlocksNew;
2976
2977 /* Mark the new blocks as unallocated. */
2978 for (unsigned idxBlock = cBlocksOld; idxBlock < cBlocksNew; idxBlock++)
2979 pImage->pBlockAllocationTable[idxBlock] = ~0U;
2980 }
2981 else
2982 rc = VERR_NO_MEMORY;
2983
2984 if (RT_SUCCESS(rc))
2985 {
2986 /* Write the block array before updating the rest. */
2987 rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset, pImage->pBlockAllocationTable,
2988 cBlocksNew * sizeof(uint32_t), NULL);
2989 }
2990
2991 if (RT_SUCCESS(rc))
2992 {
2993 /* Update size and new block count. */
2994 pImage->cBlockAllocationTableEntries = cBlocksNew;
2995 pImage->cbSize = cbSize;
2996
2997 /* Update geometry. */
2998 pImage->PCHSGeometry = *pPCHSGeometry;
2999 pImage->LCHSGeometry = *pLCHSGeometry;
3000 }
3001 }
3002
3003 /* Update header information in base image file. */
3004 pImage->fDynHdrNeedsUpdate = true;
3005 vhdFlush(pImage);
3006 }
3007 /* Same size doesn't change the image at all. */
3008
3009 LogFlowFunc(("returns %Rrc\n", rc));
3010 return rc;
3011}
3012
3013
3014VBOXHDDBACKEND g_VhdBackend =
3015{
3016 /* pszBackendName */
3017 "VHD",
3018 /* cbSize */
3019 sizeof(VBOXHDDBACKEND),
3020 /* uBackendCaps */
3021 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE |
3022 VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC |
3023 VD_CAP_ASYNC | VD_CAP_VFS,
3024 /* paFileExtensions */
3025 s_aVhdFileExtensions,
3026 /* paConfigInfo */
3027 NULL,
3028 /* hPlugin */
3029 NIL_RTLDRMOD,
3030 /* pfnCheckIfValid */
3031 vhdCheckIfValid,
3032 /* pfnOpen */
3033 vhdOpen,
3034 /* pfnCreate */
3035 vhdCreate,
3036 /* pfnRename */
3037 vhdRename,
3038 /* pfnClose */
3039 vhdClose,
3040 /* pfnRead */
3041 vhdRead,
3042 /* pfnWrite */
3043 vhdWrite,
3044 /* pfnFlush */
3045 vhdFlush,
3046 /* pfnGetVersion */
3047 vhdGetVersion,
3048 /* pfnGetSize */
3049 vhdGetSize,
3050 /* pfnGetFileSize */
3051 vhdGetFileSize,
3052 /* pfnGetPCHSGeometry */
3053 vhdGetPCHSGeometry,
3054 /* pfnSetPCHSGeometry */
3055 vhdSetPCHSGeometry,
3056 /* pfnGetLCHSGeometry */
3057 vhdGetLCHSGeometry,
3058 /* pfnSetLCHSGeometry */
3059 vhdSetLCHSGeometry,
3060 /* pfnGetImageFlags */
3061 vhdGetImageFlags,
3062 /* pfnGetOpenFlags */
3063 vhdGetOpenFlags,
3064 /* pfnSetOpenFlags */
3065 vhdSetOpenFlags,
3066 /* pfnGetComment */
3067 vhdGetComment,
3068 /* pfnSetComment */
3069 vhdSetComment,
3070 /* pfnGetUuid */
3071 vhdGetUuid,
3072 /* pfnSetUuid */
3073 vhdSetUuid,
3074 /* pfnGetModificationUuid */
3075 vhdGetModificationUuid,
3076 /* pfnSetModificationUuid */
3077 vhdSetModificationUuid,
3078 /* pfnGetParentUuid */
3079 vhdGetParentUuid,
3080 /* pfnSetParentUuid */
3081 vhdSetParentUuid,
3082 /* pfnGetParentModificationUuid */
3083 vhdGetParentModificationUuid,
3084 /* pfnSetParentModificationUuid */
3085 vhdSetParentModificationUuid,
3086 /* pfnDump */
3087 vhdDump,
3088 /* pfnGetTimeStamp */
3089 vhdGetTimeStamp,
3090 /* pfnGetParentTimeStamp */
3091 vhdGetParentTimeStamp,
3092 /* pfnSetParentTimeStamp */
3093 vhdSetParentTimeStamp,
3094 /* pfnGetParentFilename */
3095 vhdGetParentFilename,
3096 /* pfnSetParentFilename */
3097 vhdSetParentFilename,
3098 /* pfnIsAsyncIOSupported */
3099 vhdIsAsyncIOSupported,
3100 /* pfnAsyncRead */
3101 vhdAsyncRead,
3102 /* pfnAsyncWrite */
3103 vhdAsyncWrite,
3104 /* pfnAsyncFlush */
3105 vhdAsyncFlush,
3106 /* pfnComposeLocation */
3107 genericFileComposeLocation,
3108 /* pfnComposeName */
3109 genericFileComposeName,
3110 /* pfnCompact */
3111 NULL,
3112 /* pfnResize */
3113 vhdResize
3114};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use