VirtualBox

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

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

VHD: Fix possible data corruption

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

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