VirtualBox

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

Last change on this file since 20466 was 20416, checked in by vboxsync, 16 years ago

VHD: Fix cloning (public #4080)

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