VirtualBox

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

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

VHDHDDCore.cpp: On second thought, the code looks fine.

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