VirtualBox

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

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

VHD: Backout r38444. Caused another problem and #3263 will be fixed in another way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.5 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 void vhdFreeImageMemory(PVHDIMAGE pImage)
805{
806 if (pImage->pszParentFilename)
807 {
808 RTStrFree(pImage->pszParentFilename);
809 pImage->pszParentFilename = NULL;
810 }
811 if (pImage->pBlockAllocationTable)
812 {
813 RTMemFree(pImage->pBlockAllocationTable);
814 pImage->pBlockAllocationTable = NULL;
815 }
816 if (pImage->pu8Bitmap)
817 {
818 RTMemFree(pImage->pu8Bitmap);
819 pImage->pu8Bitmap = NULL;
820 }
821 RTMemFree(pImage);
822}
823
824static int vhdFreeImage(PVHDIMAGE pImage)
825{
826 int rc = VINF_SUCCESS;
827
828 /* Freeing a never allocated image (e.g. because the open failed) is
829 * not signalled as an error. After all nothing bad happens. */
830 if (pImage) {
831 vhdFlush(pImage);
832 RTFileClose(pImage->File);
833 vhdFreeImageMemory(pImage);
834 }
835
836 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
837 return rc;
838}
839
840static int vhdClose(void *pBackendData, bool fDelete)
841{
842 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
843 int rc = VINF_SUCCESS;
844
845 /* Freeing a never allocated image (e.g. because the open failed) is
846 * not signalled as an error. After all nothing bad happens. */
847 if (pImage) {
848 if (fDelete)
849 {
850 /* No point in updating the file that is deleted anyway. */
851 RTFileClose(pImage->File);
852 RTFileDelete(pImage->pszFilename);
853 vhdFreeImageMemory(pImage);
854 }
855 else
856 rc = vhdFreeImage(pImage);
857 }
858
859 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
860 return rc;
861}
862
863static int vhdRead(void *pBackendData, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbActuallyRead)
864{
865 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
866 int rc = VINF_SUCCESS;
867
868 LogFlow(("%s: pBackendData=%p uOffset=%#llx pvBuf=%p cbRead=%u pcbActuallyRead=%p\n", __FUNCTION__, pBackendData, uOffset, pvBuf, cbRead, pcbActuallyRead));
869
870 if (uOffset + cbRead > pImage->cbSize)
871 return VERR_INVALID_PARAMETER;
872
873 /*
874 * If we have a dynamic disk image, we need to find the data block and sector to read.
875 */
876 if (pImage->pBlockAllocationTable)
877 {
878 /*
879 * Get the data block first.
880 */
881 uint32_t cBlockAllocationTableEntry = (uOffset / 512) / pImage->cSectorsPerDataBlock;
882 uint32_t cBATEntryIndex = (uOffset / 512) % pImage->cSectorsPerDataBlock;
883 uint64_t uVhdOffset;
884
885 LogFlow(("%s: cBlockAllocationTableEntry=%u cBatEntryIndex=%u\n", __FUNCTION__, cBlockAllocationTableEntry, cBATEntryIndex));
886 LogFlow(("%s: BlockAllocationEntry=%u\n", __FUNCTION__, pImage->pBlockAllocationTable[cBlockAllocationTableEntry]));
887
888 /*
889 * If the block is not allocated the content of the entry is ~0
890 */
891 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
892 {
893 /* Return block size as read. */
894 *pcbActuallyRead = RT_MIN(cbRead, pImage->cSectorsPerDataBlock * VHD_SECTOR_SIZE);
895 return VERR_VDI_BLOCK_FREE;
896 }
897
898 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * 512;
899 Log(("%s: uVhdOffset=%llu cbRead=%u\n", __FUNCTION__, uVhdOffset, cbRead));
900
901 /*
902 * Clip read range to remain in this data block.
903 */
904 cbRead = RT_MIN(cbRead, (pImage->cbDataBlock - (cBATEntryIndex * 512)));
905
906 /* Read in the block's bitmap. */
907 rc = RTFileReadAt(pImage->File,
908 (uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] * VHD_SECTOR_SIZE,
909 pImage->pu8Bitmap, pImage->cbDataBlockBitmap, NULL);
910 if (RT_SUCCESS(rc))
911 {
912 uint32_t cSectors = 0;
913 uint32_t iBitmap = cBATEntryIndex / 8; /* Byte in the block bitmap. */
914 Assert(iBitmap < pImage->cbDataBlockBitmap);
915
916 /*
917 * The index of the bit in the byte of the data block bitmap.
918 * The most signifcant bit stands for a lower sector number.
919 *
920 * There are 8 bits in a byte but they go from 0 .. 7
921 * hence the (8 - 1) to get the index in the bitmap byte.
922 */
923 uint8_t iBitInByte = (8 - 1) - (cBATEntryIndex % 8);
924 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
925
926 if (ASMBitTest(puBitmap, iBitInByte))
927 {
928 uint32_t iBATEntryIndexCurr = cBATEntryIndex + 1;
929
930 /*
931 * The first sector being read is marked dirty, read as much as we
932 * can from child. Note that only sectors that are marked dirty
933 * must be read from child.
934 */
935 do
936 {
937 cSectors++;
938
939 iBitmap = iBATEntryIndexCurr / 8; /* Byte in the block bitmap. */
940 iBitInByte = (8 - 1) - (iBATEntryIndexCurr % 8);
941 puBitmap = pImage->pu8Bitmap + iBitmap;
942 if (!ASMBitTest(puBitmap, iBitInByte))
943 break;
944
945 iBATEntryIndexCurr++;
946 } while (cSectors < (cbRead / VHD_SECTOR_SIZE));
947
948 cbRead = cSectors * VHD_SECTOR_SIZE;
949
950 Log(("%s: uVhdOffset=%llu cbRead=%u\n", __FUNCTION__, uVhdOffset, cbRead));
951 rc = RTFileReadAt(pImage->File, uVhdOffset, pvBuf, cbRead, NULL);
952 }
953 else
954 {
955 uint32_t iBATEntryIndexCurr = cBATEntryIndex + 1;
956
957 /*
958 * The first sector being read is marked clean, so we should read from
959 * our parent instead, but only as much as there are the following
960 * clean sectors, because the block may still contain dirty sectors
961 * further on. We just need to compute the number of clean sectors
962 * and pass it to our caller along with the notification that they
963 * should be read from the parent.
964 */
965 do
966 {
967 cSectors++;
968
969 iBitmap = iBATEntryIndexCurr / 8; /* Byte in the block bitmap. */
970 iBitInByte = (8 - 1) - (iBATEntryIndexCurr % 8);
971 puBitmap = pImage->pu8Bitmap + iBitmap;
972 if (ASMBitTest(puBitmap, iBitInByte))
973 break;
974
975 iBATEntryIndexCurr++;
976 } while (cSectors < (cbRead / VHD_SECTOR_SIZE));
977
978 cbRead = cSectors * VHD_SECTOR_SIZE;
979 Log(("%s: Sectors free: uVhdOffset=%llu cbRead=%u\n", __FUNCTION__, uVhdOffset, cbRead));
980 rc = VERR_VDI_BLOCK_FREE;
981 }
982 }
983 else
984 AssertMsgFailed(("Reading block bitmap failed rc=%Vrc\n", rc));
985 }
986 else
987 {
988 rc = RTFileReadAt(pImage->File, uOffset, pvBuf, cbRead, NULL);
989 }
990
991 if (pcbActuallyRead)
992 *pcbActuallyRead = cbRead;
993
994 Log2(("vhdRead: off=%#llx pvBuf=%p cbRead=%d\n"
995 "%.*Vhxd\n",
996 uOffset, pvBuf, cbRead, cbRead, pvBuf));
997
998 return rc;
999}
1000
1001static int vhdWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
1002{
1003 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1004 int rc = VINF_SUCCESS;
1005
1006 LogFlow(("%s: pBackendData=%p uOffset=%llu pvBuf=%p cbWrite=%u pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p fWrite=%u\n",
1007 __FUNCTION__, pBackendData, uOffset, pvBuf, cbWrite, pcbPreRead, pcbPostRead, fWrite));
1008
1009 Assert(uOffset % 512 == 0);
1010 Assert(cbWrite % 512 == 0);
1011
1012 if (pImage->pBlockAllocationTable)
1013 {
1014 /*
1015 * Get the data block first.
1016 */
1017 uint32_t cSector = uOffset / 512;
1018 uint32_t cBlockAllocationTableEntry = cSector / pImage->cSectorsPerDataBlock;
1019 uint32_t cBATEntryIndex = cSector % pImage->cSectorsPerDataBlock;
1020 uint64_t uVhdOffset;
1021
1022 /*
1023 * If the block is not allocated the content of the entry is ~0
1024 * and we need to allocate a new block.
1025 */
1026 if (pImage->pBlockAllocationTable[cBlockAllocationTableEntry] == ~0U)
1027 {
1028 size_t cbNewBlock = (pImage->cbDataBlock + pImage->cbDataBlockBitmap) * sizeof(uint8_t);
1029 uint8_t *pNewBlock = (uint8_t *)RTMemAllocZ(cbNewBlock);
1030
1031 if (!pNewBlock)
1032 return VERR_NO_MEMORY;
1033
1034 /*
1035 * Write the new block at the current end of the file.
1036 */
1037 rc = RTFileWriteAt(pImage->File, pImage->uCurrentEndOfFile, pNewBlock, cbNewBlock, NULL);
1038
1039 /*
1040 * Set the new end of the file and link the new block into the BAT.
1041 */
1042 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] = pImage->uCurrentEndOfFile / 512;
1043 pImage->uCurrentEndOfFile += cbNewBlock;
1044 RTMemFree(pNewBlock);
1045 }
1046
1047 /*
1048 * Calculate the real offset in the file.
1049 */
1050 uVhdOffset = ((uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] + pImage->cDataBlockBitmapSectors + cBATEntryIndex) * 512;
1051
1052 /*
1053 * Clip write range.
1054 */
1055 cbWrite = RT_MIN(cbWrite, (pImage->cbDataBlock - (cBATEntryIndex * 512)));
1056 RTFileWriteAt(pImage->File, uVhdOffset, pvBuf, cbWrite, NULL);
1057
1058 /* Read in the block's bitmap. */
1059 rc = RTFileReadAt(pImage->File,
1060 (uint64_t)pImage->pBlockAllocationTable[cBlockAllocationTableEntry] * VHD_SECTOR_SIZE,
1061 pImage->pu8Bitmap, pImage->cbDataBlockBitmap, NULL);
1062 if (RT_SUCCESS(rc))
1063 {
1064 /* Set the bits for all sectors having been written. */
1065 for (uint32_t iSector = 0; iSector < (cbWrite / VHD_SECTOR_SIZE); iSector++)
1066 {
1067 uint32_t iBitmap = cBATEntryIndex / 8; /* Byte in the block bitmap. */
1068 uint8_t iBitInByte = (8 - 1) - (cBATEntryIndex % 8);
1069 uint8_t *puBitmap = pImage->pu8Bitmap + iBitmap;
1070
1071 ASMBitSet(puBitmap, iBitInByte);
1072 cBATEntryIndex++;
1073 }
1074
1075 /* Write the bitmap back. */
1076 rc = RTFileWriteAt(pImage->File,
1077 pImage->pBlockAllocationTable[cBlockAllocationTableEntry] * VHD_SECTOR_SIZE,
1078 pImage->pu8Bitmap, pImage->cbDataBlockBitmap, NULL);
1079 }
1080 }
1081 else
1082 {
1083 rc = RTFileWriteAt(pImage->File, uOffset, pvBuf, cbWrite, NULL);
1084 }
1085
1086 if (pcbWriteProcess)
1087 *pcbWriteProcess = cbWrite;
1088
1089 return rc;
1090}
1091
1092static int vhdFlush(void *pBackendData)
1093{
1094 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1095
1096 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1097 return VINF_SUCCESS;
1098
1099 if (pImage->pBlockAllocationTable)
1100 {
1101 /*
1102 * This is an expanding image. Write the BAT and copy of the disk footer.
1103 */
1104 size_t cbBlockAllocationTableToWrite = pImage->cBlockAllocationTableEntries * sizeof(uint32_t);
1105 uint32_t *pBlockAllocationTableToWrite = (uint32_t *)RTMemAllocZ(cbBlockAllocationTableToWrite);
1106
1107 if (!pBlockAllocationTableToWrite)
1108 return VERR_NO_MEMORY;
1109
1110 /*
1111 * The BAT entries have to be stored in big endian format.
1112 */
1113 unsigned i = 0;
1114 for (i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1115 {
1116 pBlockAllocationTableToWrite[i] = RT_H2BE_U32(pImage->pBlockAllocationTable[i]);
1117 }
1118
1119 /*
1120 * Write the block allocation table after the copy of the disk footer and the dynamic disk header.
1121 */
1122 RTFileWriteAt(pImage->File, pImage->uBlockAllocationTableOffset, pBlockAllocationTableToWrite, cbBlockAllocationTableToWrite, NULL);
1123 RTFileWriteAt(pImage->File, pImage->uCurrentEndOfFile, &pImage->vhdFooterCopy, sizeof(VHDFooter), NULL);
1124 if (pImage->fDynHdrNeedsUpdate)
1125 vhdDynamicHeaderUpdate(pImage);
1126 RTMemFree(pBlockAllocationTableToWrite);
1127 }
1128
1129 int rc = RTFileFlush(pImage->File);
1130
1131 return rc;
1132}
1133
1134static uint64_t vhdGetSize(void *pBackendData)
1135{
1136 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1137
1138 Assert(pImage);
1139
1140 if (pImage)
1141 {
1142 Log(("%s: cbSize=%llu\n", __FUNCTION__, pImage->cbSize));
1143 return pImage->cbSize;
1144 }
1145 else
1146 return 0;
1147}
1148
1149static uint64_t vhdGetFileSize(void *pBackendData)
1150{
1151 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1152
1153 Assert(pImage);
1154
1155 if (pImage)
1156 {
1157 uint64_t cb;
1158 int rc = RTFileGetSize(pImage->File, &cb);
1159 if (RT_SUCCESS(rc))
1160 return cb;
1161 else
1162 return 0;
1163 }
1164 else
1165 return 0;
1166}
1167
1168static int vhdGetUuid(void *pBackendData, PRTUUID pUuid)
1169{
1170 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1171 int rc;
1172
1173 Assert(pImage);
1174
1175 if (pImage)
1176 {
1177 *pUuid = pImage->ImageUuid;
1178 rc = VINF_SUCCESS;
1179 }
1180 else
1181 rc = VERR_VDI_NOT_OPENED;
1182 LogFlow(("%s: returned %Rrc (%RTuuid)\n", __FUNCTION__, rc, pUuid));
1183 return rc;
1184}
1185
1186static int vhdSetUuid(void *pBackendData, PCRTUUID pUuid)
1187{
1188 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1189 int rc;
1190
1191 LogFlowFunc((" %RTuuid\n", pUuid));
1192 Assert(pImage);
1193
1194 if (pImage)
1195 {
1196 pImage->ImageUuid = *pUuid;
1197 /**@todo: implement */
1198 rc = VINF_SUCCESS;
1199 }
1200 else
1201 rc = VERR_VDI_NOT_OPENED;
1202 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1203 return rc;
1204}
1205
1206static int vhdGetComment(void *pBackendData, char *pszComment, size_t cbComment)
1207{
1208 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1209 int rc;
1210
1211 Assert(pImage);
1212
1213 if (pImage)
1214 {
1215 rc = VERR_VDI_VALUE_NOT_FOUND;
1216 }
1217 else
1218 rc = VERR_VDI_NOT_OPENED;
1219
1220 LogFlow(("%s: returned %Rrc comment='%s'\n", __FUNCTION__, rc, pszComment));
1221 return rc;
1222}
1223
1224static int vhdSetComment(void *pBackendData, const char *pszComment)
1225{
1226 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1227 int rc;
1228
1229 LogFlowFunc((" comment '%s'\n", pszComment));
1230 Assert(pImage);
1231
1232 if (pImage)
1233 {
1234 /**@todo: implement */
1235 rc = VINF_SUCCESS;
1236 }
1237 else
1238 rc = VERR_VDI_NOT_OPENED;
1239
1240 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1241 return rc;
1242}
1243
1244static int vhdGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1245{
1246 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1247 int rc;
1248
1249 Assert(pImage);
1250
1251 if (pImage)
1252 {
1253 rc = VERR_VDI_VALUE_NOT_FOUND;
1254 }
1255 else
1256 rc = VERR_VDI_NOT_OPENED;
1257 LogFlow(("%s: returned %Rrc (%RTuuid)\n", __FUNCTION__, rc, pUuid));
1258 return rc;
1259}
1260
1261static int vhdSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1262{
1263 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1264 int rc;
1265
1266 LogFlowFunc((" %RTuuid\n", pUuid));
1267 Assert(pImage);
1268
1269 if (pImage)
1270 {
1271 rc = VINF_SUCCESS;
1272 }
1273 else
1274 rc = VERR_VDI_NOT_OPENED;
1275 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1276 return rc;
1277}
1278
1279static int vhdGetParentUuid(void *pBackendData, PRTUUID pUuid)
1280{
1281 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1282 int rc;
1283
1284 Assert(pImage);
1285
1286 if (pImage)
1287 {
1288 *pUuid = pImage->ParentUuid;
1289 rc = VINF_SUCCESS;
1290 }
1291 else
1292 rc = VERR_VDI_NOT_OPENED;
1293 LogFlow(("%s: returned %Rrc (%RTuuid)\n", __FUNCTION__, rc, pUuid));
1294 return rc;
1295}
1296
1297static int vhdSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1298{
1299 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1300 int rc = VINF_SUCCESS;
1301
1302 LogFlowFunc((" %RTuuid\n", pUuid));
1303 Assert(pImage);
1304
1305 if (pImage && pImage->File != NIL_RTFILE)
1306 {
1307 if (pImage->enmImageType != VD_IMAGE_TYPE_FIXED)
1308 {
1309 pImage->ParentUuid = *pUuid;
1310 pImage->fDynHdrNeedsUpdate = true;
1311 }
1312 else
1313 rc = VERR_NOT_SUPPORTED;
1314 }
1315 else
1316 rc = VERR_VDI_NOT_OPENED;
1317 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1318 return rc;
1319}
1320
1321static int vhdGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1322{
1323 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1324 int rc;
1325
1326 Assert(pImage);
1327
1328 if (pImage)
1329 {
1330 rc = VERR_VDI_VALUE_NOT_FOUND;
1331 }
1332 else
1333 rc = VERR_VDI_NOT_OPENED;
1334 LogFlow(("%s: returned %Rrc (%RTuuid)\n", __FUNCTION__, rc, pUuid));
1335 return rc;
1336}
1337
1338static int vhdSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1339{
1340 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1341 int rc;
1342
1343 LogFlow(("%s: %RTuuid\n", pUuid));
1344 Assert(pImage);
1345
1346 if (pImage)
1347 {
1348 rc = VINF_SUCCESS;
1349 }
1350 else
1351 rc = VERR_VDI_NOT_OPENED;
1352 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1353 return rc;
1354}
1355
1356/**
1357 * Internal: Derive drive geometry from its size.
1358 */
1359static void vhdSetDiskGeometry(PVHDIMAGE pImage, uint64_t cbSize)
1360{
1361 uint64_t u64TotalSectors = cbSize / 512;
1362 uint32_t u32CylinderTimesHeads, u32Heads, u32SectorsPerTrack;
1363
1364 if (u64TotalSectors > 65535 * 16 * 255)
1365 {
1366 /* ATA disks limited to 127 GB. */
1367 u64TotalSectors = 65535 * 16 * 255;
1368 }
1369
1370 if (u64TotalSectors >= 65535 * 16 * 63)
1371 {
1372 u32SectorsPerTrack = 255;
1373 u32Heads = 16;
1374 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1375 }
1376 else
1377 {
1378 u32SectorsPerTrack = 17;
1379 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1380
1381 u32Heads = (u32CylinderTimesHeads + 1023) / 1024;
1382
1383 if (u32Heads < 4)
1384 {
1385 u32Heads = 4;
1386 }
1387 if (u32CylinderTimesHeads >= (u32Heads * 1024) || u32Heads > 16)
1388 {
1389 u32SectorsPerTrack = 31;
1390 u32Heads = 16;
1391 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1392 }
1393 if (u32CylinderTimesHeads >= (u32Heads * 1024))
1394 {
1395 u32SectorsPerTrack = 63;
1396 u32Heads = 16;
1397 u32CylinderTimesHeads = u64TotalSectors / u32SectorsPerTrack;
1398 }
1399 }
1400 pImage->PCHSGeometry.cCylinders = u32CylinderTimesHeads / u32Heads;
1401 pImage->PCHSGeometry.cHeads = u32Heads;
1402 pImage->PCHSGeometry.cSectors = u32SectorsPerTrack;
1403 pImage->LCHSGeometry.cCylinders = 0;
1404 pImage->LCHSGeometry.cHeads = 0;
1405 pImage->LCHSGeometry.cSectors = 0;
1406}
1407
1408
1409/**
1410 * Internal: signal an error to the frontend.
1411 */
1412DECLINLINE(int) vhdError(PVHDIMAGE pImage, int rc, RT_SRC_POS_DECL,
1413 const char *pszFormat, ...)
1414{
1415 va_list va;
1416 va_start(va, pszFormat);
1417 if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
1418 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
1419 pszFormat, va);
1420 va_end(va);
1421 return rc;
1422}
1423
1424static uint32_t vhdAllocateParentLocators(PVHDIMAGE pImage, VHDDynamicDiskHeader *pDDH, uint64_t u64Offset)
1425{
1426 PVHDPLE pLocator = pDDH->ParentLocatorEntry;
1427 /* Relative Windows path. */
1428 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2R);
1429 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH / VHD_SECTOR_SIZE);
1430 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1431 u64Offset += VHD_RELATIVE_MAX_PATH;
1432 pLocator++;
1433 /* Absolute Windows path. */
1434 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_WI2K);
1435 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH / VHD_SECTOR_SIZE);
1436 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1437 u64Offset += VHD_ABSOLUTE_MAX_PATH;
1438 pLocator++;
1439 /* Unicode relative Windows path. */
1440 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2RU);
1441 pLocator->u32DataSpace = RT_H2BE_U32(VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1442 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1443 u64Offset += VHD_RELATIVE_MAX_PATH * sizeof(RTUTF16);
1444 pLocator++;
1445 /* Unicode absolute Windows path. */
1446 pLocator->u32Code = RT_H2BE_U32(VHD_PLATFORM_CODE_W2KU);
1447 pLocator->u32DataSpace = RT_H2BE_U32(VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16) / VHD_SECTOR_SIZE);
1448 pLocator->u64DataOffset = RT_H2BE_U64(u64Offset);
1449 return u64Offset + VHD_ABSOLUTE_MAX_PATH * sizeof(RTUTF16);
1450}
1451
1452/**
1453 * Internal: Additional code for dynamic VHD image creation.
1454 */
1455static int vhdCreateDynamicImage(PVHDIMAGE pImage, uint64_t cbSize)
1456{
1457 int rc;
1458 VHDDynamicDiskHeader DynamicDiskHeader;
1459 uint32_t u32BlockAllocationTableSectors;
1460
1461 memset(&DynamicDiskHeader, 0, sizeof(DynamicDiskHeader));
1462
1463 pImage->u64DataOffset = sizeof(VHDFooter);
1464 pImage->cbDataBlock = VHD_BLOCK_SIZE; /* 2 MB */
1465 pImage->cSectorsPerDataBlock = pImage->cbDataBlock / VHD_SECTOR_SIZE;
1466 pImage->cbDataBlockBitmap = pImage->cSectorsPerDataBlock / 8;
1467 pImage->cDataBlockBitmapSectors = pImage->cbDataBlockBitmap / VHD_SECTOR_SIZE;
1468 pImage->pu8Bitmap = (uint8_t *)RTMemAllocZ(pImage->cbDataBlockBitmap);
1469 if (!pImage->pu8Bitmap)
1470 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for bitmap storage"));
1471
1472 /* Initialize BAT. */
1473 pImage->uBlockAllocationTableOffset = (uint64_t)sizeof(VHDFooter) + sizeof(VHDDynamicDiskHeader);
1474 pImage->cBlockAllocationTableEntries = (uint32_t)(cbSize / pImage->cbDataBlock);
1475 u32BlockAllocationTableSectors = (pImage->cBlockAllocationTableEntries * sizeof(uint32_t) + VHD_SECTOR_SIZE - 1) / VHD_SECTOR_SIZE;
1476 pImage->pBlockAllocationTable = (uint32_t *)RTMemAllocZ(pImage->cBlockAllocationTableEntries * sizeof(uint32_t));
1477 if (!pImage->pBlockAllocationTable)
1478 return vhdError(pImage, VERR_NO_MEMORY, RT_SRC_POS, N_("VHD: cannot allocate memory for BAT"));
1479
1480 for (unsigned i = 0; i < pImage->cBlockAllocationTableEntries; i++)
1481 {
1482 pImage->pBlockAllocationTable[i] = 0xFFFFFFFF; /* It is actually big endian. */
1483 }
1484 /* Round up to the sector size. */
1485 pImage->uCurrentEndOfFile = vhdAllocateParentLocators(pImage, &DynamicDiskHeader,
1486 pImage->uBlockAllocationTableOffset + u32BlockAllocationTableSectors * VHD_SECTOR_SIZE);
1487
1488 rc = RTFileSetSize(pImage->File, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1489 if (RT_FAILURE(rc))
1490 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1491
1492 /* Initialize and write the dynamic disk header. */
1493 memcpy(DynamicDiskHeader.Cookie, VHD_DYNAMIC_DISK_HEADER_COOKIE, sizeof(DynamicDiskHeader.Cookie));
1494 DynamicDiskHeader.DataOffset = UINT64_C(0xFFFFFFFFFFFFFFFF); /* Initially the disk has no data. */
1495 DynamicDiskHeader.TableOffset = RT_H2BE_U64(pImage->uBlockAllocationTableOffset);
1496 DynamicDiskHeader.HeaderVersion = RT_H2BE_U32(VHD_DYNAMIC_DISK_HEADER_VERSION);
1497 DynamicDiskHeader.BlockSize = RT_H2BE_U32(pImage->cbDataBlock);
1498 DynamicDiskHeader.MaxTableEntries = RT_H2BE_U32(pImage->cBlockAllocationTableEntries);
1499 /* Compute and update checksum. */
1500 DynamicDiskHeader.Checksum = 0;
1501 DynamicDiskHeader.Checksum = RT_H2BE_U32(vhdChecksum(&DynamicDiskHeader, sizeof(DynamicDiskHeader)));
1502
1503 rc = RTFileWriteAt(pImage->File, sizeof(VHDFooter), &DynamicDiskHeader, sizeof(DynamicDiskHeader), NULL);
1504 if (RT_FAILURE(rc))
1505 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write dynamic disk header to image '%s'"), pImage->pszFilename);
1506
1507 /* Write BAT. */
1508 rc = RTFileWriteAt(pImage->File, pImage->uBlockAllocationTableOffset, pImage->pBlockAllocationTable,
1509 pImage->cBlockAllocationTableEntries * sizeof(uint32_t), NULL);
1510 if (RT_FAILURE(rc))
1511 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write BAT to image '%s'"), pImage->pszFilename);
1512
1513 return rc;
1514}
1515
1516/**
1517 * Internal: The actual code for VHD image creation, both fixed and dynamic.
1518 */
1519static int vhdCreateImage(PVHDIMAGE pImage, VDIMAGETYPE enmType,
1520 uint64_t cbSize, unsigned uImageFlags,
1521 const char *pszComment,
1522 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1523 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1524 unsigned uOpenFlags,
1525 PFNVMPROGRESS pfnProgress, void *pvUser,
1526 unsigned uPercentStart, unsigned uPercentSpan)
1527{
1528 int rc;
1529 RTFILE File;
1530 VHDFooter Footer;
1531 RTTIMESPEC now;
1532
1533 pImage->uOpenFlags = uOpenFlags;
1534
1535 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
1536 if (pImage->pInterfaceError)
1537 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
1538
1539 /* Create image file. */
1540 rc = RTFileOpen(&File, pImage->pszFilename,
1541 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
1542 if (RT_FAILURE(rc))
1543 return vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot create image '%s'"), pImage->pszFilename);
1544 pImage->File = File;
1545
1546 pImage->enmImageType = enmType;
1547 pImage->cbSize = cbSize;
1548 pImage->ImageUuid = *pUuid;
1549 RTUuidClear(&pImage->ParentUuid);
1550 vhdSetDiskGeometry(pImage, cbSize);
1551
1552 /* Initialize the footer. */
1553 memset(&Footer, 0, sizeof(Footer));
1554 memcpy(Footer.Cookie, VHD_FOOTER_COOKIE, sizeof(Footer.Cookie));
1555 Footer.Features = RT_H2BE_U32(0x2);
1556 Footer.Version = RT_H2BE_U32(VHD_FOOTER_FILE_FORMAT_VERSION);
1557 Footer.TimeStamp = RT_H2BE_U32(vhdRtTime2VhdTime(RTTimeNow(&now)));
1558 memcpy(Footer.CreatorApp, "vbox", sizeof(Footer.CreatorApp));
1559 Footer.CreatorVer = RT_H2BE_U32(VBOX_VERSION);
1560#ifdef RT_OS_DARWIN
1561 Footer.CreatorOS = RT_H2BE_U32(0x4D616320); /* "Mac " */
1562#else /* Virtual PC supports only two platforms atm, so everything else will be Wi2k. */
1563 Footer.CreatorOS = RT_H2BE_U32(0x5769326B); /* "Wi2k" */
1564#endif
1565 Footer.OrigSize = RT_H2BE_U64(cbSize);
1566 Footer.CurSize = Footer.OrigSize;
1567 Footer.DiskGeometryCylinder = RT_H2BE_U16(pImage->PCHSGeometry.cCylinders);
1568 Footer.DiskGeometryHeads = pImage->PCHSGeometry.cHeads;
1569 Footer.DiskGeometrySectors = pImage->PCHSGeometry.cSectors;
1570 memcpy(Footer.UniqueID, pImage->ImageUuid.au8, sizeof(Footer.UniqueID));
1571 Footer.SavedState = 0;
1572
1573 switch (enmType)
1574 {
1575 case VD_IMAGE_TYPE_FIXED:
1576 Footer.DiskType = RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_FIXED);
1577 /*
1578 * Initialize fixed image.
1579 * "The size of the entire file is the size of the hard disk in
1580 * the guest operating system plus the size of the footer."
1581 */
1582 pImage->u64DataOffset = VHD_FOOTER_DATA_OFFSET_FIXED;
1583 pImage->uCurrentEndOfFile = cbSize;
1584 rc = RTFileSetSize(File, pImage->uCurrentEndOfFile + sizeof(VHDFooter));
1585 if (RT_FAILURE(rc))
1586 {
1587 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot set the file size for '%s'"), pImage->pszFilename);
1588 goto out;
1589 }
1590 break;
1591 case VD_IMAGE_TYPE_NORMAL:
1592 case VD_IMAGE_TYPE_DIFF:
1593 /*
1594 * Initialize dynamic image.
1595 *
1596 * The overall structure of dynamic disk is:
1597 *
1598 * [Copy of hard disk footer (512 bytes)]
1599 * [Dynamic disk header (1024 bytes)]
1600 * [BAT (Block Allocation Table)]
1601 * [Parent Locators]
1602 * [Data block 1]
1603 * [Data block 2]
1604 * ...
1605 * [Data block N]
1606 * [Hard disk footer (512 bytes)]
1607 */
1608 Footer.DiskType = enmType == VD_IMAGE_TYPE_DIFF ?
1609 RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DIFFERENCING) :
1610 RT_H2BE_U32(VHD_FOOTER_DISK_TYPE_DYNAMIC);
1611 /* We are half way thourgh with creation of image, let the caller know. */
1612 if (pfnProgress)
1613 pfnProgress(NULL /* WARNING! pVM=NULL */, (uPercentStart + uPercentSpan) / 2, pvUser);
1614
1615 rc = vhdCreateDynamicImage(pImage, cbSize);
1616 if (RT_FAILURE(rc))
1617 goto out;
1618
1619 break;
1620 default:
1621 /* Unknown/invalid image type. */
1622 rc = VERR_NOT_IMPLEMENTED;
1623 break;
1624 }
1625
1626 Footer.DataOffset = RT_H2BE_U64(pImage->u64DataOffset);
1627 pImage->vhdFooterCopy = Footer;
1628
1629 /* Compute and update the footer checksum. */
1630 Footer.Checksum = 0;
1631 Footer.Checksum = RT_H2BE_U32(vhdChecksum(&Footer, sizeof(Footer)));
1632
1633 /* Store the footer */
1634 rc = RTFileWriteAt(File, pImage->uCurrentEndOfFile, &Footer, sizeof(Footer), NULL);
1635 if (RT_FAILURE(rc))
1636 {
1637 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write footer to image '%s'"), pImage->pszFilename);
1638 goto out;
1639 }
1640
1641 /* Dynamic images contain a copy of the footer at the very beginning of the file. */
1642 if (enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_DIFF)
1643 {
1644 /* Write the copy of the footer. */
1645 rc = RTFileWriteAt(File, 0, &Footer, sizeof(Footer), NULL);
1646 if (RT_FAILURE(rc))
1647 {
1648 vhdError(pImage, rc, RT_SRC_POS, N_("VHD: cannot write a copy of footer to image '%s'"), pImage->pszFilename);
1649 goto out;
1650 }
1651 }
1652
1653 if (pfnProgress)
1654 pfnProgress(NULL /* WARNING! pVM=NULL */, uPercentStart + uPercentSpan, pvUser);
1655
1656out:
1657 return rc;
1658}
1659
1660static int vhdCreate(const char *pszFilename, VDIMAGETYPE enmType,
1661 uint64_t cbSize, unsigned uImageFlags,
1662 const char *pszComment,
1663 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1664 PCPDMMEDIAGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
1665 unsigned uOpenFlags, unsigned uPercentStart,
1666 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
1667 PVDINTERFACE pVDIfsImage, PVDINTERFACE pVDIfsOperation,
1668 void **ppvBackendData)
1669{
1670 int rc = VINF_SUCCESS;
1671 PVHDIMAGE pImage;
1672
1673 PFNVMPROGRESS pfnProgress = NULL;
1674 void *pvUser = NULL;
1675 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
1676 VDINTERFACETYPE_PROGRESS);
1677 PVDINTERFACEPROGRESS pCbProgress = NULL;
1678 if (pIfProgress)
1679 {
1680 pCbProgress = VDGetInterfaceProgress(pIfProgress);
1681 if (pCbProgress)
1682 pfnProgress = pCbProgress->pfnProgress;
1683 pvUser = pIfProgress->pvUser;
1684 }
1685
1686 /* Check open flags. All valid flags are supported. */
1687 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
1688 {
1689 rc = VERR_INVALID_PARAMETER;
1690 return rc;
1691 }
1692
1693 /* @todo Check the values of other params */
1694
1695 pImage = (PVHDIMAGE)RTMemAllocZ(sizeof(VHDIMAGE));
1696 if (!pImage)
1697 {
1698 rc = VERR_NO_MEMORY;
1699 return rc;
1700 }
1701 pImage->pszFilename = pszFilename;
1702 pImage->File = NIL_RTFILE;
1703 pImage->pVDIfsDisk = NULL;
1704
1705 rc = vhdCreateImage(pImage, enmType, cbSize, uImageFlags, pszComment,
1706 pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags,
1707 pfnProgress, pvUser, uPercentStart, uPercentSpan);
1708
1709 if (RT_SUCCESS(rc))
1710 {
1711 /* So far the image is opened in read/write mode. Make sure the
1712 * image is opened in read-only mode if the caller requested that. */
1713 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1714 {
1715 vhdClose(pImage, false);
1716 rc = vhdOpenImage(pImage, uOpenFlags);
1717 if (RT_FAILURE(rc))
1718 goto out;
1719 }
1720 *ppvBackendData = pImage;
1721 }
1722out:
1723 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1724 return rc;
1725}
1726
1727static void vhdDump(void *pBackendData)
1728{
1729 PVHDIMAGE pImage = (PVHDIMAGE)pBackendData;
1730
1731 Assert(pImage);
1732 if (pImage)
1733 {
1734 /** @todo this is just a stub */
1735 }
1736}
1737
1738
1739static int vhdGetTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
1740{
1741 int rc = VINF_SUCCESS;
1742 PVHDIMAGE pImage = (PVHDIMAGE)pvBackendData;
1743
1744 Assert(pImage);
1745 if (pImage)
1746 {
1747 RTFSOBJINFO info;
1748
1749 rc = RTFileQueryInfo(pImage->File, &info, RTFSOBJATTRADD_NOTHING);
1750 *pTimeStamp = info.ModificationTime;
1751 }
1752 else
1753 rc = VERR_VDI_NOT_OPENED;
1754 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1755 return rc;
1756}
1757
1758static int vhdGetParentTimeStamp(void *pvBackendData, PRTTIMESPEC pTimeStamp)
1759{
1760 int rc = VINF_SUCCESS;
1761 PVHDIMAGE pImage = (PVHDIMAGE)pvBackendData;
1762
1763 Assert(pImage);
1764 if (pImage)
1765 vhdTime2RtTime(pTimeStamp, pImage->u32ParentTimeStamp);
1766 else
1767 rc = VERR_VDI_NOT_OPENED;
1768 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1769 return rc;
1770}
1771
1772static int vhdSetParentTimeStamp(void *pvBackendData, PCRTTIMESPEC pTimeStamp)
1773{
1774 int rc = VINF_SUCCESS;
1775 PVHDIMAGE pImage = (PVHDIMAGE)pvBackendData;
1776
1777 Assert(pImage);
1778 if (pImage)
1779 {
1780 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1781 rc = VERR_VDI_IMAGE_READ_ONLY;
1782 else
1783 {
1784 pImage->u32ParentTimeStamp = vhdRtTime2VhdTime(pTimeStamp);
1785 pImage->fDynHdrNeedsUpdate = true;
1786 }
1787 }
1788 else
1789 rc = VERR_VDI_NOT_OPENED;
1790 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1791 return rc;
1792}
1793
1794static int vhdGetParentFilename(void *pvBackendData, char **ppszParentFilename)
1795{
1796 int rc = VINF_SUCCESS;
1797 PVHDIMAGE pImage = (PVHDIMAGE)pvBackendData;
1798
1799 Assert(pImage);
1800 if (pImage)
1801 *ppszParentFilename = RTStrDup(pImage->pszParentFilename);
1802 else
1803 rc = VERR_VDI_NOT_OPENED;
1804 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1805 return rc;
1806}
1807
1808static int vhdSetParentFilename(void *pvBackendData, const char *pszParentFilename)
1809{
1810 int rc = VINF_SUCCESS;
1811 PVHDIMAGE pImage = (PVHDIMAGE)pvBackendData;
1812
1813 Assert(pImage);
1814 if (pImage)
1815 {
1816 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1817 rc = VERR_VDI_IMAGE_READ_ONLY;
1818 else
1819 {
1820 if (pImage->pszParentFilename)
1821 RTStrFree(pImage->pszParentFilename);
1822 pImage->pszParentFilename = RTStrDup(pszParentFilename);
1823 if (!pImage->pszParentFilename)
1824 rc = VERR_NO_MEMORY;
1825 else
1826 pImage->fDynHdrNeedsUpdate = true;
1827 }
1828 }
1829 else
1830 rc = VERR_VDI_NOT_OPENED;
1831 LogFlow(("%s: returned %Rrc\n", __FUNCTION__, rc));
1832 return rc;
1833}
1834
1835static bool vhdIsAsyncIOSupported(void *pvBackendData)
1836{
1837 return false;
1838}
1839
1840static int vhdAsyncRead(void *pvBackendData, uint64_t uOffset, size_t cbRead,
1841 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
1842{
1843 int rc = VERR_NOT_IMPLEMENTED;
1844 LogFlowFunc(("returns %Rrc\n", rc));
1845 return rc;
1846}
1847
1848static int vhdAsyncWrite(void *pvBackendData, uint64_t uOffset, size_t cbWrite,
1849 PPDMDATASEG paSeg, unsigned cSeg, void *pvUser)
1850{
1851 int rc = VERR_NOT_IMPLEMENTED;
1852 LogFlowFunc(("returns %Rrc\n", rc));
1853 return rc;
1854}
1855
1856
1857VBOXHDDBACKEND g_VhdBackend =
1858{
1859 /* pszBackendName */
1860 "VHD",
1861 /* cbSize */
1862 sizeof(VBOXHDDBACKEND),
1863 /* uBackendCaps */
1864 VD_CAP_UUID | VD_CAP_DIFF | VD_CAP_FILE,
1865 /* papszFileExtensions */
1866 s_apszVhdFileExtensions,
1867 /* paConfigInfo */
1868 NULL,
1869 /* pfnCheckIfValid */
1870 vhdCheckIfValid,
1871 /* pfnOpen */
1872 vhdOpen,
1873 /* pfnCreate */
1874 vhdCreate,
1875 /* pfnRename */
1876 vhdRename,
1877 /* pfnClose */
1878 vhdClose,
1879 /* pfnRead */
1880 vhdRead,
1881 /* pfnWrite */
1882 vhdWrite,
1883 /* pfnFlush */
1884 vhdFlush,
1885 /* pfnGetVersion */
1886 vhdGetVersion,
1887 /* pfnGetImageType */
1888 vhdGetImageType,
1889 /* pfnGetSize */
1890 vhdGetSize,
1891 /* pfnGetFileSize */
1892 vhdGetFileSize,
1893 /* pfnGetPCHSGeometry */
1894 vhdGetPCHSGeometry,
1895 /* pfnSetPCHSGeometry */
1896 vhdSetPCHSGeometry,
1897 /* pfnGetLCHSGeometry */
1898 vhdGetLCHSGeometry,
1899 /* pfnSetLCHSGeometry */
1900 vhdSetLCHSGeometry,
1901 /* pfnGetImageFlags */
1902 vhdGetImageFlags,
1903 /* pfnGetOpenFlags */
1904 vhdGetOpenFlags,
1905 /* pfnSetOpenFlags */
1906 vhdSetOpenFlags,
1907 /* pfnGetComment */
1908 vhdGetComment,
1909 /* pfnSetComment */
1910 vhdSetComment,
1911 /* pfnGetUuid */
1912 vhdGetUuid,
1913 /* pfnSetUuid */
1914 vhdSetUuid,
1915 /* pfnGetModificationUuid */
1916 vhdGetModificationUuid,
1917 /* pfnSetModificationUuid */
1918 vhdSetModificationUuid,
1919 /* pfnGetParentUuid */
1920 vhdGetParentUuid,
1921 /* pfnSetParentUuid */
1922 vhdSetParentUuid,
1923 /* pfnGetParentModificationUuid */
1924 vhdGetParentModificationUuid,
1925 /* pfnSetParentModificationUuid */
1926 vhdSetParentModificationUuid,
1927 /* pfnDump */
1928 vhdDump,
1929 /* pfnGetTimeStamp */
1930 vhdGetTimeStamp,
1931 /* pfnGetParentTimeStamp */
1932 vhdGetParentTimeStamp,
1933 /* pfnSetParentTimeStamp */
1934 vhdSetParentTimeStamp,
1935 /* pfnGetParentFilename */
1936 vhdGetParentFilename,
1937 /* pfnSetParentFilename */
1938 vhdSetParentFilename,
1939 /* pfnIsAsyncIOSupported */
1940 vhdIsAsyncIOSupported,
1941 /* pfnAsyncRead */
1942 vhdAsyncRead,
1943 /* pfnAsyncWrite */
1944 vhdAsyncWrite
1945};
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