VirtualBox

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

Last change on this file since 28125 was 27808, checked in by vboxsync, 15 years ago

VBoxHDD: First part of the async I/O support

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

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