VirtualBox

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

最後變更 在這個檔案從27806是 27358,由 vboxsync 提交於 15 年 前

VHD: Dump a bit of information

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

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