VirtualBox

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

Last change on this file since 31673 was 31379, checked in by vboxsync, 15 years ago

VHDHDDCore: typo

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