VirtualBox

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

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

typo (accidental undo actually)

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