VirtualBox

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

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

VHD: Forgot to remove the alignment

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