VirtualBox

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

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

iprt/asm*.h: split out asm-math.h, don't include asm-*.h from asm.h, don't include asm.h from sup.h. Fixed a couple file headers.

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