VirtualBox

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

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

VHD: Fix access beyond the block bitmap which can lead to arbitrary crashes

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