VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette