VirtualBox

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

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

API: big medium handling change and lots of assorted other cleanups and fixes

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