VirtualBox

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

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

Devices/Storage: a number of small memory leaks in error paths

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette