VirtualBox

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

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

Storage/VBoxHDD: eliminate the obsolete "-new" part of the name.

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