VirtualBox

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

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

VHD: fix non booting guests because of the wrong order we check for valid sectors in the block bitmap

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