VirtualBox

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

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

VHD: Fix incompatibility with Virtual PC

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

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