VirtualBox

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

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

Storage: a few warnings and a typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.8 KB
Line 
1/* $Id: VHD.cpp 34147 2010-11-17 20:46:42Z vboxsync $ */
2/** @file
3 * VHD Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD_VHD
22#include <VBox/vd-plugin.h>
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <VBox/version.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/mem.h>
30#include <iprt/uuid.h>
31#include <iprt/path.h>
32#include <iprt/string.h>
33
34#define VHD_RELATIVE_MAX_PATH 512
35#define VHD_ABSOLUTE_MAX_PATH 512
36
37#define VHD_SECTOR_SIZE 512
38#define VHD_BLOCK_SIZE (2 * _1M)
39
40/* This is common to all VHD disk types and is located at the end of the image */
41#pragma pack(1)
42typedef struct VHDFooter
43{
44 char Cookie[8];
45 uint32_t Features;
46 uint32_t Version;
47 uint64_t DataOffset;
48 uint32_t TimeStamp;
49 uint8_t CreatorApp[4];
50 uint32_t CreatorVer;
51 uint32_t CreatorOS;
52 uint64_t OrigSize;
53 uint64_t CurSize;
54 uint16_t DiskGeometryCylinder;
55 uint8_t DiskGeometryHeads;
56 uint8_t DiskGeometrySectors;
57 uint32_t DiskType;
58 uint32_t Checksum;
59 char UniqueID[16];
60 uint8_t SavedState;
61 uint8_t Reserved[427];
62} VHDFooter;
63#pragma pack()
64
65/* this really is spelled with only one n */
66#define VHD_FOOTER_COOKIE "conectix"
67#define VHD_FOOTER_COOKIE_SIZE 8
68
69#define VHD_FOOTER_FEATURES_NOT_ENABLED 0
70#define VHD_FOOTER_FEATURES_TEMPORARY 1
71#define VHD_FOOTER_FEATURES_RESERVED 2
72
73#define VHD_FOOTER_FILE_FORMAT_VERSION 0x00010000
74#define VHD_FOOTER_DATA_OFFSET_FIXED UINT64_C(0xffffffffffffffff)
75#define VHD_FOOTER_DISK_TYPE_FIXED 2
76#define VHD_FOOTER_DISK_TYPE_DYNAMIC 3
77#define VHD_FOOTER_DISK_TYPE_DIFFERENCING 4
78
79#define VHD_MAX_LOCATOR_ENTRIES 8
80#define VHD_PLATFORM_CODE_NONE 0
81#define VHD_PLATFORM_CODE_WI2R 0x57693272
82#define VHD_PLATFORM_CODE_WI2K 0x5769326B
83#define VHD_PLATFORM_CODE_W2RU 0x57327275
84#define VHD_PLATFORM_CODE_W2KU 0x57326B75
85#define VHD_PLATFORM_CODE_MAC 0x4D163220
86#define VHD_PLATFORM_CODE_MACX 0x4D163258
87
88/* Header for expanding disk images. */
89#pragma pack(1)
90typedef struct VHDParentLocatorEntry
91{
92 uint32_t u32Code;
93 uint32_t u32DataSpace;
94 uint32_t u32DataLength;
95 uint32_t u32Reserved;
96 uint64_t u64DataOffset;
97} VHDPLE, *PVHDPLE;
98
99typedef struct VHDDynamicDiskHeader
100{
101 char Cookie[8];
102 uint64_t DataOffset;
103 uint64_t TableOffset;
104 uint32_t HeaderVersion;
105 uint32_t MaxTableEntries;
106 uint32_t BlockSize;
107 uint32_t Checksum;
108 uint8_t ParentUuid[16];
109 uint32_t ParentTimeStamp;
110 uint32_t Reserved0;
111 uint16_t ParentUnicodeName[256];
112 VHDPLE ParentLocatorEntry[VHD_MAX_LOCATOR_ENTRIES];
113 uint8_t Reserved1[256];
114} VHDDynamicDiskHeader;
115#pragma pack()
116
117#define VHD_DYNAMIC_DISK_HEADER_COOKIE "cxsparse"
118#define VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE 8
119#define VHD_DYNAMIC_DISK_HEADER_VERSION 0x00010000
120
121/**
122 * Complete VHD image data structure.
123 */
124typedef struct VHDIMAGE
125{
126 /** Image file name. */
127 const char *pszFilename;
128 /** Opaque storage handle. */
129 PVDIOSTORAGE pStorage;
130
131 /** I/O interface. */
132 PVDINTERFACE pInterfaceIO;
133 /** I/O interface callbacks. */
134 PVDINTERFACEIOINT pInterfaceIOCallbacks;
135
136 /** Pointer to the per-disk VD interface list. */
137 PVDINTERFACE pVDIfsDisk;
138 /** Pointer to the per-image VD interface list. */
139 PVDINTERFACE pVDIfsImage;
140 /** Error interface. */
141 PVDINTERFACE pInterfaceError;
142 /** Error interface callback table. */
143 PVDINTERFACEERROR pInterfaceErrorCallbacks;
144
145 /** Open flags passed by VBoxHDD layer. */
146 unsigned uOpenFlags;
147 /** Image flags defined during creation or determined during open. */
148 unsigned uImageFlags;
149 /** Total size of the image. */
150 uint64_t cbSize;
151
152 /** Physical geometry of this image. */
153 VDGEOMETRY PCHSGeometry;
154 /** Logical geometry of this image. */
155 VDGEOMETRY LCHSGeometry;
156
157 /** Image UUID. */
158 RTUUID ImageUuid;
159 /** Parent image UUID. */
160 RTUUID ParentUuid;
161
162 /** Parent's time stamp at the time of image creation. */
163 uint32_t u32ParentTimeStamp;
164 /** Relative path to the parent image. */
165 char *pszParentFilename;
166
167 /** The Block Allocation Table. */
168 uint32_t *pBlockAllocationTable;
169 /** Number of entries in the table. */
170 uint32_t cBlockAllocationTableEntries;
171
172 /** Size of one data block. */
173 uint32_t cbDataBlock;
174 /** Sectors per data block. */
175 uint32_t cSectorsPerDataBlock;
176 /** Length of the sector bitmap in bytes. */
177 uint32_t cbDataBlockBitmap;
178 /** A copy of the disk footer. */
179 VHDFooter vhdFooterCopy;
180 /** Current end offset of the file (without the disk footer). */
181 uint64_t uCurrentEndOfFile;
182 /** Size of the data block bitmap in sectors. */
183 uint32_t cDataBlockBitmapSectors;
184 /** Start of the block allocation table. */
185 uint64_t uBlockAllocationTableOffset;
186 /** Buffer to hold block's bitmap for bit search operations. */
187 uint8_t *pu8Bitmap;
188 /** Offset to the next data structure (dynamic disk header). */
189 uint64_t u64DataOffset;
190 /** Flag to force dynamic disk header update. */
191 bool fDynHdrNeedsUpdate;
192} VHDIMAGE, *PVHDIMAGE;
193
194/**
195 * Structure tracking the expansion process of the image
196 * for async access.
197 */
198typedef struct VHDIMAGEEXPAND
199{
200 /** Flag indicating the status of each step. */
201 volatile uint32_t fFlags;
202 /** The index in the block allocation table which is written. */
203 uint32_t idxBatAllocated;
204 /** Big endian representation of the block index
205 * which is written in the BAT. */
206 uint32_t idxBlockBe;
207 /** Old end of the file - used for rollback in case of an error. */
208 uint64_t cbEofOld;
209 /** Sector bitmap written to the new block - variable in size. */
210 uint8_t au8Bitmap[1];
211} VHDIMAGEEXPAND, *PVHDIMAGEEXPAND;
212
213/**
214 * Flag defines
215 */
216#define VHDIMAGEEXPAND_STEP_IN_PROGRESS (0x0)
217#define VHDIMAGEEXPAND_STEP_FAILED (0x2)
218#define VHDIMAGEEXPAND_STEP_SUCCESS (0x3)
219/** All steps completed successfully. */
220#define VHDIMAGEEXPAND_ALL_SUCCESS (0xff)
221/** All steps completed (no success indicator) */
222#define VHDIMAGEEXPAND_ALL_COMPLETE (0xaa)
223
224/** Every status field has 2 bits so we can encode 4 steps in one byte. */
225#define VHDIMAGEEXPAND_STATUS_MASK 0x03
226#define VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT 0x00
227#define VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT 0x02
228#define VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT 0x04
229#define VHDIMAGEEXPAND_BAT_STATUS_SHIFT 0x06
230
231/**
232 * Helper macros to get and set the status field.
233 */
234#define VHDIMAGEEXPAND_STATUS_GET(fFlags, cShift) \
235 (((fFlags) >> (cShift)) & VHDIMAGEEXPAND_STATUS_MASK)
236#define VHDIMAGEEXPAND_STATUS_SET(fFlags, cShift, uVal) \
237 ASMAtomicOrU32(&(fFlags), ((uVal) & VHDIMAGEEXPAND_STATUS_MASK) << (cShift))
238
239/*******************************************************************************
240* Static Variables *
241*******************************************************************************/
242
243/** NULL-terminated array of supported file extensions. */
244static const VDFILEEXTENSION s_aVhdFileExtensions[] =
245{
246 {"vhd", VDTYPE_HDD},
247 {NULL, VDTYPE_INVALID}
248};
249
250/*******************************************************************************
251* Internal Functions *
252*******************************************************************************/
253
254/**
255 * Internal: signal an error to the frontend.
256 */
257DECLINLINE(int) vhdError(PVHDIMAGE pImage, int rc, RT_SRC_POS_DECL,
258 const char *pszFormat, ...)
259{
260 va_list va;
261 va_start(va, pszFormat);
262 if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
263 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
264 pszFormat, va);
265 va_end(va);
266 return rc;
267}
268
269/**
270 * Internal: signal an informational message to the frontend.
271 */
272DECLINLINE(int) vhdMessage(PVHDIMAGE pImage, const char *pszFormat, ...)
273{
274 int rc = VINF_SUCCESS;
275 va_list va;
276 va_start(va, pszFormat);
277 if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
278 rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
279 pszFormat, va);
280 va_end(va);
281 return rc;
282}
283
284
285DECLINLINE(int) vhdFileOpen(PVHDIMAGE pImage, const char *pszFilename,
286 uint32_t fOpen)
287{
288 return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
289 pszFilename, fOpen,
290 &pImage->pStorage);
291}
292
293DECLINLINE(int) vhdFileClose(PVHDIMAGE pImage)
294{
295 return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
296 pImage->pStorage);
297}
298
299DECLINLINE(int) vhdFileDelete(PVHDIMAGE pImage, const char *pszFilename)
300{
301 return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
302 pszFilename);
303}
304
305DECLINLINE(int) vhdFileMove(PVHDIMAGE pImage, const char *pszSrc,
306 const char *pszDst, unsigned fMove)
307{
308 return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
309 pszSrc, pszDst, fMove);
310}
311
312DECLINLINE(int) vhdFileGetFreeSpace(PVHDIMAGE pImage, const char *pszFilename,
313 int64_t *pcbFree)
314{
315 return pImage->pInterfaceIOCallbacks->pfnGetFreeSpace(pImage->pInterfaceIO->pvUser,
316 pszFilename, pcbFree);
317}
318
319DECLINLINE(int) vhdFileGetModificationTime(PVHDIMAGE pImage,
320 const char *pszFilename,
321 PRTTIMESPEC pModificationTime)
322{
323 return pImage->pInterfaceIOCallbacks->pfnGetModificationTime(pImage->pInterfaceIO->pvUser,
324 pszFilename,
325 pModificationTime);
326}
327
328DECLINLINE(int) vhdFileGetSize(PVHDIMAGE pImage, uint64_t *pcbSize)
329{
330 return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
331 pImage->pStorage, pcbSize);
332}
333
334DECLINLINE(int) vhdFileSetSize(PVHDIMAGE pImage, uint64_t cbSize)
335{
336 return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
337 pImage->pStorage, cbSize);
338}
339
340DECLINLINE(int) vhdFileWriteSync(PVHDIMAGE pImage, uint64_t uOffset,
341 const void *pvBuffer, size_t cbBuffer,
342 size_t *pcbWritten)
343{
344 return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
345 pImage->pStorage, uOffset,
346 pvBuffer, cbBuffer, pcbWritten);
347}
348
349DECLINLINE(int) vhdFileReadSync(PVHDIMAGE pImage, uint64_t uOffset,
350 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
351{
352 return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
353 pImage->pStorage, uOffset,
354 pvBuffer, cbBuffer, pcbRead);
355}
356
357DECLINLINE(int) vhdFileFlushSync(PVHDIMAGE pImage)
358{
359 return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
360 pImage->pStorage);
361}
362
363DECLINLINE(int) vhdFileReadUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
364 PVDIOCTX pIoCtx, size_t cbRead)
365{
366 return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
367 pImage->pStorage,
368 uOffset, pIoCtx,
369 cbRead);
370}
371
372DECLINLINE(int) vhdFileWriteUserAsync(PVHDIMAGE pImage, uint64_t uOffset,
373 PVDIOCTX pIoCtx, size_t cbWrite,
374 PFNVDXFERCOMPLETED pfnComplete,
375 void *pvCompleteUser)
376{
377 return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
378 pImage->pStorage,
379 uOffset, pIoCtx,
380 cbWrite,
381 pfnComplete,
382 pvCompleteUser);
383}
384
385DECLINLINE(int) vhdFileReadMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
386 void *pvBuffer, size_t cbBuffer,
387 PVDIOCTX pIoCtx, PPVDMETAXFER ppMetaXfer,
388 PFNVDXFERCOMPLETED pfnComplete,
389 void *pvCompleteUser)
390{
391 return pImage->pInterfaceIOCallbacks->pfnReadMetaAsync(pImage->pInterfaceIO->pvUser,
392 pImage->pStorage,
393 uOffset, pvBuffer,
394 cbBuffer, pIoCtx,
395 ppMetaXfer,
396 pfnComplete,
397 pvCompleteUser);
398}
399
400DECLINLINE(int) vhdFileWriteMetaAsync(PVHDIMAGE pImage, uint64_t uOffset,
401 void *pvBuffer, size_t cbBuffer,
402 PVDIOCTX pIoCtx,
403 PFNVDXFERCOMPLETED pfnComplete,
404 void *pvCompleteUser)
405{
406 return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
407 pImage->pStorage,
408 uOffset, pvBuffer,
409 cbBuffer, pIoCtx,
410 pfnComplete,
411 pvCompleteUser);
412}
413
414DECLINLINE(int) vhdFileFlushAsync(PVHDIMAGE pImage, PVDIOCTX pIoCtx,
415 PFNVDXFERCOMPLETED pfnComplete,
416 void *pvCompleteUser)
417{
418 return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
419 pImage->pStorage,
420 pIoCtx, pfnComplete,
421 pvCompleteUser);
422}
423
424DECLINLINE(void) vhdFileMetaXferRelease(PVHDIMAGE pImage, PVDMETAXFER pMetaXfer)
425{
426 pImage->pInterfaceIOCallbacks->pfnMetaXferRelease(pImage->pInterfaceIO->pvUser,
427 pMetaXfer);
428}
429
430
431/**
432 * Internal: Compute and update header checksum.
433 */
434static uint32_t vhdChecksum(void *pHeader, uint32_t cbSize)
435{
436 uint32_t checksum = 0;
437 for (uint32_t i = 0; i < cbSize; i++)
438 checksum += ((unsigned char *)pHeader)[i];
439 return ~checksum;
440}
441
442/**
443 * Internal: Convert filename to UTF16 with appropriate endianness.
444 */
445static int vhdFilenameToUtf16(const char *pszFilename, uint16_t *pu16Buf,
446 uint32_t cbBufSize, uint32_t *pcbActualSize,
447 bool fBigEndian)
448{
449 int rc;
450 PRTUTF16 tmp16 = NULL;
451 size_t cTmp16Len;
452
453 rc = RTStrToUtf16(pszFilename, &tmp16);
454 if (RT_FAILURE(rc))
455 goto out;
456 cTmp16Len = RTUtf16Len(tmp16);
457 if (cTmp16Len * sizeof(*tmp16) > cbBufSize)
458 {
459 rc = VERR_FILENAME_TOO_LONG;
460 goto out;
461 }
462
463 if (fBigEndian)
464 for (unsigned i = 0; i < cTmp16Len; i++)
465 pu16Buf[i] = RT_H2BE_U16(tmp16[i]);
466 else
467 memcpy(pu16Buf, tmp16, cTmp16Len * sizeof(*tmp16));
468 if (pcbActualSize)
469 *pcbActualSize = (uint32_t)(cTmp16Len * sizeof(*tmp16));
470
471out:
472 if (tmp16)
473 RTUtf16Free(tmp16);
474 return rc;
475}
476
477/**
478 * Internal: Update one locator entry.
479 */
480static int vhdLocatorUpdate(PVHDIMAGE pImage, PVHDPLE pLocator, const char *pszFilename)
481{
482 int rc;
483 uint32_t cb, cbMaxLen = RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE;
484 void *pvBuf = RTMemTmpAllocZ(cbMaxLen);
485 char *pszTmp;
486
487 if (!pvBuf)
488 {
489 rc = VERR_NO_MEMORY;
490 goto out;
491 }
492
493 switch (RT_BE2H_U32(pLocator->u32Code))
494 {
495 case VHD_PLATFORM_CODE_WI2R:
496 /* Update plain relative name. */
497 cb = (uint32_t)strlen(pszFilename);
498 if (cb > cbMaxLen)
499 {
500 rc = VERR_FILENAME_TOO_LONG;
501 goto out;
502 }
503 memcpy(pvBuf, pszFilename, cb);
504 pLocator->u32DataLength = RT_H2BE_U32(cb);
505 break;
506 case VHD_PLATFORM_CODE_WI2K:
507 /* Update plain absolute name. */
508 rc = RTPathAbs(pszFilename, (char *)pvBuf, cbMaxLen);
509 if (RT_FAILURE(rc))
510 goto out;
511 pLocator->u32DataLength = RT_H2BE_U32((uint32_t)strlen((const char *)pvBuf));
512 break;
513 case VHD_PLATFORM_CODE_W2RU:
514 /* Update unicode relative name. */
515 rc = vhdFilenameToUtf16(pszFilename, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
516 if (RT_FAILURE(rc))
517 goto out;
518 pLocator->u32DataLength = RT_H2BE_U32(cb);
519 break;
520 case VHD_PLATFORM_CODE_W2KU:
521 /* Update unicode absolute name. */
522 pszTmp = (char*)RTMemTmpAllocZ(cbMaxLen);
523 if (!pszTmp)
524 {
525 rc = VERR_NO_MEMORY;
526 goto out;
527 }
528 rc = RTPathAbs(pszFilename, pszTmp, cbMaxLen);
529 if (RT_FAILURE(rc))
530 {
531 RTMemTmpFree(pszTmp);
532 goto out;
533 }
534 rc = vhdFilenameToUtf16(pszTmp, (uint16_t *)pvBuf, cbMaxLen, &cb, false);
535 RTMemTmpFree(pszTmp);
536 if (RT_FAILURE(rc))
537 goto out;
538 pLocator->u32DataLength = RT_H2BE_U32(cb);
539 break;
540 default:
541 rc = VERR_NOT_IMPLEMENTED;
542 goto out;
543 }
544 rc = vhdFileWriteSync(pImage, RT_BE2H_U64(pLocator->u64DataOffset),
545 pvBuf, RT_BE2H_U32(pLocator->u32DataSpace) * VHD_SECTOR_SIZE,
546 NULL);
547
548out:
549 if (pvBuf)
550 RTMemTmpFree(pvBuf);
551 return rc;
552}
553
554/**
555 * Internal: Update dynamic disk header from VHDIMAGE.
556 */
557static int vhdDynamicHeaderUpdate(PVHDIMAGE pImage)
558{
559 VHDDynamicDiskHeader ddh;
560 int rc, i;
561
562 if (!pImage)
563 return VERR_VD_NOT_OPENED;
564
565 rc = vhdFileReadSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
566 if (RT_FAILURE(rc))
567 return rc;
568 if (memcmp(ddh.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE) != 0)
569 return VERR_VD_VHD_INVALID_HEADER;
570
571 uint32_t u32Checksum = RT_BE2H_U32(ddh.Checksum);
572 ddh.Checksum = 0;
573 if (u32Checksum != vhdChecksum(&ddh, sizeof(ddh)))
574 return VERR_VD_VHD_INVALID_HEADER;
575
576 /* Update parent's timestamp. */
577 ddh.ParentTimeStamp = RT_H2BE_U32(pImage->u32ParentTimeStamp);
578 /* Update parent's filename. */
579 if (pImage->pszParentFilename)
580 {
581 rc = vhdFilenameToUtf16(RTPathFilename(pImage->pszParentFilename),
582 ddh.ParentUnicodeName, sizeof(ddh.ParentUnicodeName) - 1, NULL, true);
583 if (RT_FAILURE(rc))
584 return rc;
585 }
586
587 /* Update parent's locators. */
588 for (i = 0; i < VHD_MAX_LOCATOR_ENTRIES; i++)
589 {
590 /* Skip empty locators */
591 if (ddh.ParentLocatorEntry[i].u32Code != RT_H2BE_U32(VHD_PLATFORM_CODE_NONE))
592 {
593 if (pImage->pszParentFilename)
594 {
595 rc = vhdLocatorUpdate(pImage, &ddh.ParentLocatorEntry[i], pImage->pszParentFilename);
596 if (RT_FAILURE(rc))
597 return rc;
598 }
599 else
600 {
601 /* The parent was deleted. */
602 ddh.ParentLocatorEntry[i].u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_NONE);
603 }
604 }
605 }
606 /* Update parent's UUID */
607 memcpy(ddh.ParentUuid, pImage->ParentUuid.au8, sizeof(ddh.ParentUuid));
608
609 /* Update data offset and number of table entries. */
610 ddh.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
611
612 ddh.Checksum = 0;
613 ddh.Checksum = RT_H2BE_U32(vhdChecksum(&ddh, sizeof(ddh)));
614 rc = vhdFileWriteSync(pImage, pImage->u64DataOffset, &ddh, sizeof(ddh), NULL);
615 return rc;
616}
617
618/**
619 * Internal: Update the VHD footer.
620 */
621static int vhdUpdateFooter(PVHDIMAGE pImage)
622{
623 int rc = VINF_SUCCESS;
624
625 /* Update fields which can change. */
626 pImage->vhdFooterCopy.CurSize = RT_H2BE_U64(pImage->cbSize);
627 pImage->vhdFooterCopy.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
628 pImage->vhdFooterCopy.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
629 pImage->vhdFooterCopy.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
630
631 pImage->vhdFooterCopy.Checksum = 0;
632 pImage->vhdFooterCopy.Checksum = RT_H2BE_U32(vhdChecksum(&pImage->vhdFooterCopy, sizeof(VHDFooter)));
633
634 if (pImage->pBlockAllocationTable)
635 rc = vhdFileWriteSync(pImage, 0, &pImage->vhdFooterCopy,
636 sizeof(VHDFooter), NULL);
637
638 if (RT_SUCCESS(rc))
639 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
640 &pImage->vhdFooterCopy, sizeof(VHDFooter), NULL);
641
642 return rc;
643}
644
645/**
646 * Internal. Flush image data to disk.
647 */
648static int vhdFlushImage(PVHDIMAGE pImage)
649{
650 int rc = VINF_SUCCESS;
651
652 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
653 return VINF_SUCCESS;
654
655 if (pImage->pBlockAllocationTable)
656 {
657 /*
658 * This is an expanding image. Write the BAT and copy of the disk footer.
659 */
660 size_t cbBlockAllocationTableToWrite = pImage->cBlockAllocationTableEntries * sizeof(uint32_t);
661 uint32_t *pBlockAllocationTableToWrite = (uint32_t *)RTMemAllocZ(cbBlockAllocationTableToWrite);
662
663 if (!pBlockAllocationTableToWrite)
664 return VERR_NO_MEMORY;
665
666 /*
667 * The BAT entries have to be stored in big endian format.
668 */
669 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
670 pBlockAllocationTableToWrite[i] = RT_H2BE_U32(pImage->pBlockAllocationTable[i]);
671
672 /*
673 * Write the block allocation table after the copy of the disk footer and the dynamic disk header.
674 */
675 vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
676 pBlockAllocationTableToWrite,
677 cbBlockAllocationTableToWrite, NULL);
678 if (pImage->fDynHdrNeedsUpdate)
679 rc = vhdDynamicHeaderUpdate(pImage);
680 RTMemFree(pBlockAllocationTableToWrite);
681 }
682
683 if (RT_SUCCESS(rc))
684 rc = vhdUpdateFooter(pImage);
685
686 if (RT_SUCCESS(rc))
687 rc = vhdFileFlushSync(pImage);
688
689 return rc;
690}
691
692/**
693 * Internal. Free all allocated space for representing an image except pImage,
694 * and optionally delete the image from disk.
695 */
696static int vhdFreeImage(PVHDIMAGE pImage, bool fDelete)
697{
698 int rc = VINF_SUCCESS;
699
700 /* Freeing a never allocated image (e.g. because the open failed) is
701 * not signalled as an error. After all nothing bad happens. */
702 if (pImage)
703 {
704 if (pImage->pStorage)
705 {
706 /* No point updating the file that is deleted anyway. */
707 if (!fDelete)
708 vhdFlushImage(pImage);
709
710 vhdFileClose(pImage);
711 pImage->pStorage = NULL;
712 }
713
714 if (pImage->pszParentFilename)
715 {
716 RTStrFree(pImage->pszParentFilename);
717 pImage->pszParentFilename = NULL;
718 }
719 if (pImage->pBlockAllocationTable)
720 {
721 RTMemFree(pImage->pBlockAllocationTable);
722 pImage->pBlockAllocationTable = NULL;
723 }
724 if (pImage->pu8Bitmap)
725 {
726 RTMemFree(pImage->pu8Bitmap);
727 pImage->pu8Bitmap = NULL;
728 }
729
730 if (fDelete && pImage->pszFilename)
731 rc = vhdFileDelete(pImage, pImage->pszFilename);
732 }
733
734 LogFlowFunc(("returns %Rrc\n", rc));
735 return rc;
736}
737
738/* 946684800 is the number of seconds between 1/1/1970 and 1/1/2000 */
739#define VHD_TO_UNIX_EPOCH_SECONDS UINT64_C(946684800)
740
741static uint32_t vhdRtTime2VhdTime(PCRTTIMESPEC pRtTimeStamp)
742{
743 uint64_t u64Seconds = RTTimeSpecGetSeconds(pRtTimeStamp);
744 return (uint32_t)(u64Seconds - VHD_TO_UNIX_EPOCH_SECONDS);
745}
746
747static void vhdTime2RtTime(PRTTIMESPEC pRtTimeStamp, uint32_t u32VhdTimeStamp)
748{
749 RTTimeSpecSetSeconds(pRtTimeStamp, VHD_TO_UNIX_EPOCH_SECONDS + u32VhdTimeStamp);
750}
751
752/**
753 * Internal: Allocates the block bitmap rounding up to the next 32bit or 64bit boundary.
754 * Can be freed with RTMemFree. The memory is zeroed.
755 */
756DECLINLINE(uint8_t *)vhdBlockBitmapAllocate(PVHDIMAGE pImage)
757{
758#ifdef RT_ARCH_AMD64
759 return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 8);
760#else
761 return (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap + 4);
762#endif
763}
764
765/**
766 * Internal: called when the async expansion process completed (failure or success).
767 * Will do the necessary rollback if an error occurred.
768 */
769static int vhdAsyncExpansionComplete(PVHDIMAGE pImage, PVDIOCTX pIoCtx, PVHDIMAGEEXPAND pExpand)
770{
771 int rc = VINF_SUCCESS;
772 uint32_t fFlags = ASMAtomicReadU32(&pExpand->fFlags);
773 bool fIoInProgress = false;
774
775 /* Quick path, check if everything succeeded. */
776 if (fFlags == VHDIMAGEEXPAND_ALL_SUCCESS)
777 {
778 RTMemFree(pExpand);
779 }
780 else
781 {
782 uint32_t uStatus;
783
784 uStatus = VHDIMAGEEXPAND_STATUS_GET(pExpand->fFlags, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
785 if ( uStatus == VHDIMAGEEXPAND_STEP_FAILED
786 || uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
787 {
788 /* Undo and restore the old value. */
789 pImage->pBlockAllocationTable[pExpand->idxBatAllocated] = ~0U;
790
791 /* Restore the old value on the disk.
792 * No need for a completion callback because we can't
793 * do anything if this fails. */
794 if (uStatus == VHDIMAGEEXPAND_STEP_SUCCESS)
795 {
796 rc = vhdFileWriteMetaAsync(pImage,
797 pImage->uBlockAllocationTableOffset
798 + pExpand->idxBatAllocated * sizeof(uint32_t),
799 &pImage->pBlockAllocationTable[pExpand->idxBatAllocated],
800 sizeof(uint32_t), pIoCtx, NULL, NULL);
801 fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
802 }
803 }
804
805 /* Restore old size (including the footer because another application might
806 * fill up the free space making it impossible to add the footer)
807 * and add the footer at the right place again. */
808 rc = vhdFileSetSize(pImage, pExpand->cbEofOld + sizeof(VHDFooter));
809 AssertRC(rc);
810
811 pImage->uCurrentEndOfFile = pExpand->cbEofOld;
812 rc = vhdFileWriteMetaAsync(pImage, pImage->uCurrentEndOfFile,
813 &pImage->vhdFooterCopy, sizeof(VHDFooter),
814 pIoCtx, NULL, NULL);
815 fIoInProgress |= rc == VERR_VD_ASYNC_IO_IN_PROGRESS;
816 }
817
818 return fIoInProgress ? VERR_VD_ASYNC_IO_IN_PROGRESS : rc;
819}
820
821static int vhdAsyncExpansionStepCompleted(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq, unsigned iStep)
822{
823 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
824 PVHDIMAGEEXPAND pExpand = (PVHDIMAGEEXPAND)pvUser;
825
826 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc iStep=%u\n",
827 pBackendData, pIoCtx, pvUser, rcReq, iStep));
828
829 if (RT_SUCCESS(rcReq))
830 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_SUCCESS);
831 else
832 VHDIMAGEEXPAND_STATUS_SET(pExpand->fFlags, iStep, VHDIMAGEEXPAND_STEP_FAILED);
833
834 if ((pExpand->fFlags & VHDIMAGEEXPAND_ALL_COMPLETE) == VHDIMAGEEXPAND_ALL_COMPLETE)
835 return vhdAsyncExpansionComplete(pImage, pIoCtx, pExpand);
836
837 return VERR_VD_ASYNC_IO_IN_PROGRESS;
838}
839
840static int vhdAsyncExpansionDataBlockBitmapComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
841{
842 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BLOCKBITMAP_STATUS_SHIFT);
843}
844
845static int vhdAsyncExpansionDataComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
846{
847 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_USERBLOCK_STATUS_SHIFT);
848}
849
850static int vhdAsyncExpansionBatUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
851{
852 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_BAT_STATUS_SHIFT);
853}
854
855static int vhdAsyncExpansionFooterUpdateComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
856{
857 return vhdAsyncExpansionStepCompleted(pBackendData, pIoCtx, pvUser, rcReq, VHDIMAGEEXPAND_FOOTER_STATUS_SHIFT);
858}
859
860static int vhdLoadDynamicDisk(PVHDIMAGE pImage, uint64_t uDynamicDiskHeaderOffset)
861{
862 VHDDynamicDiskHeader vhdDynamicDiskHeader;
863 int rc = VINF_SUCCESS;
864 uint32_t *pBlockAllocationTable;
865 uint64_t uBlockAllocationTableOffset;
866 unsigned i = 0;
867
868 Log(("Open a dynamic disk.\n"));
869
870 /*
871 * Read the dynamic disk header.
872 */
873 rc = vhdFileReadSync(pImage, uDynamicDiskHeaderOffset,
874 &vhdDynamicDiskHeader, sizeof(VHDDynamicDiskHeader),
875 NULL);
876 if (memcmp(vhdDynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, VHD_DYNAMIC_DISK_HEADER_COOKIE_SIZE))
877 return VERR_INVALID_PARAMETER;
878
879 pImage->cbDataBlock = RT_BE2H_U32(vhdDynamicDiskHeader.BlockSize);
880 LogFlowFunc(("BlockSize=%u\n", pImage->cbDataBlock));
881 pImage->cBlockAllocationTableEntries = RT_BE2H_U32(vhdDynamicDiskHeader.MaxTableEntries);
882 LogFlowFunc(("MaxTableEntries=%lu\n", pImage->cBlockAllocationTableEntries));
883 AssertMsg(!(pImage->cbDataBlock % VHD_SECTOR_SIZE), ("%s: Data block size is not a multiple of %!\n", __FUNCTION__, VHD_SECTOR_SIZE));
884
885 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
886 LogFlowFunc(("SectorsPerDataBlock=%u\n", pImage->cSectorsPerDataBlock));
887
888 /*
889 * Every block starts with a bitmap indicating which sectors are valid and which are not.
890 * We store the size of it to be able to calculate the real offset.
891 */
892 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
893 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
894 /* Round up to full sector size */
895 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
896 pImage->cDataBlockBitmapSectors++;
897 LogFlowFunc(("cbDataBlockBitmap=%u\n", pImage->cbDataBlockBitmap));
898 LogFlowFunc(("cDataBlockBitmapSectors=%u\n", pImage->cDataBlockBitmapSectors));
899
900 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
901 if (!pImage->pu8Bitmap)
902 return VERR_NO_MEMORY;
903
904 pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
905 if (!pBlockAllocationTable)
906 return VERR_NO_MEMORY;
907
908 /*
909 * Read the table.
910 */
911 uBlockAllocationTableOffset = RT_BE2H_U64(vhdDynamicDiskHeader.TableOffset);
912 LogFlowFunc(("uBlockAllocationTableOffset=%llu\n", uBlockAllocationTableOffset));
913 pImage->uBlockAllocationTableOffset = uBlockAllocationTableOffset;
914 rc = vhdFileReadSync(pImage, uBlockAllocationTableOffset,
915 pBlockAllocationTable,
916 pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
917 NULL);
918
919 /*
920 * Because the offset entries inside the allocation table are stored big endian
921 * we need to convert them into host endian.
922 */
923 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
924 if (!pImage->pBlockAllocationTable)
925 {
926 RTMemFree(pBlockAllocationTable);
927 return VERR_NO_MEMORY;
928 }
929
930 for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
931 pImage->pBlockAllocationTable[i] = RT_BE2H_U32(pBlockAllocationTable[i]);
932
933 RTMemFree(pBlockAllocationTable);
934
935 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF)
936 memcpy(pImage->ParentUuid.au8, vhdDynamicDiskHeader.ParentUuid, sizeof(pImage->ParentUuid));
937
938 return rc;
939}
940
941static int vhdOpenImage(PVHDIMAGE pImage, unsigned uOpenFlags)
942{
943 uint64_t FileSize;
944 VHDFooter vhdFooter;
945
946 pImage->uOpenFlags = uOpenFlags;
947
948 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
949 if (pImage->pInterfaceError)
950 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
951
952 /* Get I/O interface. */
953 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
954 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
955 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
956 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
957
958 /*
959 * Open the image.
960 */
961 int rc = vhdFileOpen(pImage, pImage->pszFilename,
962 VDOpenFlagsToFileOpenFlags(uOpenFlags,
963 false /* fCreate */));
964 if (RT_FAILURE(rc))
965 {
966 /* Do NOT signal an appropriate error here, as the VD layer has the
967 * choice of retrying the open if it failed. */
968 return rc;
969 }
970
971 rc = vhdFileGetSize(pImage, &FileSize);
972 pImage->uCurrentEndOfFile = FileSize - sizeof(VHDFooter);
973
974 rc = vhdFileReadSync(pImage, pImage->uCurrentEndOfFile,
975 &vhdFooter, sizeof(VHDFooter), NULL);
976 if (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0)
977 return VERR_VD_VHD_INVALID_HEADER;
978
979 switch (RT_BE2H_U32(vhdFooter.DiskType))
980 {
981 case VHD_FOOTER_DISK_TYPE_FIXED:
982 {
983 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
984 }
985 break;
986 case VHD_FOOTER_DISK_TYPE_DYNAMIC:
987 {
988 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
989 }
990 break;
991 case VHD_FOOTER_DISK_TYPE_DIFFERENCING:
992 {
993 pImage->uImageFlags |= VD_IMAGE_FLAGS_DIFF;
994 pImage->uImageFlags &= ~VD_IMAGE_FLAGS_FIXED;
995 }
996 break;
997 default:
998 return VERR_NOT_IMPLEMENTED;
999 }
1000
1001 pImage->cbSize = RT_BE2H_U64(vhdFooter.CurSize);
1002 pImage->LCHSGeometry.cCylinders = 0;
1003 pImage->LCHSGeometry.cHeads = 0;
1004 pImage->LCHSGeometry.cSectors = 0;
1005 pImage->PCHSGeometry.cCylinders = RT_BE2H_U16(vhdFooter.DiskGeometryCylinder);
1006 pImage->PCHSGeometry.cHeads = vhdFooter.DiskGeometryHeads;
1007 pImage->PCHSGeometry.cSectors = vhdFooter.DiskGeometrySectors;
1008
1009 /*
1010 * Copy of the disk footer.
1011 * If we allocate new blocks in differencing disks on write access
1012 * the footer is overwritten. We need to write it at the end of the file.
1013 */
1014 memcpy(&pImage->vhdFooterCopy, &vhdFooter, sizeof(VHDFooter));
1015
1016 /*
1017 * Is there a better way?
1018 */
1019 memcpy(&pImage->ImageUuid, &vhdFooter.UniqueID, 16);
1020
1021 pImage->u64DataOffset = RT_BE2H_U64(vhdFooter.DataOffset);
1022 LogFlowFunc(("DataOffset=%llu\n", pImage->u64DataOffset));
1023
1024 if (!(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED))
1025 rc = vhdLoadDynamicDisk(pImage, pImage->u64DataOffset);
1026
1027 if (RT_FAILURE(rc))
1028 vhdFreeImage(pImage, false);
1029 return rc;
1030}
1031
1032/**
1033 * Internal: Checks if a sector in the block bitmap is set
1034 */
1035DECLINLINE(bool) vhdBlockBitmapSectorContainsData(PVHDIMAGE pImage, uint32_t cBlockBitmapEntry)
1036{
1037 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
1038
1039 /*
1040 * The index of the bit in the byte of the data block bitmap.
1041 * The most significant bit stands for a lower sector number.
1042 */
1043 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
1044 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
1045
1046 AssertMsg(puBitmap < (pImage->pu8Bitmap + pImage->cbDataBlockBitmap),
1047 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
1048
1049 return ASMBitTest(puBitmap, iBitInByte);
1050}
1051
1052/**
1053 * Internal: Sets the given sector in the sector bitmap.
1054 */
1055DECLINLINE(bool) vhdBlockBitmapSectorSet(PVHDIMAGE pImage, uint8_t *pu8Bitmap, uint32_t cBlockBitmapEntry)
1056{
1057 uint32_t iBitmap = (cBlockBitmapEntry / 8); /* Byte in the block bitmap. */
1058
1059 /*
1060 * The index of the bit in the byte of the data block bitmap.
1061 * The most significant bit stands for a lower sector number.
1062 */
1063 uint8_t iBitInByte = (8-1) - (cBlockBitmapEntry % 8);
1064 uint8_t *puBitmap = pu8Bitmap + iBitmap;
1065
1066 AssertMsg(puBitmap < (pu8Bitmap + pImage->cbDataBlockBitmap),
1067 ("VHD: Current bitmap position exceeds maximum size of the bitmap\n"));
1068
1069 return !ASMBitTestAndSet(puBitmap, iBitInByte);
1070}
1071
1072/**
1073 * Internal: Derive drive geometry from its size.
1074 */
1075static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
1076{
1077 uint64_t u64TotalSectors = cbSize / VHD_SECTOR_SIZE;
1078 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
1079
1080 if (u64TotalSectors > 65535 * 16 * 255)
1081 {
1082 /* ATA disks limited to 127 GB. */
1083 u64TotalSectors = 65535 * 16 * 255;
1084 }
1085
1086 if (u64TotalSectors >= 65535 * 16 * 63)
1087 {
1088 u32SectorsPerTrack = 255;
1089 u32Heads = 16;
1090 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1091 }
1092 else
1093 {
1094 u32SectorsPerTrack = 17;
1095 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1096
1097 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
1098
1099 if (u32Heads < 4)
1100 {
1101 u32Heads = 4;
1102 }
1103 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
1104 {
1105 u32SectorsPerTrack = 31;
1106 u32Heads = 16;
1107 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1108 }
1109 if (u32CylinderTimesHeads >= (u32Heads * 1024))
1110 {
1111 u32SectorsPerTrack = 63;
1112 u32Heads = 16;
1113 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1114 }
1115 }
1116 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
1117 pImage->PCHSGeometry.cHeads = u32Heads;
1118 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
1119 pImage->LCHSGeometry.cCylinders = 0;
1120 pImage->LCHSGeometry.cHeads = 0;
1121 pImage->LCHSGeometry.cSectors = 0;
1122}
1123
1124
1125static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
1126{
1127 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
1128 /* Relative Windows path. */
1129 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
1130 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
1131 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1132 u64Offset += VHD_RELATIVE_MAX_PATH;
1133 pLocator++;
1134 /* Absolute Windows path. */
1135 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
1136 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
1137 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1138 u64Offset += VHD_ABSOLUTE_MAX_PATH;
1139 pLocator++;
1140 /* Unicode relative Windows path. */
1141 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
1142 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1143 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1144 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
1145 pLocator++;
1146 /* Unicode absolute Windows path. */
1147 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
1148 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1149 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1150 return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
1151}
1152
1153/**
1154 * Internal: Additional code for dynamic VHD image creation.
1155 */
1156static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
1157{
1158 int rc;
1159 VHDDynamicDiskHeader DynamicDiskHeader;
1160 uint32_t u32BlockAllocationTableSectors;
1161 void *pvTmp = NULL;
1162
1163 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
1164
1165 pImage->u64DataOffset = sizeof(VHDFooter);
1166 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1167 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1168 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1169 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1170 /* Align to sector boundary */
1171 if (pImage->cbDataBlockBitmap % VHD_SECTOR_SIZE > 0)
1172 pImage->cDataBlockBitmapSectors++;
1173 pImage->pu8Bitmap = vhdBlockBitmapAllocate(pImage);
1174 if (!pImage->pu8Bitmap)
1175 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1176
1177 /* Initialize BAT. */
1178 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1179 pImage->cBlockAllocationTableEntries = (uint32_t)((cbSize + pImage->cbDataBlock - 1) / pImage->cbDataBlock); /* Align table to the block size. */
1180 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1181 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1182 if (!pImage->pBlockAllocationTable)
1183 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1184
1185 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1186 {
1187 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1188 }
1189
1190 /* Round up to the sector size. */
1191 if (pImage->uImageFlags & VD_IMAGE_FLAGS_DIFF) /* fix hyper-v unreadable error */
1192 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1193 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1194 else
1195 pImage->uCurrentEndOfFile = pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE;
1196
1197 /* Set dynamic image size. */
1198 pvTmp = RTMemTmpAllocZ(pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1199 if (!pvTmp)
1200 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1201
1202 rc = vhdFileWriteSync(pImage, 0, pvTmp,
1203 pImage->uCurrentEndOfFile + sizeof(VHDFooter), NULL);
1204 if (RT_FAILURE(rc))
1205 {
1206 RTMemTmpFree(pvTmp);
1207 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1208 }
1209
1210 RTMemTmpFree(pvTmp);
1211
1212 /* Initialize and write the dynamic disk header. */
1213 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1214 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1215 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1216 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1217 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1218 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1219 /* Compute and update checksum. */
1220 DynamicDiskHeader.Checksum = 0;
1221 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1222
1223 rc = vhdFileWriteSync(pImage, sizeof(VHDFooter), &DynamicDiskHeader,
1224 sizeof(DynamicDiskHeader), NULL);
1225 if (RT_FAILURE(rc))
1226 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1227
1228 /* Write BAT. */
1229 rc = vhdFileWriteSync(pImage, pImage->uBlockAllocationTableOffset,
1230 pImage->pBlockAllocationTable,
1231 pImage->cBlockAllocationTableEntries * sizeof(uint32_t),
1232 NULL);
1233 if (RT_FAILURE(rc))
1234 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1235
1236 return rc;
1237}
1238
1239/**
1240 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1241 */
1242static int vhdCreateImage(PVHDIMAGE pImage, uint64_t cbSize,
1243 unsigned uImageFlags, const char *pszComment,
1244 PCVDGEOMETRY pPCHSGeometry,
1245 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1246 unsigned uOpenFlags,
1247 PFNVDPROGRESS pfnProgress, void *pvUser,
1248 unsigned uPercentStart, unsigned uPercentSpan)
1249{
1250 int rc;
1251 VHDFooter Footer;
1252 RTTIMESPEC now;
1253
1254 pImage->uOpenFlags = uOpenFlags;
1255 pImage->uImageFlags = uImageFlags;
1256
1257 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
1258 if (pImage->pInterfaceError)
1259 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
1260
1261 rc = vhdFileOpen(pImage, pImage->pszFilename,
1262 VDOpenFlagsToFileOpenFlags(uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
1263 true /* fCreate */));
1264 if (RT_FAILURE(rc))
1265 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1266
1267
1268 pImage->cbSize = cbSize;
1269 pImage->ImageUuid = *pUuid;
1270 RTUuidClear(&pImage->ParentUuid);
1271 vhdSetDiskGeometry(pImage, cbSize);
1272
1273 /* Initialize the footer. */
1274 memset(&Footer, 0, sizeof(Footer));
1275 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1276 Footer.Features = RT_H2BE_U32(0x2);
1277 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1278 Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1279 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1280 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1281#ifdef RT_OS_DARWIN
1282 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1283#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1284 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1285#endif
1286 Footer.OrigSize = RT_H2BE_U64(cbSize);
1287 Footer.CurSize = Footer.OrigSize;
1288 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1289 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1290 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1291 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1292 Footer.SavedState = 0;
1293
1294 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
1295 {
1296 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1297 /*
1298 * Initialize fixed image.
1299 * "The size of the entire file is the size of the hard disk in
1300 * the guest operating system plus the size of the footer."
1301 */
1302 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1303 pImage->uCurrentEndOfFile = cbSize;
1304 /** @todo r=klaus replace this with actual data writes, see the experience
1305 * with VDI files on Windows, can cause long freezes when writing. */
1306 rc = vhdFileSetSize(pImage, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1307 if (RT_FAILURE(rc))
1308 {
1309 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1310 goto out;
1311 }
1312 }
1313 else
1314 {
1315 /*
1316 * Initialize dynamic image.
1317 *
1318 * The overall structure of dynamic disk is:
1319 *
1320 * [Copy of hard disk footer (512 bytes)]
1321 * [Dynamic disk header (1024 bytes)]
1322 * [BAT (Block Allocation Table)]
1323 * [Parent Locators]
1324 * [Data block 1]
1325 * [Data block 2]
1326 * ...
1327 * [Data block N]
1328 * [Hard disk footer (512 bytes)]
1329 */
1330 Footer.DiskType = (uImageFlags & VD_IMAGE_FLAGS_DIFF)
1331 ? RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING)
1332 : RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1333 /* We are half way thorough with creation of image, let the caller know. */
1334 if (pfnProgress)
1335 pfnProgress(pvUser, (uPercentStart + uPercentSpan) / 2);
1336
1337 rc = vhdCreateDynamicImage(pImage, cbSize);
1338 if (RT_FAILURE(rc))
1339 goto out;
1340 }
1341
1342 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1343
1344 /* Compute and update the footer checksum. */
1345 Footer.Checksum = 0;
1346 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1347
1348 pImage->vhdFooterCopy = Footer;
1349
1350 /* Store the footer */
1351 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile, &Footer,
1352 sizeof(Footer), NULL);
1353 if (RT_FAILURE(rc))
1354 {
1355 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1356 goto out;
1357 }
1358
1359 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1360 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
1361 {
1362 /* Write the copy of the footer. */
1363 rc = vhdFileWriteSync(pImage, 0, &Footer, sizeof(Footer), NULL);
1364 if (RT_FAILURE(rc))
1365 {
1366 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1367 goto out;
1368 }
1369 }
1370
1371out:
1372 if (RT_SUCCESS(rc) && pfnProgress)
1373 pfnProgress(pvUser, uPercentStart + uPercentSpan);
1374
1375 if (RT_FAILURE(rc))
1376 vhdFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
1377 return rc;
1378}
1379
1380
1381/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1382static int vhdCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1383 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1384{
1385 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1386 int rc;
1387 PVDIOSTORAGE pStorage;
1388 uint64_t cbFile;
1389 VHDFooter vhdFooter;
1390
1391 /* Get I/O interface. */
1392 PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
1393 AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
1394 PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
1395 AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
1396
1397 rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
1398 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1399 false /* fCreate */),
1400 &pStorage);
1401 if (RT_FAILURE(rc))
1402 goto out;
1403
1404 rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
1405 &cbFile);
1406 if (RT_FAILURE(rc))
1407 {
1408 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
1409 rc = VERR_VD_VHD_INVALID_HEADER;
1410 goto out;
1411 }
1412
1413 rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
1414 cbFile - sizeof(VHDFooter),
1415 &vhdFooter, sizeof(VHDFooter), NULL);
1416 if (RT_FAILURE(rc) || (memcmp(vhdFooter.Cookie, VHD_FOOTER_COOKIE, VHD_FOOTER_COOKIE_SIZE) != 0))
1417 rc = VERR_VD_VHD_INVALID_HEADER;
1418 else
1419 {
1420 *penmType = VDTYPE_HDD;
1421 rc = VINF_SUCCESS;
1422 }
1423
1424 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
1425
1426out:
1427 LogFlowFunc(("returns %Rrc\n", rc));
1428 return rc;
1429}
1430
1431/** @copydoc VBOXHDDBACKEND::pfnOpen */
1432static int vhdOpen(const char *pszFilename, unsigned uOpenFlags,
1433 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1434 VDTYPE enmType, void **ppBackendData)
1435{
1436 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
1437 int rc = VINF_SUCCESS;
1438 PVHDIMAGE pImage;
1439
1440 /* Check open flags. All valid flags are supported. */
1441 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1442 {
1443 rc = VERR_INVALID_PARAMETER;
1444 goto out;
1445 }
1446
1447 /* Check remaining arguments. */
1448 if ( !VALID_PTR(pszFilename)
1449 || !*pszFilename)
1450 {
1451 rc = VERR_INVALID_PARAMETER;
1452 goto out;
1453 }
1454
1455 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1456 if (!pImage)
1457 {
1458 rc = VERR_NO_MEMORY;
1459 goto out;
1460 }
1461
1462 pImage->pszFilename = pszFilename;
1463 pImage->pStorage = NULL;
1464 pImage->pVDIfsDisk = pVDIfsDisk;
1465 pImage->pVDIfsImage = pVDIfsImage;
1466
1467 rc = vhdOpenImage(pImage, uOpenFlags);
1468 if (RT_SUCCESS(rc))
1469 *ppBackendData = pImage;
1470 else
1471 RTMemFree(pImage);
1472
1473out:
1474 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1475 return rc;
1476}
1477
1478/** @copydoc VBOXHDDBACKEND::pfnCreate */
1479static int vhdCreate(const char *pszFilename, uint64_t cbSize,
1480 unsigned uImageFlags, const char *pszComment,
1481 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1482 PCRTUUID pUuid, unsigned uOpenFlags,
1483 unsigned uPercentStart, unsigned uPercentSpan,
1484 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1485 PVDINTERFACE pVDIfsOperation, void **ppBackendData)
1486{
1487 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));
1488 int rc = VINF_SUCCESS;
1489 PVHDIMAGE pImage;
1490
1491 PFNVDPROGRESS pfnProgress = NULL;
1492 void *pvUser = NULL;
1493 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1494 VDINTERFACETYPE_PROGRESS);
1495 PVDINTERFACEPROGRESS pCbProgress = NULL;
1496 if (pIfProgress)
1497 {
1498 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1499 if (pCbProgress)
1500 pfnProgress = pCbProgress->pfnProgress;
1501 pvUser = pIfProgress->pvUser;
1502 }
1503
1504 /* Check open flags. All valid flags are supported. */
1505 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1506 {
1507 rc = VERR_INVALID_PARAMETER;
1508 return rc;
1509 }
1510
1511 /* @todo Check the values of other params */
1512
1513 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1514 if (!pImage)
1515 {
1516 rc = VERR_NO_MEMORY;
1517 return rc;
1518 }
1519 pImage->pszFilename = pszFilename;
1520 pImage->pStorage = NULL;
1521 pImage->pVDIfsDisk = pVDIfsDisk;
1522 pImage->pVDIfsImage = pVDIfsImage;
1523
1524 /* Get I/O interface. */
1525 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
1526 if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIO)))
1527 {
1528 RTMemFree(pImage);
1529 return VERR_INVALID_PARAMETER;
1530 }
1531 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
1532 if (RT_UNLIKELY(!VALID_PTR(pImage->pInterfaceIOCallbacks)))
1533 {
1534 RTMemFree(pImage);
1535 return VERR_INVALID_PARAMETER;
1536 }
1537
1538 rc = vhdCreateImage(pImage, cbSize, uImageFlags, pszComment,
1539 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1540 pfnProgress, pvUser, uPercentStart, uPercentSpan);
1541
1542 if (RT_SUCCESS(rc))
1543 {
1544 /* So far the image is opened in read/write mode. Make sure the
1545 * image is opened in read-only mode if the caller requested that. */
1546 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1547 {
1548 vhdFreeImage(pImage, false);
1549 rc = vhdOpenImage(pImage, uOpenFlags);
1550 if (RT_FAILURE(rc))
1551 {
1552 RTMemFree(pImage);
1553 goto out;
1554 }
1555 }
1556 *ppBackendData = pImage;
1557 }
1558 else
1559 RTMemFree(pImage);
1560
1561out:
1562 LogFlowFunc(("returns %Rrc\n", rc));
1563 return rc;
1564}
1565
1566/** @copydoc VBOXHDDBACKEND::pfnRename */
1567static int vhdRename(void *pBackendData, const char *pszFilename)
1568{
1569 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1570 int rc = VINF_SUCCESS;
1571 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1572
1573 /* Check arguments. */
1574 if ( !pImage
1575 || !pszFilename
1576 || !*pszFilename)
1577 {
1578 rc = VERR_INVALID_PARAMETER;
1579 goto out;
1580 }
1581
1582 /* Close the image. */
1583 rc = vhdFreeImage(pImage, false);
1584 if (RT_FAILURE(rc))
1585 goto out;
1586
1587 /* Rename the file. */
1588 rc = vhdFileMove(pImage, pImage->pszFilename, pszFilename, 0);
1589 if (RT_FAILURE(rc))
1590 {
1591 /* The move failed, try to reopen the original image. */
1592 int rc2 = vhdOpenImage(pImage, pImage->uOpenFlags);
1593 if (RT_FAILURE(rc2))
1594 rc = rc2;
1595
1596 goto out;
1597 }
1598
1599 /* Update pImage with the new information. */
1600 pImage->pszFilename = pszFilename;
1601
1602 /* Open the old file with new name. */
1603 rc = vhdOpenImage(pImage, pImage->uOpenFlags);
1604 if (RT_FAILURE(rc))
1605 goto out;
1606
1607out:
1608 LogFlowFunc(("returns %Rrc\n", rc));
1609 return rc;
1610}
1611
1612/** @copydoc VBOXHDDBACKEND::pfnClose */
1613static int vhdClose(void *pBackendData, bool fDelete)
1614{
1615 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1616 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1617 int rc;
1618
1619 rc = vhdFreeImage(pImage, fDelete);
1620 RTMemFree(pImage);
1621
1622 LogFlowFunc(("returns %Rrc\n", rc));
1623 return rc;
1624}
1625
1626/** @copydoc VBOXHDDBACKEND::pfnRead */
1627static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
1628 size_t cbBuf, size_t *pcbActuallyRead)
1629{
1630 LogFlowFunc(("pBackendData=%p uOffset=%#llx pvBuf=%p cbBuf=%u pcbActuallyRead=%p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbActuallyRead));
1631 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1632 int rc = VINF_SUCCESS;
1633
1634 if (uOffset + cbBuf > pImage->cbSize)
1635 {
1636 rc = VERR_INVALID_PARAMETER;
1637 goto out;
1638 }
1639
1640 /*
1641 * If we have a dynamic disk image, we need to find the data block and sector to read.
1642 */
1643 if (pImage->pBlockAllocationTable)
1644 {
1645 /*
1646 * Get the data block first.
1647 */
1648 uint32_t cBlockAllocationTableEntry = (uOffset / VHD_SECTOR_SIZE) / pImage->cSectorsPerDataBlock;
1649 uint32_t cBATEntryIndex = (uOffset / VHD_SECTOR_SIZE) % pImage->cSectorsPerDataBlock;
1650 uint64_t uVhdOffset;
1651
1652 LogFlowFunc(("cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", cBlockAllocationTableEntry, cBATEntryIndex));
1653 LogFlowFunc(("BlockAllocationEntry=%u\n", pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
1654
1655 /*
1656 * If the block is not allocated the content of the entry is ~0
1657 */
1658 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1659 {
1660 /* Return block size as read. */
1661 *pcbActuallyRead = RT_MIN(cbBuf, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
1662 rc = VERR_VD_BLOCK_FREE;
1663 goto out;
1664 }
1665
1666 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1667 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1668
1669 /*
1670 * Clip read range to remain in this data block.
1671 */
1672 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1673
1674 /* Read in the block's bitmap. */
1675 rc = vhdFileReadSync(pImage,
1676 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1677 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1678 NULL);
1679 if (RT_SUCCESS(rc))
1680 {
1681 uint32_t cSectors = 0;
1682
1683 if (vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1684 {
1685 cBATEntryIndex++;
1686 cSectors = 1;
1687
1688 /*
1689 * The first sector being read is marked dirty, read as much as we
1690 * can from child. Note that only sectors that are marked dirty
1691 * must be read from child.
1692 */
1693 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1694 && vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1695 {
1696 cBATEntryIndex++;
1697 cSectors++;
1698 }
1699
1700 cbBuf = cSectors * VHD_SECTOR_SIZE;
1701
1702 LogFlowFunc(("uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1703 rc = vhdFileReadSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
1704 }
1705 else
1706 {
1707 /*
1708 * The first sector being read is marked clean, so we should read from
1709 * our parent instead, but only as much as there are the following
1710 * clean sectors, because the block may still contain dirty sectors
1711 * further on. We just need to compute the number of clean sectors
1712 * and pass it to our caller along with the notification that they
1713 * should be read from the parent.
1714 */
1715 cBATEntryIndex++;
1716 cSectors = 1;
1717
1718 while ( (cSectors < (cbBuf / VHD_SECTOR_SIZE))
1719 && !vhdBlockBitmapSectorContainsData(pImage, cBATEntryIndex))
1720 {
1721 cBATEntryIndex++;
1722 cSectors++;
1723 }
1724
1725 cbBuf = cSectors * VHD_SECTOR_SIZE;
1726 LogFunc(("Sectors free: uVhdOffset=%llu cbBuf=%u\n", uVhdOffset, cbBuf));
1727 rc = VERR_VD_BLOCK_FREE;
1728 }
1729 }
1730 else
1731 AssertMsgFailed(("Reading block bitmap failed rc=%Rrc\n", rc));
1732 }
1733 else
1734 {
1735 rc = vhdFileReadSync(pImage, uOffset, pvBuf, cbBuf, NULL);
1736 }
1737
1738 if (RT_SUCCESS(rc))
1739 {
1740 if (pcbActuallyRead)
1741 *pcbActuallyRead = cbBuf;
1742
1743 Log2(("vhdRead: off=%#llx pvBuf=%p cbBuf=%d\n"
1744 "%.*Rhxd\n",
1745 uOffset, pvBuf, cbBuf, cbBuf, pvBuf));
1746 }
1747
1748out:
1749 LogFlowFunc(("returns %Rrc\n", rc));
1750 return rc;
1751}
1752
1753/** @copydoc VBOXHDDBACKEND::pfnWrite */
1754static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
1755 size_t cbBuf, size_t *pcbWriteProcess,
1756 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
1757{
1758 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbBuf=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess));
1759 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1760 int rc = VINF_SUCCESS;
1761
1762 LogFlowFunc(("pBackendData=%p uOffset=%llu pvBuf=%p cbBuf=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1763 pBackendData, uOffset, pvBuf, cbBuf, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite));
1764
1765 AssertPtr(pImage);
1766 Assert(uOffset % VHD_SECTOR_SIZE == 0);
1767 Assert(cbBuf % VHD_SECTOR_SIZE == 0);
1768
1769 if (pImage->pBlockAllocationTable)
1770 {
1771 /*
1772 * Get the data block first.
1773 */
1774 uint32_t cSector = uOffset / VHD_SECTOR_SIZE;
1775 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1776 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1777 uint64_t uVhdOffset;
1778
1779 /*
1780 * Clip write range.
1781 */
1782 cbBuf = RT_MIN(cbBuf, (pImage->cbDataBlock - (cBATEntryIndex * VHD_SECTOR_SIZE)));
1783
1784 /*
1785 * If the block is not allocated the content of the entry is ~0
1786 * and we need to allocate a new block. Note that while blocks are
1787 * allocated with a relatively big granularity, each sector has its
1788 * own bitmap entry, indicating whether it has been written or not.
1789 * So that means for the purposes of the higher level that the
1790 * granularity is invisible. This means there's no need to return
1791 * VERR_VD_BLOCK_FREE unless the block hasn't been allocated yet.
1792 */
1793 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1794 {
1795 /* Check if the block allocation should be suppressed. */
1796 if (fWrite & VD_WRITE_NO_ALLOC)
1797 {
1798 *pcbPreRead = cBATEntryIndex * VHD_SECTOR_SIZE;
1799 *pcbPostRead = pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE - cbBuf - *pcbPreRead;
1800
1801 if (pcbWriteProcess)
1802 *pcbWriteProcess = cbBuf;
1803 rc = VERR_VD_BLOCK_FREE;
1804 goto out;
1805 }
1806
1807 size_t cbNewBlock = pImage->cbDataBlock + (pImage->cDataBlockBitmapSectors * VHD_SECTOR_SIZE);
1808 uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
1809
1810 if (!pNewBlock)
1811 {
1812 rc = VERR_NO_MEMORY;
1813 goto out;
1814 }
1815
1816 /*
1817 * Write the new block at the current end of the file.
1818 */
1819 rc = vhdFileWriteSync(pImage, pImage->uCurrentEndOfFile,
1820 pNewBlock, cbNewBlock, NULL);
1821 AssertRC(rc);
1822
1823 /*
1824 * Set the new end of the file and link the new block into the BAT.
1825 */
1826 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / VHD_SECTOR_SIZE;
1827 pImage->uCurrentEndOfFile += cbNewBlock;
1828 RTMemFree(pNewBlock);
1829
1830 /* Write the updated BAT and the footer to remain in a consistent state. */
1831 rc = vhdFlushImage(pImage);
1832 AssertRC(rc);
1833 }
1834
1835 /*
1836 * Calculate the real offset in the file.
1837 */
1838 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * VHD_SECTOR_SIZE;
1839
1840 /* Write data. */
1841 vhdFileWriteSync(pImage, uVhdOffset, pvBuf, cbBuf, NULL);
1842
1843 /* Read in the block's bitmap. */
1844 rc = vhdFileReadSync(pImage,
1845 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1846 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1847 NULL);
1848 if (RT_SUCCESS(rc))
1849 {
1850 bool fChanged = false;
1851
1852 /* Set the bits for all sectors having been written. */
1853 for (uint32_t iSector = 0; iSector < (cbBuf / VHD_SECTOR_SIZE); iSector++)
1854 {
1855 fChanged |= vhdBlockBitmapSectorSet(pImage, pImage->pu8Bitmap, cBATEntryIndex);
1856 cBATEntryIndex++;
1857 }
1858
1859 if (fChanged)
1860 {
1861 /* Write the bitmap back. */
1862 rc = vhdFileWriteSync(pImage,
1863 ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry]) * VHD_SECTOR_SIZE,
1864 pImage->pu8Bitmap, pImage->cbDataBlockBitmap,
1865 NULL);
1866 }
1867 }
1868 }
1869 else
1870 {
1871 rc = vhdFileWriteSync(pImage, uOffset, pvBuf, cbBuf, NULL);
1872 }
1873
1874 if (pcbWriteProcess)
1875 *pcbWriteProcess = cbBuf;
1876
1877 /* Stay on the safe side. Do not run the risk of confusing the higher
1878 * level, as that can be pretty lethal to image consistency. */
1879 *pcbPreRead = 0;
1880 *pcbPostRead = 0;
1881
1882out:
1883 LogFlowFunc(("returns %Rrc\n", rc));
1884 return rc;
1885}
1886
1887/** @copydoc VBOXHDDBACKEND::pfnFlush */
1888static int vhdFlush(void *pBackendData)
1889{
1890 LogFlowFunc(("pBackendData=%#p", pBackendData));
1891 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1892 int rc;
1893
1894 rc = vhdFlushImage(pImage);
1895 LogFlowFunc(("returns %Rrc\n", rc));
1896 return rc;
1897}
1898
1899/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
1900static unsigned vhdGetVersion(void *pBackendData)
1901{
1902 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1903 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1904 unsigned ver = 0;
1905
1906 AssertPtr(pImage);
1907
1908 if (pImage)
1909 ver = 1; /**< @todo use correct version */
1910
1911 LogFlowFunc(("returns %u\n", ver));
1912 return ver;
1913}
1914
1915/** @copydoc VBOXHDDBACKEND::pfnGetSize */
1916static uint64_t vhdGetSize(void *pBackendData)
1917{
1918 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1919 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1920 uint64_t cb = 0;
1921
1922 AssertPtr(pImage);
1923
1924 if (pImage && pImage->pStorage)
1925 cb = pImage->cbSize;
1926
1927 LogFlowFunc(("returns %llu\n", cb));
1928 return cb;
1929}
1930
1931/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
1932static uint64_t vhdGetFileSize(void *pBackendData)
1933{
1934 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1935 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1936 uint64_t cb = 0;
1937
1938 AssertPtr(pImage);
1939
1940 if (pImage && pImage->pStorage)
1941 cb = pImage->uCurrentEndOfFile + sizeof(VHDFooter);
1942
1943 LogFlowFunc(("returns %lld\n", cb));
1944 return cb;
1945}
1946
1947/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
1948static int vhdGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
1949{
1950 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
1951 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1952 int rc;
1953
1954 AssertPtr(pImage);
1955
1956 if (pImage)
1957 {
1958 if (pImage->PCHSGeometry.cCylinders)
1959 {
1960 *pPCHSGeometry = pImage->PCHSGeometry;
1961 rc = VINF_SUCCESS;
1962 }
1963 else
1964 rc = VERR_VD_GEOMETRY_NOT_SET;
1965 }
1966 else
1967 rc = VERR_VD_NOT_OPENED;
1968
1969 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors));
1970 return rc;
1971}
1972
1973/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1974static int vhdSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
1975{
1976 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1977 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1978 int rc;
1979
1980 AssertPtr(pImage);
1981
1982 if (pImage)
1983 {
1984 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1985 {
1986 rc = VERR_VD_IMAGE_READ_ONLY;
1987 goto out;
1988 }
1989
1990 pImage->PCHSGeometry = *pPCHSGeometry;
1991 rc = VINF_SUCCESS;
1992 }
1993 else
1994 rc = VERR_VD_NOT_OPENED;
1995
1996out:
1997 LogFlowFunc(("returns %Rrc\n", rc));
1998 return rc;
1999}
2000
2001/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
2002static int vhdGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
2003{
2004LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2005 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2006 int rc;
2007
2008 AssertPtr(pImage);
2009
2010 if (pImage)
2011 {
2012 if (pImage->LCHSGeometry.cCylinders)
2013 {
2014 *pLCHSGeometry = pImage->LCHSGeometry;
2015 rc = VINF_SUCCESS;
2016 }
2017 else
2018 rc = VERR_VD_GEOMETRY_NOT_SET;
2019 }
2020 else
2021 rc = VERR_VD_NOT_OPENED;
2022
2023 LogFlowFunc(("returns %Rrc (CHS=%u/%u/%u)\n", rc, pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors));
2024 return rc;
2025}
2026
2027/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
2028static int vhdSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
2029{
2030 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2031 int rc;
2032
2033 AssertPtr(pImage);
2034
2035 if (pImage)
2036 {
2037 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2038 {
2039 rc = VERR_VD_IMAGE_READ_ONLY;
2040 goto out;
2041 }
2042
2043 pImage->LCHSGeometry = *pLCHSGeometry;
2044 rc = VINF_SUCCESS;
2045 }
2046 else
2047 rc = VERR_VD_NOT_OPENED;
2048
2049out:
2050 LogFlowFunc(("returns %Rrc\n", rc));
2051 return rc;
2052}
2053
2054/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
2055static unsigned vhdGetImageFlags(void *pBackendData)
2056{
2057 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2058 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2059 unsigned uImageFlags;
2060
2061 AssertPtr(pImage);
2062
2063 if (pImage)
2064 uImageFlags = pImage->uImageFlags;
2065 else
2066 uImageFlags = 0;
2067
2068 LogFlowFunc(("returns %#x\n", uImageFlags));
2069 return uImageFlags;
2070}
2071
2072/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
2073static unsigned vhdGetOpenFlags(void *pBackendData)
2074{
2075 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2076 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2077 unsigned uOpenFlags;
2078
2079 AssertPtr(pImage);
2080
2081 if (pImage)
2082 uOpenFlags = pImage->uOpenFlags;
2083 else
2084 uOpenFlags = 0;
2085
2086 LogFlowFunc(("returns %#x\n", uOpenFlags));
2087 return uOpenFlags;
2088}
2089
2090/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
2091static int vhdSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2092{
2093 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2094 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2095 int rc;
2096
2097 /* Image must be opened and the new flags must be valid. */
2098 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL)))
2099 {
2100 rc = VERR_INVALID_PARAMETER;
2101 goto out;
2102 }
2103
2104 /* Implement this operation via reopening the image. */
2105 rc = vhdFreeImage(pImage, false);
2106 if (RT_FAILURE(rc))
2107 goto out;
2108 rc = vhdOpenImage(pImage, uOpenFlags);
2109
2110out:
2111 LogFlowFunc(("returns %Rrc\n", rc));
2112 return rc;
2113}
2114
2115/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2116static int vhdGetComment(void *pBackendData, char *pszComment,
2117 size_t cbComment)
2118{
2119 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2120 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2121 int rc;
2122
2123 AssertPtr(pImage);
2124
2125 if (pImage)
2126 rc = VERR_NOT_SUPPORTED;
2127 else
2128 rc = VERR_VD_NOT_OPENED;
2129
2130 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2131 return rc;
2132}
2133
2134/** @copydoc VBOXHDDBACKEND::pfnSetComment */
2135static int vhdSetComment(void *pBackendData, const char *pszComment)
2136{
2137 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2138 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
2139 int rc;
2140
2141 AssertPtr(pImage);
2142
2143 if (pImage)
2144 {
2145 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2146 rc = VERR_VD_IMAGE_READ_ONLY;
2147 else
2148 rc = VERR_NOT_SUPPORTED;
2149 }
2150 else
2151 rc = VERR_VD_NOT_OPENED;
2152
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 find 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 = (unsigned)(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 overwriting 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