VirtualBox

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

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

VHD: Fix image creation

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

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