VirtualBox

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

Last change on this file since 12767 was 12762, checked in by vboxsync, 16 years ago

VHD: fix bug which prevents to boot from images which are expanding and over 4GB in size

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