VirtualBox

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

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

Storage/VHD: cooperate with VBoxHDD to suppress redundant writes

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

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