VirtualBox

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

Last change on this file since 20567 was 20473, checked in by vboxsync, 16 years ago

VHD: Fix crash reported on vbox-dev when discarding snapshots (missing check if a parent filename is actually existing)

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

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