VirtualBox

source: vbox/trunk/src/VBox/Storage/VHDX.cpp@ 55747

Last change on this file since 55747 was 54430, checked in by vboxsync, 10 years ago

Storage/VD: make use of the image type (hdd/dvd/floppy) for sanity checking when creating disk images

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 91.7 KB
Line 
1/* $Id: VHDX.cpp 54430 2015-02-24 10:43:16Z vboxsync $ */
2/** @file
3 * VHDX - VHDX Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2012-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD_VHDX
22#include <VBox/vd-plugin.h>
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/alloc.h>
29#include <iprt/path.h>
30#include <iprt/uuid.h>
31#include <iprt/crc.h>
32
33#include "VDBackends.h"
34
35/*******************************************************************************
36* On disk data structures *
37*******************************************************************************/
38
39/**
40 * VHDX file type identifier.
41 */
42#pragma pack(1)
43typedef struct VhdxFileIdentifier
44{
45 /** Signature. */
46 uint64_t u64Signature;
47 /** Creator ID - UTF-16 string (not neccessarily null terminated). */
48 uint16_t awszCreator[256];
49} VhdxFileIdentifier;
50#pragma pack()
51/** Pointer to an on disk VHDX file type identifier. */
52typedef VhdxFileIdentifier *PVhdxFileIdentifier;
53
54/** VHDX file type identifier signature ("vhdxfile"). */
55#define VHDX_FILE_IDENTIFIER_SIGNATURE UINT64_C(0x656c696678646876)
56/** Start offset of the VHDX file type identifier. */
57#define VHDX_FILE_IDENTIFIER_OFFSET UINT64_C(0)
58
59/**
60 * VHDX header.
61 */
62#pragma pack(1)
63typedef struct VhdxHeader
64{
65 /** Signature. */
66 uint32_t u32Signature;
67 /** Checksum. */
68 uint32_t u32Checksum;
69 /** Sequence number. */
70 uint64_t u64SequenceNumber;
71 /** File write UUID. */
72 RTUUID UuidFileWrite;
73 /** Data write UUID. */
74 RTUUID UuidDataWrite;
75 /** Log UUID. */
76 RTUUID UuidLog;
77 /** Version of the log format. */
78 uint16_t u16LogVersion;
79 /** VHDX format version.. */
80 uint16_t u16Version;
81 /** Length of the log region. */
82 uint32_t u32LogLength;
83 /** Start offset of the log offset in the file. */
84 uint64_t u64LogOffset;
85 /** Reserved bytes. */
86 uint8_t u8Reserved[4016];
87} VhdxHeader;
88#pragma pack()
89/** Pointer to an on disk VHDX header. */
90typedef VhdxHeader *PVhdxHeader;
91
92/** VHDX header signature ("head"). */
93#define VHDX_HEADER_SIGNATURE UINT32_C(0x64616568)
94/** Start offset of the first VHDX header. */
95#define VHDX_HEADER1_OFFSET _64K
96/** Start offset of the second VHDX header. */
97#define VHDX_HEADER2_OFFSET _128K
98/** Current Log format version. */
99#define VHDX_HEADER_LOG_VERSION UINT16_C(0)
100/** Current VHDX format version. */
101#define VHDX_HEADER_VHDX_VERSION UINT16_C(1)
102
103/**
104 * VHDX region table header
105 */
106#pragma pack(1)
107typedef struct VhdxRegionTblHdr
108{
109 /** Signature. */
110 uint32_t u32Signature;
111 /** Checksum. */
112 uint32_t u32Checksum;
113 /** Number of region table entries following this header. */
114 uint32_t u32EntryCount;
115 /** Reserved. */
116 uint32_t u32Reserved;
117} VhdxRegionTblHdr;
118#pragma pack()
119/** Pointer to an on disk VHDX region table header. */
120typedef VhdxRegionTblHdr *PVhdxRegionTblHdr;
121
122/** VHDX region table header signature. */
123#define VHDX_REGION_TBL_HDR_SIGNATURE UINT32_C(0x69676572)
124/** Maximum number of entries which can follow. */
125#define VHDX_REGION_TBL_HDR_ENTRY_COUNT_MAX UINT32_C(2047)
126/** Offset where the region table is stored (192 KB). */
127#define VHDX_REGION_TBL_HDR_OFFSET UINT64_C(196608)
128/** Maximum size of the region table. */
129#define VHDX_REGION_TBL_SIZE_MAX _64K
130
131/**
132 * VHDX region table entry.
133 */
134#pragma pack(1)
135typedef struct VhdxRegionTblEntry
136{
137 /** Object UUID. */
138 RTUUID UuidObject;
139 /** File offset of the region. */
140 uint64_t u64FileOffset;
141 /** Length of the region in bytes. */
142 uint32_t u32Length;
143 /** Flags for this object. */
144 uint32_t u32Flags;
145} VhdxRegionTblEntry;
146#pragma pack()
147/** Pointer to an on disk VHDX region table entry. */
148typedef struct VhdxRegionTblEntry *PVhdxRegionTblEntry;
149
150/** Flag whether this region is required. */
151#define VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED RT_BIT_32(0)
152/** UUID for the BAT region. */
153#define VHDX_REGION_TBL_ENTRY_UUID_BAT "2dc27766-f623-4200-9d64-115e9bfd4a08"
154/** UUID for the metadata region. */
155#define VHDX_REGION_TBL_ENTRY_UUID_METADATA "8b7ca206-4790-4b9a-b8fe-575f050f886e"
156
157/**
158 * VHDX Log entry header.
159 */
160#pragma pack(1)
161typedef struct VhdxLogEntryHdr
162{
163 /** Signature. */
164 uint32_t u32Signature;
165 /** Checksum. */
166 uint32_t u32Checksum;
167 /** Total length of the entry in bytes. */
168 uint32_t u32EntryLength;
169 /** Tail of the log entries. */
170 uint32_t u32Tail;
171 /** Sequence number. */
172 uint64_t u64SequenceNumber;
173 /** Number of descriptors in this log entry. */
174 uint32_t u32DescriptorCount;
175 /** Reserved. */
176 uint32_t u32Reserved;
177 /** Log UUID. */
178 RTUUID UuidLog;
179 /** VHDX file size in bytes while the log entry was written. */
180 uint64_t u64FlushedFileOffset;
181 /** File size in bytes all allocated file structures fit into when the
182 * log entry was written. */
183 uint64_t u64LastFileOffset;
184} VhdxLogEntryHdr;
185#pragma pack()
186/** Pointer to an on disk VHDX log entry header. */
187typedef struct VhdxLogEntryHdr *PVhdxLogEntryHdr;
188
189/** VHDX log entry signature ("loge"). */
190#define VHDX_LOG_ENTRY_HEADER_SIGNATURE UINT32_C(0x65676f6c)
191
192/**
193 * VHDX log zero descriptor.
194 */
195#pragma pack(1)
196typedef struct VhdxLogZeroDesc
197{
198 /** Signature of this descriptor. */
199 uint32_t u32ZeroSignature;
200 /** Reserved. */
201 uint32_t u32Reserved;
202 /** Length of the section to zero. */
203 uint64_t u64ZeroLength;
204 /** File offset to write zeros to. */
205 uint64_t u64FileOffset;
206 /** Sequence number (must macht the field in the log entry header). */
207 uint64_t u64SequenceNumber;
208} VhdxLogZeroDesc;
209#pragma pack()
210/** Pointer to an on disk VHDX log zero descriptor. */
211typedef struct VhdxLogZeroDesc *PVhdxLogZeroDesc;
212
213/** Signature of a VHDX log zero descriptor ("zero"). */
214#define VHDX_LOG_ZERO_DESC_SIGNATURE UINT32_C(0x6f72657a)
215
216/**
217 * VHDX log data descriptor.
218 */
219#pragma pack(1)
220typedef struct VhdxLogDataDesc
221{
222 /** Signature of this descriptor. */
223 uint32_t u32DataSignature;
224 /** Trailing 4 bytes removed from the update. */
225 uint32_t u32TrailingBytes;
226 /** Leading 8 bytes removed from the update. */
227 uint64_t u64LeadingBytes;
228 /** File offset to write zeros to. */
229 uint64_t u64FileOffset;
230 /** Sequence number (must macht the field in the log entry header). */
231 uint64_t u64SequenceNumber;
232} VhdxLogDataDesc;
233#pragma pack()
234/** Pointer to an on disk VHDX log data descriptor. */
235typedef struct VhdxLogDataDesc *PVhdxLogDataDesc;
236
237/** Signature of a VHDX log data descriptor ("desc"). */
238#define VHDX_LOG_DATA_DESC_SIGNATURE UINT32_C(0x63736564)
239
240/**
241 * VHDX log data sector.
242 */
243#pragma pack(1)
244typedef struct VhdxLogDataSector
245{
246 /** Signature of the data sector. */
247 uint32_t u32DataSignature;
248 /** 4 most significant bytes of the sequence number. */
249 uint32_t u32SequenceHigh;
250 /** Raw data associated with the update. */
251 uint8_t u8Data[4084];
252 /** 4 least significant bytes of the sequence number. */
253 uint32_t u32SequenceLow;
254} VhdxLogDataSector;
255#pragma pack()
256/** Pointer to an on disk VHDX log data sector. */
257typedef VhdxLogDataSector *PVhdxLogDataSector;
258
259/** Signature of a VHDX log data sector ("data"). */
260#define VHDX_LOG_DATA_SECTOR_SIGNATURE UINT32_C(0x61746164)
261
262/**
263 * VHDX BAT entry.
264 */
265#pragma pack(1)
266typedef struct VhdxBatEntry
267{
268 /** The BAT entry, contains state and offset. */
269 uint64_t u64BatEntry;
270} VhdxBatEntry;
271#pragma pack()
272typedef VhdxBatEntry *PVhdxBatEntry;
273
274/** Return the BAT state from a given entry. */
275#define VHDX_BAT_ENTRY_GET_STATE(bat) ((bat) & UINT64_C(0x7))
276/** Get the FileOffsetMB field from a given BAT entry. */
277#define VHDX_BAT_ENTRY_GET_FILE_OFFSET_MB(bat) (((bat) & UINT64_C(0xfffffffffff00000)) >> 20)
278/** Get a byte offset from the BAT entry. */
279#define VHDX_BAT_ENTRY_GET_FILE_OFFSET(bat) (VHDX_BAT_ENTRY_GET_FILE_OFFSET_MB(bat) * (uint64_t)_1M)
280
281/** Block not present and the data is undefined. */
282#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_NOT_PRESENT (0)
283/** Data in this block is undefined. */
284#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNDEFINED (1)
285/** Data in this block contains zeros. */
286#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_ZERO (2)
287/** Block was unmapped by the application or system and data is either zero or
288 * the data before the block was unmapped. */
289#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNMAPPED (3)
290/** Block data is in the file pointed to by the FileOffsetMB field. */
291#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_FULLY_PRESENT (6)
292/** Block is partially present, use sector bitmap to get present sectors. */
293#define VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT (7)
294
295/** The sector bitmap block is undefined and not allocated in the file. */
296#define VHDX_BAT_ENTRY_SB_BLOCK_NOT_PRESENT (0)
297/** The sector bitmap block is defined at the file location. */
298#define VHDX_BAT_ENTRY_SB_BLOCK_PRESENT (6)
299
300/**
301 * VHDX Metadata tabl header.
302 */
303#pragma pack(1)
304typedef struct VhdxMetadataTblHdr
305{
306 /** Signature. */
307 uint64_t u64Signature;
308 /** Reserved. */
309 uint16_t u16Reserved;
310 /** Number of entries in the table. */
311 uint16_t u16EntryCount;
312 /** Reserved */
313 uint32_t u32Reserved2[5];
314} VhdxMetadataTblHdr;
315#pragma pack()
316/** Pointer to an on disk metadata table header. */
317typedef VhdxMetadataTblHdr *PVhdxMetadataTblHdr;
318
319/** Signature of a VHDX metadata table header ("metadata"). */
320#define VHDX_METADATA_TBL_HDR_SIGNATURE UINT64_C(0x617461646174656d)
321/** Maximum number of entries the metadata table can have. */
322#define VHDX_METADATA_TBL_HDR_ENTRY_COUNT_MAX UINT16_C(2047)
323
324/**
325 * VHDX Metadata table entry.
326 */
327#pragma pack(1)
328typedef struct VhdxMetadataTblEntry
329{
330 /** Item UUID. */
331 RTUUID UuidItem;
332 /** Offset of the metadata item. */
333 uint32_t u32Offset;
334 /** Length of the metadata item. */
335 uint32_t u32Length;
336 /** Flags for the metadata item. */
337 uint32_t u32Flags;
338 /** Reserved. */
339 uint32_t u32Reserved;
340} VhdxMetadataTblEntry;
341#pragma pack()
342/** Pointer to an on disk metadata table entry. */
343typedef VhdxMetadataTblEntry *PVhdxMetadataTblEntry;
344
345/** FLag whether the metadata item is system or user metadata. */
346#define VHDX_METADATA_TBL_ENTRY_FLAGS_IS_USER RT_BIT_32(0)
347/** FLag whether the metadata item is file or virtual disk metadata. */
348#define VHDX_METADATA_TBL_ENTRY_FLAGS_IS_VDISK RT_BIT_32(1)
349/** FLag whether the backend must understand the metadata item to load the image. */
350#define VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED RT_BIT_32(2)
351
352/** File parameters item UUID. */
353#define VHDX_METADATA_TBL_ENTRY_ITEM_FILE_PARAMS "caa16737-fa36-4d43-b3b6-33f0aa44e76b"
354/** Virtual disk size item UUID. */
355#define VHDX_METADATA_TBL_ENTRY_ITEM_VDISK_SIZE "2fa54224-cd1b-4876-b211-5dbed83bf4b8"
356/** Page 83 UUID. */
357#define VHDX_METADATA_TBL_ENTRY_ITEM_PAGE83_DATA "beca12ab-b2e6-4523-93ef-c309e000c746"
358/** Logical sector size UUID. */
359#define VHDX_METADATA_TBL_ENTRY_ITEM_LOG_SECT_SIZE "8141bf1d-a96f-4709-ba47-f233a8faab5f"
360/** Physical sector size UUID. */
361#define VHDX_METADATA_TBL_ENTRY_ITEM_PHYS_SECT_SIZE "cda348c7-445d-4471-9cc9-e9885251c556"
362/** Parent locator UUID. */
363#define VHDX_METADATA_TBL_ENTRY_ITEM_PARENT_LOCATOR "a8d35f2d-b30b-454d-abf7-d3d84834ab0c"
364
365/**
366 * VHDX File parameters metadata item.
367 */
368#pragma pack(1)
369typedef struct VhdxFileParameters
370{
371 /** Block size. */
372 uint32_t u32BlockSize;
373 /** Flags. */
374 uint32_t u32Flags;
375} VhdxFileParameters;
376#pragma pack()
377/** Pointer to an on disk VHDX file parameters metadata item. */
378typedef struct VhdxFileParameters *PVhdxFileParameters;
379
380/** Flag whether to leave blocks allocated in the file or if it is possible to unmap them. */
381#define VHDX_FILE_PARAMETERS_FLAGS_LEAVE_BLOCKS_ALLOCATED RT_BIT_32(0)
382/** Flag whether this file has a parent VHDX file. */
383#define VHDX_FILE_PARAMETERS_FLAGS_HAS_PARENT RT_BIT_32(1)
384
385/**
386 * VHDX virtual disk size metadata item.
387 */
388#pragma pack(1)
389typedef struct VhdxVDiskSize
390{
391 /** Virtual disk size. */
392 uint64_t u64VDiskSize;
393} VhdxVDiskSize;
394#pragma pack()
395/** Pointer to an on disk VHDX virtual disk size metadata item. */
396typedef struct VhdxVDiskSize *PVhdxVDiskSize;
397
398/**
399 * VHDX page 83 data metadata item.
400 */
401#pragma pack(1)
402typedef struct VhdxPage83Data
403{
404 /** UUID for the SCSI device. */
405 RTUUID UuidPage83Data;
406} VhdxPage83Data;
407#pragma pack()
408/** Pointer to an on disk VHDX vpage 83 data metadata item. */
409typedef struct VhdxPage83Data *PVhdxPage83Data;
410
411/**
412 * VHDX virtual disk logical sector size.
413 */
414#pragma pack(1)
415typedef struct VhdxVDiskLogicalSectorSize
416{
417 /** Logical sector size. */
418 uint32_t u32LogicalSectorSize;
419} VhdxVDiskLogicalSectorSize;
420#pragma pack()
421/** Pointer to an on disk VHDX virtual disk logical sector size metadata item. */
422typedef struct VhdxVDiskLogicalSectorSize *PVhdxVDiskLogicalSectorSize;
423
424/**
425 * VHDX virtual disk physical sector size.
426 */
427#pragma pack(1)
428typedef struct VhdxVDiskPhysicalSectorSize
429{
430 /** Physical sector size. */
431 uint64_t u64PhysicalSectorSize;
432} VhdxVDiskPhysicalSectorSize;
433#pragma pack()
434/** Pointer to an on disk VHDX virtual disk physical sector size metadata item. */
435typedef struct VhdxVDiskPhysicalSectorSize *PVhdxVDiskPhysicalSectorSize;
436
437/**
438 * VHDX parent locator header.
439 */
440#pragma pack(1)
441typedef struct VhdxParentLocatorHeader
442{
443 /** Locator type UUID. */
444 RTUUID UuidLocatorType;
445 /** Reserved. */
446 uint16_t u16Reserved;
447 /** Number of key value pairs. */
448 uint16_t u16KeyValueCount;
449} VhdxParentLocatorHeader;
450#pragma pack()
451/** Pointer to an on disk VHDX parent locator header metadata item. */
452typedef struct VhdxParentLocatorHeader *PVhdxParentLocatorHeader;
453
454/** VHDX parent locator type. */
455#define VHDX_PARENT_LOCATOR_TYPE_VHDX "b04aefb7-d19e-4a81-b789-25b8e9445913"
456
457/**
458 * VHDX parent locator entry.
459 */
460#pragma pack(1)
461typedef struct VhdxParentLocatorEntry
462{
463 /** Offset of the key. */
464 uint32_t u32KeyOffset;
465 /** Offset of the value. */
466 uint32_t u32ValueOffset;
467 /** Length of the key. */
468 uint16_t u16KeyLength;
469 /** Length of the value. */
470 uint16_t u16ValueLength;
471} VhdxParentLocatorEntry;
472#pragma pack()
473/** Pointer to an on disk VHDX parent locator entry. */
474typedef struct VhdxParentLocatorEntry *PVhdxParentLocatorEntry;
475
476/*******************************************************************************
477* Constants And Macros, Structures and Typedefs *
478*******************************************************************************/
479
480typedef enum VHDXMETADATAITEM
481{
482 VHDXMETADATAITEM_UNKNOWN = 0,
483 VHDXMETADATAITEM_FILE_PARAMS,
484 VHDXMETADATAITEM_VDISK_SIZE,
485 VHDXMETADATAITEM_PAGE83_DATA,
486 VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE,
487 VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE,
488 VHDXMETADATAITEM_PARENT_LOCATOR,
489 VHDXMETADATAITEM_32BIT_HACK = 0x7fffffff
490} VHDXMETADATAITEM;
491
492/**
493 * Table to validate the metadata item UUIDs and the flags.
494 */
495typedef struct VHDXMETADATAITEMPROPS
496{
497 /** Item UUID. */
498 const char *pszItemUuid;
499 /** Flag whether this is a user or system metadata item. */
500 bool fIsUser;
501 /** Flag whether this is a virtual disk or file metadata item. */
502 bool fIsVDisk;
503 /** Flag whether this metadata item is required to load the file. */
504 bool fIsRequired;
505 /** Metadata item enum associated with this UUID. */
506 VHDXMETADATAITEM enmMetadataItem;
507} VHDXMETADATAITEMPROPS;
508
509/**
510 * VHDX image data structure.
511 */
512typedef struct VHDXIMAGE
513{
514 /** Image name. */
515 const char *pszFilename;
516 /** Storage handle. */
517 PVDIOSTORAGE pStorage;
518
519 /** Pointer to the per-disk VD interface list. */
520 PVDINTERFACE pVDIfsDisk;
521 /** Pointer to the per-image VD interface list. */
522 PVDINTERFACE pVDIfsImage;
523 /** Error interface. */
524 PVDINTERFACEERROR pIfError;
525 /** I/O interface. */
526 PVDINTERFACEIOINT pIfIo;
527
528 /** Open flags passed by VBoxHD layer. */
529 unsigned uOpenFlags;
530 /** Image flags defined during creation or determined during open. */
531 unsigned uImageFlags;
532 /** Version of the VHDX image format. */
533 unsigned uVersion;
534 /** Total size of the image. */
535 uint64_t cbSize;
536 /** Logical sector size of the image. */
537 uint32_t cbLogicalSector;
538 /** Block size of the image. */
539 size_t cbBlock;
540 /** Physical geometry of this image. */
541 VDGEOMETRY PCHSGeometry;
542 /** Logical geometry of this image. */
543 VDGEOMETRY LCHSGeometry;
544
545 /** The BAT. */
546 PVhdxBatEntry paBat;
547 /** Chunk ratio. */
548 uint32_t uChunkRatio;
549
550} VHDXIMAGE, *PVHDXIMAGE;
551
552/**
553 * Endianess conversion direction.
554 */
555typedef enum VHDXECONV
556{
557 /** Host to file endianess. */
558 VHDXECONV_H2F = 0,
559 /** File to host endianess. */
560 VHDXECONV_F2H
561} VHDXECONV;
562
563/** Macros for endianess conversion. */
564#define SET_ENDIAN_U16(u16) (enmConv == VHDXECONV_H2F ? RT_H2LE_U16(u16) : RT_LE2H_U16(u16))
565#define SET_ENDIAN_U32(u32) (enmConv == VHDXECONV_H2F ? RT_H2LE_U32(u32) : RT_LE2H_U32(u32))
566#define SET_ENDIAN_U64(u64) (enmConv == VHDXECONV_H2F ? RT_H2LE_U64(u64) : RT_LE2H_U64(u64))
567
568/*******************************************************************************
569* Static Variables *
570*******************************************************************************/
571
572/**
573 * NULL-terminated array of supported file extensions.
574 */
575static const VDFILEEXTENSION s_aVhdxFileExtensions[] =
576{
577 {"vhdx", VDTYPE_HDD},
578 {NULL, VDTYPE_INVALID}
579};
580
581/**
582 * Static table to verify the metadata item properties and the flags.
583 */
584static const VHDXMETADATAITEMPROPS s_aVhdxMetadataItemProps[] =
585{
586 /* pcszItemUuid fIsUser, fIsVDisk, fIsRequired, enmMetadataItem */
587 {VHDX_METADATA_TBL_ENTRY_ITEM_FILE_PARAMS, false, false, true, VHDXMETADATAITEM_FILE_PARAMS},
588 {VHDX_METADATA_TBL_ENTRY_ITEM_VDISK_SIZE, false, true, true, VHDXMETADATAITEM_VDISK_SIZE},
589 {VHDX_METADATA_TBL_ENTRY_ITEM_PAGE83_DATA, false, true, true, VHDXMETADATAITEM_PAGE83_DATA},
590 {VHDX_METADATA_TBL_ENTRY_ITEM_LOG_SECT_SIZE, false, true, true, VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE},
591 {VHDX_METADATA_TBL_ENTRY_ITEM_PHYS_SECT_SIZE, false, true, true, VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE},
592 {VHDX_METADATA_TBL_ENTRY_ITEM_PARENT_LOCATOR, false, false, true, VHDXMETADATAITEM_PARENT_LOCATOR}
593};
594
595/*******************************************************************************
596* Internal Functions *
597*******************************************************************************/
598
599/**
600 * Converts the file identifier between file and host endianness.
601 *
602 * @returns nothing.
603 * @param enmConv Direction of the conversion.
604 * @param pFileIdentifierConv Where to store the converted file identifier.
605 * @param pFileIdentifier The file identifier to convert.
606 *
607 * @note It is safe to use the same pointer for pFileIdentifierConv and pFileIdentifier.
608 */
609DECLINLINE(void) vhdxConvFileIdentifierEndianess(VHDXECONV enmConv, PVhdxFileIdentifier pFileIdentifierConv,
610 PVhdxFileIdentifier pFileIdentifier)
611{
612 pFileIdentifierConv->u64Signature = SET_ENDIAN_U64(pFileIdentifier->u64Signature);
613 for (unsigned i = 0; i < RT_ELEMENTS(pFileIdentifierConv->awszCreator); i++)
614 pFileIdentifierConv->awszCreator[i] = SET_ENDIAN_U16(pFileIdentifier->awszCreator[i]);
615}
616
617/**
618 * Converts a UUID between file and host endianness.
619 *
620 * @returns nothing.
621 * @param enmConv Direction of the conversion.
622 * @param pUuidConv Where to store the converted UUID.
623 * @param pUuid The UUID to convert.
624 *
625 * @note It is safe to use the same pointer for pUuidConv and pUuid.
626 */
627DECLINLINE(void) vhdxConvUuidEndianess(VHDXECONV enmConv, PRTUUID pUuidConv, PRTUUID pUuid)
628{
629#if 1
630 memcpy(pUuidConv, pUuid, sizeof(RTUUID));
631#else
632 pUuidConv->Gen.u32TimeLow = SET_ENDIAN_U32(pUuid->Gen.u32TimeLow);
633 pUuidConv->Gen.u16TimeMid = SET_ENDIAN_U16(pUuid->Gen.u16TimeMid);
634 pUuidConv->Gen.u16TimeHiAndVersion = SET_ENDIAN_U16(pUuid->Gen.u16TimeHiAndVersion);
635 pUuidConv->Gen.u8ClockSeqHiAndReserved = pUuid->Gen.u8ClockSeqHiAndReserved;
636 pUuidConv->Gen.u8ClockSeqLow = pUuid->Gen.u8ClockSeqLow;
637 for (unsigned i = 0; i < RT_ELEMENTS(pUuidConv->Gen.au8Node); i++)
638 pUuidConv->Gen.au8Node[i] = pUuid->Gen.au8Node[i];
639#endif
640}
641
642/**
643 * Converts a VHDX header between file and host endianness.
644 *
645 * @returns nothing.
646 * @param enmConv Direction of the conversion.
647 * @param pHdrConv Where to store the converted header.
648 * @param pHdr The VHDX header to convert.
649 *
650 * @note It is safe to use the same pointer for pHdrConv and pHdr.
651 */
652DECLINLINE(void) vhdxConvHeaderEndianess(VHDXECONV enmConv, PVhdxHeader pHdrConv, PVhdxHeader pHdr)
653{
654 pHdrConv->u32Signature = SET_ENDIAN_U32(pHdr->u32Signature);
655 pHdrConv->u32Checksum = SET_ENDIAN_U32(pHdr->u32Checksum);
656 pHdrConv->u64SequenceNumber = SET_ENDIAN_U64(pHdr->u64SequenceNumber);
657 vhdxConvUuidEndianess(enmConv, &pHdrConv->UuidFileWrite, &pHdrConv->UuidFileWrite);
658 vhdxConvUuidEndianess(enmConv, &pHdrConv->UuidDataWrite, &pHdrConv->UuidDataWrite);
659 vhdxConvUuidEndianess(enmConv, &pHdrConv->UuidLog, &pHdrConv->UuidLog);
660 pHdrConv->u16LogVersion = SET_ENDIAN_U16(pHdr->u16LogVersion);
661 pHdrConv->u16Version = SET_ENDIAN_U16(pHdr->u16Version);
662 pHdrConv->u32LogLength = SET_ENDIAN_U32(pHdr->u32LogLength);
663 pHdrConv->u64LogOffset = SET_ENDIAN_U64(pHdr->u64LogOffset);
664}
665
666/**
667 * Converts a VHDX region table header between file and host endianness.
668 *
669 * @returns nothing.
670 * @param enmConv Direction of the conversion.
671 * @param pRegTblHdrConv Where to store the converted header.
672 * @param pRegTblHdr The VHDX region table header to convert.
673 *
674 * @note It is safe to use the same pointer for pRegTblHdrConv and pRegTblHdr.
675 */
676DECLINLINE(void) vhdxConvRegionTblHdrEndianess(VHDXECONV enmConv, PVhdxRegionTblHdr pRegTblHdrConv,
677 PVhdxRegionTblHdr pRegTblHdr)
678{
679 pRegTblHdrConv->u32Signature = SET_ENDIAN_U32(pRegTblHdr->u32Signature);
680 pRegTblHdrConv->u32Checksum = SET_ENDIAN_U32(pRegTblHdr->u32Checksum);
681 pRegTblHdrConv->u32EntryCount = SET_ENDIAN_U32(pRegTblHdr->u32EntryCount);
682 pRegTblHdrConv->u32Reserved = SET_ENDIAN_U32(pRegTblHdr->u32Reserved);
683}
684
685/**
686 * Converts a VHDX region table entry between file and host endianness.
687 *
688 * @returns nothing.
689 * @param enmConv Direction of the conversion.
690 * @param pRegTblEntConv Where to store the converted region table entry.
691 * @param pRegTblEnt The VHDX region table entry to convert.
692 *
693 * @note It is safe to use the same pointer for pRegTblEntConv and pRegTblEnt.
694 */
695DECLINLINE(void) vhdxConvRegionTblEntryEndianess(VHDXECONV enmConv, PVhdxRegionTblEntry pRegTblEntConv,
696 PVhdxRegionTblEntry pRegTblEnt)
697{
698 vhdxConvUuidEndianess(enmConv, &pRegTblEntConv->UuidObject, &pRegTblEnt->UuidObject);
699 pRegTblEntConv->u64FileOffset = SET_ENDIAN_U64(pRegTblEnt->u64FileOffset);
700 pRegTblEntConv->u32Length = SET_ENDIAN_U32(pRegTblEnt->u32Length);
701 pRegTblEntConv->u32Flags = SET_ENDIAN_U32(pRegTblEnt->u32Flags);
702}
703
704/**
705 * Converts a VHDX log entry header between file and host endianness.
706 *
707 * @returns nothing.
708 * @param enmConv Direction of the conversion.
709 * @param pLogEntryHdrConv Where to store the converted log entry header.
710 * @param pLogEntryHdr The VHDX log entry header to convert.
711 *
712 * @note It is safe to use the same pointer for pLogEntryHdrConv and pLogEntryHdr.
713 */
714DECLINLINE(void) vhdxConvLogEntryHdrEndianess(VHDXECONV enmConv, PVhdxLogEntryHdr pLogEntryHdrConv,
715 PVhdxLogEntryHdr pLogEntryHdr)
716{
717 pLogEntryHdrConv->u32Signature = SET_ENDIAN_U32(pLogEntryHdr->u32Signature);
718 pLogEntryHdrConv->u32Checksum = SET_ENDIAN_U32(pLogEntryHdr->u32Checksum);
719 pLogEntryHdrConv->u32EntryLength = SET_ENDIAN_U32(pLogEntryHdr->u32EntryLength);
720 pLogEntryHdrConv->u32Tail = SET_ENDIAN_U32(pLogEntryHdr->u32Tail);
721 pLogEntryHdrConv->u64SequenceNumber = SET_ENDIAN_U64(pLogEntryHdr->u64SequenceNumber);
722 pLogEntryHdrConv->u32DescriptorCount = SET_ENDIAN_U32(pLogEntryHdr->u32DescriptorCount);
723 pLogEntryHdrConv->u32Reserved = SET_ENDIAN_U32(pLogEntryHdr->u32Reserved);
724 vhdxConvUuidEndianess(enmConv, &pLogEntryHdrConv->UuidLog, &pLogEntryHdr->UuidLog);
725 pLogEntryHdrConv->u64FlushedFileOffset = SET_ENDIAN_U64(pLogEntryHdr->u64FlushedFileOffset);
726}
727
728/**
729 * Converts a VHDX log zero descriptor between file and host endianness.
730 *
731 * @returns nothing.
732 * @param enmConv Direction of the conversion.
733 * @param pLogZeroDescConv Where to store the converted log zero descriptor.
734 * @param pLogZeroDesc The VHDX log zero descriptor to convert.
735 *
736 * @note It is safe to use the same pointer for pLogZeroDescConv and pLogZeroDesc.
737 */
738DECLINLINE(void) vhdxConvLogZeroDescEndianess(VHDXECONV enmConv, PVhdxLogZeroDesc pLogZeroDescConv,
739 PVhdxLogZeroDesc pLogZeroDesc)
740{
741 pLogZeroDescConv->u32ZeroSignature = SET_ENDIAN_U32(pLogZeroDesc->u32ZeroSignature);
742 pLogZeroDescConv->u32Reserved = SET_ENDIAN_U32(pLogZeroDesc->u32Reserved);
743 pLogZeroDescConv->u64ZeroLength = SET_ENDIAN_U64(pLogZeroDesc->u64ZeroLength);
744 pLogZeroDescConv->u64FileOffset = SET_ENDIAN_U64(pLogZeroDesc->u64FileOffset);
745 pLogZeroDescConv->u64SequenceNumber = SET_ENDIAN_U64(pLogZeroDesc->u64SequenceNumber);
746}
747
748/**
749 * Converts a VHDX log data descriptor between file and host endianness.
750 *
751 * @returns nothing.
752 * @param enmConv Direction of the conversion.
753 * @param pLogDataDescConv Where to store the converted log data descriptor.
754 * @param pLogDataDesc The VHDX log data descriptor to convert.
755 *
756 * @note It is safe to use the same pointer for pLogDataDescConv and pLogDataDesc.
757 */
758DECLINLINE(void) vhdxConvLogDataDescEndianess(VHDXECONV enmConv, PVhdxLogDataDesc pLogDataDescConv,
759 PVhdxLogDataDesc pLogDataDesc)
760{
761 pLogDataDescConv->u32DataSignature = SET_ENDIAN_U32(pLogDataDesc->u32DataSignature);
762 pLogDataDescConv->u32TrailingBytes = SET_ENDIAN_U32(pLogDataDesc->u32TrailingBytes);
763 pLogDataDescConv->u64LeadingBytes = SET_ENDIAN_U64(pLogDataDesc->u64LeadingBytes);
764 pLogDataDescConv->u64FileOffset = SET_ENDIAN_U64(pLogDataDesc->u64FileOffset);
765 pLogDataDescConv->u64SequenceNumber = SET_ENDIAN_U64(pLogDataDesc->u64SequenceNumber);
766}
767
768/**
769 * Converts a VHDX log data sector between file and host endianness.
770 *
771 * @returns nothing.
772 * @param enmConv Direction of the conversion.
773 * @param pLogDataSectorConv Where to store the converted log data sector.
774 * @param pLogDataSector The VHDX log data sector to convert.
775 *
776 * @note It is safe to use the same pointer for pLogDataSectorConv and pLogDataSector.
777 */
778DECLINLINE(void) vhdxConvLogDataSectorEndianess(VHDXECONV enmConv, PVhdxLogDataSector pLogDataSectorConv,
779 PVhdxLogDataSector pLogDataSector)
780{
781 pLogDataSectorConv->u32DataSignature = SET_ENDIAN_U32(pLogDataSector->u32DataSignature);
782 pLogDataSectorConv->u32SequenceHigh = SET_ENDIAN_U32(pLogDataSector->u32SequenceHigh);
783 pLogDataSectorConv->u32SequenceLow = SET_ENDIAN_U32(pLogDataSector->u32SequenceLow);
784}
785
786/**
787 * Converts a BAT between file and host endianess.
788 *
789 * @returns nothing.
790 * @param enmConv Direction of the conversion.
791 * @param paBatEntriesConv Where to store the converted BAT.
792 * @param paBatEntries The VHDX BAT to convert.
793 *
794 * @note It is safe to use the same pointer for paBatEntriesConv and paBatEntries.
795 */
796DECLINLINE(void) vhdxConvBatTableEndianess(VHDXECONV enmConv, PVhdxBatEntry paBatEntriesConv,
797 PVhdxBatEntry paBatEntries, uint32_t cBatEntries)
798{
799 for (uint32_t i = 0; i < cBatEntries; i++)
800 paBatEntriesConv[i].u64BatEntry = SET_ENDIAN_U64(paBatEntries[i].u64BatEntry);
801}
802
803/**
804 * Converts a VHDX metadata table header between file and host endianness.
805 *
806 * @returns nothing.
807 * @param enmConv Direction of the conversion.
808 * @param pMetadataTblHdrConv Where to store the converted metadata table header.
809 * @param pMetadataTblHdr The VHDX metadata table header to convert.
810 *
811 * @note It is safe to use the same pointer for pMetadataTblHdrConv and pMetadataTblHdr.
812 */
813DECLINLINE(void) vhdxConvMetadataTblHdrEndianess(VHDXECONV enmConv, PVhdxMetadataTblHdr pMetadataTblHdrConv,
814 PVhdxMetadataTblHdr pMetadataTblHdr)
815{
816 pMetadataTblHdrConv->u64Signature = SET_ENDIAN_U64(pMetadataTblHdr->u64Signature);
817 pMetadataTblHdrConv->u16Reserved = SET_ENDIAN_U16(pMetadataTblHdr->u16Reserved);
818 pMetadataTblHdrConv->u16EntryCount = SET_ENDIAN_U16(pMetadataTblHdr->u16EntryCount);
819 for (unsigned i = 0; i < RT_ELEMENTS(pMetadataTblHdr->u32Reserved2); i++)
820 pMetadataTblHdrConv->u32Reserved2[i] = SET_ENDIAN_U32(pMetadataTblHdr->u32Reserved2[i]);
821}
822
823/**
824 * Converts a VHDX metadata table entry between file and host endianness.
825 *
826 * @returns nothing.
827 * @param enmConv Direction of the conversion.
828 * @param pMetadataTblEntryConv Where to store the converted metadata table entry.
829 * @param pMetadataTblEntry The VHDX metadata table entry to convert.
830 *
831 * @note It is safe to use the same pointer for pMetadataTblEntryConv and pMetadataTblEntry.
832 */
833DECLINLINE(void) vhdxConvMetadataTblEntryEndianess(VHDXECONV enmConv, PVhdxMetadataTblEntry pMetadataTblEntryConv,
834 PVhdxMetadataTblEntry pMetadataTblEntry)
835{
836 vhdxConvUuidEndianess(enmConv, &pMetadataTblEntryConv->UuidItem, &pMetadataTblEntry->UuidItem);
837 pMetadataTblEntryConv->u32Offset = SET_ENDIAN_U32(pMetadataTblEntry->u32Offset);
838 pMetadataTblEntryConv->u32Length = SET_ENDIAN_U32(pMetadataTblEntry->u32Length);
839 pMetadataTblEntryConv->u32Flags = SET_ENDIAN_U32(pMetadataTblEntry->u32Flags);
840 pMetadataTblEntryConv->u32Reserved = SET_ENDIAN_U32(pMetadataTblEntry->u32Reserved);
841}
842
843/**
844 * Converts a VHDX file parameters item between file and host endianness.
845 *
846 * @returns nothing.
847 * @param enmConv Direction of the conversion.
848 * @param pFileParamsConv Where to store the converted file parameters item entry.
849 * @param pFileParams The VHDX file parameters item to convert.
850 *
851 * @note It is safe to use the same pointer for pFileParamsConv and pFileParams.
852 */
853DECLINLINE(void) vhdxConvFileParamsEndianess(VHDXECONV enmConv, PVhdxFileParameters pFileParamsConv,
854 PVhdxFileParameters pFileParams)
855{
856 pFileParamsConv->u32BlockSize = SET_ENDIAN_U32(pFileParams->u32BlockSize);
857 pFileParamsConv->u32Flags = SET_ENDIAN_U32(pFileParams->u32Flags);
858}
859
860/**
861 * Converts a VHDX virtual disk size item between file and host endianness.
862 *
863 * @returns nothing.
864 * @param enmConv Direction of the conversion.
865 * @param pVDiskSizeConv Where to store the converted virtual disk size item entry.
866 * @param pVDiskSize The VHDX virtual disk size item to convert.
867 *
868 * @note It is safe to use the same pointer for pVDiskSizeConv and pVDiskSize.
869 */
870DECLINLINE(void) vhdxConvVDiskSizeEndianess(VHDXECONV enmConv, PVhdxVDiskSize pVDiskSizeConv,
871 PVhdxVDiskSize pVDiskSize)
872{
873 pVDiskSizeConv->u64VDiskSize = SET_ENDIAN_U64(pVDiskSize->u64VDiskSize);
874}
875
876/**
877 * Converts a VHDX page 83 data item between file and host endianness.
878 *
879 * @returns nothing.
880 * @param enmConv Direction of the conversion.
881 * @param pPage83DataConv Where to store the converted page 83 data item entry.
882 * @param pPage83Data The VHDX page 83 data item to convert.
883 *
884 * @note It is safe to use the same pointer for pPage83DataConv and pPage83Data.
885 */
886DECLINLINE(void) vhdxConvPage83DataEndianess(VHDXECONV enmConv, PVhdxPage83Data pPage83DataConv,
887 PVhdxPage83Data pPage83Data)
888{
889 vhdxConvUuidEndianess(enmConv, &pPage83DataConv->UuidPage83Data, &pPage83Data->UuidPage83Data);
890}
891
892/**
893 * Converts a VHDX logical sector size item between file and host endianness.
894 *
895 * @returns nothing.
896 * @param enmConv Direction of the conversion.
897 * @param pVDiskLogSectSizeConv Where to store the converted logical sector size item entry.
898 * @param pVDiskLogSectSize The VHDX logical sector size item to convert.
899 *
900 * @note It is safe to use the same pointer for pVDiskLogSectSizeConv and pVDiskLogSectSize.
901 */
902DECLINLINE(void) vhdxConvVDiskLogSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskLogicalSectorSize pVDiskLogSectSizeConv,
903 PVhdxVDiskLogicalSectorSize pVDiskLogSectSize)
904{
905 pVDiskLogSectSizeConv->u32LogicalSectorSize = SET_ENDIAN_U32(pVDiskLogSectSize->u32LogicalSectorSize);
906}
907
908/**
909 * Converts a VHDX physical sector size item between file and host endianness.
910 *
911 * @returns nothing.
912 * @param enmConv Direction of the conversion.
913 * @param pVDiskPhysSectSizeConv Where to store the converted physical sector size item entry.
914 * @param pVDiskPhysSectSize The VHDX physical sector size item to convert.
915 *
916 * @note It is safe to use the same pointer for pVDiskPhysSectSizeConv and pVDiskPhysSectSize.
917 */
918DECLINLINE(void) vhdxConvVDiskPhysSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskPhysicalSectorSize pVDiskPhysSectSizeConv,
919 PVhdxVDiskPhysicalSectorSize pVDiskPhysSectSize)
920{
921 pVDiskPhysSectSizeConv->u64PhysicalSectorSize = SET_ENDIAN_U64(pVDiskPhysSectSize->u64PhysicalSectorSize);
922}
923
924/**
925 * Converts a VHDX parent locator header item between file and host endianness.
926 *
927 * @returns nothing.
928 * @param enmConv Direction of the conversion.
929 * @param pParentLocatorHdrConv Where to store the converted parent locator header item entry.
930 * @param pParentLocatorHdr The VHDX parent locator header item to convert.
931 *
932 * @note It is safe to use the same pointer for pParentLocatorHdrConv and pParentLocatorHdr.
933 */
934DECLINLINE(void) vhdxConvParentLocatorHeaderEndianness(VHDXECONV enmConv, PVhdxParentLocatorHeader pParentLocatorHdrConv,
935 PVhdxParentLocatorHeader pParentLocatorHdr)
936{
937 vhdxConvUuidEndianess(enmConv, &pParentLocatorHdrConv->UuidLocatorType, &pParentLocatorHdr->UuidLocatorType);
938 pParentLocatorHdrConv->u16Reserved = SET_ENDIAN_U16(pParentLocatorHdr->u16Reserved);
939 pParentLocatorHdrConv->u16KeyValueCount = SET_ENDIAN_U16(pParentLocatorHdr->u16KeyValueCount);
940}
941
942/**
943 * Converts a VHDX parent locator entry between file and host endianness.
944 *
945 * @returns nothing.
946 * @param enmConv Direction of the conversion.
947 * @param pParentLocatorEntryConv Where to store the converted parent locator entry.
948 * @param pParentLocatorEntry The VHDX parent locator entry to convert.
949 *
950 * @note It is safe to use the same pointer for pParentLocatorEntryConv and pParentLocatorEntry.
951 */
952DECLINLINE(void) vhdxConvParentLocatorEntryEndianess(VHDXECONV enmConv, PVhdxParentLocatorEntry pParentLocatorEntryConv,
953 PVhdxParentLocatorEntry pParentLocatorEntry)
954{
955 pParentLocatorEntryConv->u32KeyOffset = SET_ENDIAN_U32(pParentLocatorEntry->u32KeyOffset);
956 pParentLocatorEntryConv->u32ValueOffset = SET_ENDIAN_U32(pParentLocatorEntry->u32ValueOffset);
957 pParentLocatorEntryConv->u16KeyLength = SET_ENDIAN_U16(pParentLocatorEntry->u16KeyLength);
958 pParentLocatorEntryConv->u16ValueLength = SET_ENDIAN_U16(pParentLocatorEntry->u16ValueLength);
959}
960
961/**
962 * Internal. Free all allocated space for representing an image except pImage,
963 * and optionally delete the image from disk.
964 */
965static int vhdxFreeImage(PVHDXIMAGE pImage, bool fDelete)
966{
967 int rc = VINF_SUCCESS;
968
969 /* Freeing a never allocated image (e.g. because the open failed) is
970 * not signalled as an error. After all nothing bad happens. */
971 if (pImage)
972 {
973 if (pImage->pStorage)
974 {
975 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
976 pImage->pStorage = NULL;
977 }
978
979 if (pImage->paBat)
980 {
981 RTMemFree(pImage->paBat);
982 pImage->paBat = NULL;
983 }
984
985 if (fDelete && pImage->pszFilename)
986 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
987 }
988
989 LogFlowFunc(("returns %Rrc\n", rc));
990 return rc;
991}
992
993/**
994 * Loads all required fields from the given VHDX header.
995 * The header must be converted to the host endianess and validated already.
996 *
997 * @returns VBox status code.
998 * @param pImage Image instance data.
999 * @param pHdr The header to load.
1000 */
1001static int vhdxLoadHeader(PVHDXIMAGE pImage, PVhdxHeader pHdr)
1002{
1003 int rc = VINF_SUCCESS;
1004
1005 LogFlowFunc(("pImage=%#p pHdr=%#p\n", pImage, pHdr));
1006
1007 /*
1008 * Most fields in the header are not required because the backend implements
1009 * readonly access only so far.
1010 * We just have to check that the log is empty, we have to refuse to load the
1011 * image otherwsie because replaying the log is not implemented.
1012 */
1013 if (pHdr->u16Version == VHDX_HEADER_VHDX_VERSION)
1014 {
1015 /* Check that the log UUID is zero. */
1016 pImage->uVersion = pHdr->u16Version;
1017 if (!RTUuidIsNull(&pHdr->UuidLog))
1018 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1019 "VHDX: Image \'%s\' has a non empty log which is not supported",
1020 pImage->pszFilename);
1021 }
1022 else
1023 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1024 "VHDX: Image \'%s\' uses an unsupported version (%u) of the VHDX format",
1025 pImage->pszFilename, pHdr->u16Version);
1026
1027 LogFlowFunc(("return rc=%Rrc\n", rc));
1028 return rc;
1029}
1030
1031/**
1032 * Determines the current header and loads it.
1033 *
1034 * @returns VBox status code.
1035 * @param pImage Image instance data.
1036 */
1037static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
1038{
1039 PVhdxHeader pHdr1, pHdr2;
1040 uint32_t u32ChkSum = 0;
1041 uint32_t u32ChkSumSaved = 0;
1042 bool fHdr1Valid = false;
1043 bool fHdr2Valid = false;
1044 int rc = VINF_SUCCESS;
1045
1046 LogFlowFunc(("pImage=%#p\n", pImage));
1047
1048 /*
1049 * The VHDX format defines two headers at different offsets to provide failure
1050 * consistency. Only one header is current. This can be determined using the
1051 * sequence number and checksum fields in the header.
1052 */
1053 pHdr1 = (PVhdxHeader)RTMemAllocZ(sizeof(VhdxHeader));
1054 pHdr2 = (PVhdxHeader)RTMemAllocZ(sizeof(VhdxHeader));
1055
1056 if (pHdr1 && pHdr2)
1057 {
1058 /* Read the first header. */
1059 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER1_OFFSET,
1060 pHdr1, sizeof(*pHdr1));
1061 if (RT_SUCCESS(rc))
1062 {
1063 vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr1, pHdr1);
1064
1065 /* Validate checksum. */
1066 u32ChkSumSaved = pHdr1->u32Checksum;
1067 pHdr1->u32Checksum = 0;
1068 u32ChkSum = RTCrc32C(pHdr1, sizeof(VhdxHeader));
1069
1070 if ( pHdr1->u32Signature == VHDX_HEADER_SIGNATURE
1071 && u32ChkSum == u32ChkSumSaved)
1072 fHdr1Valid = true;
1073 }
1074
1075 /* Try to read the second header in any case (even if reading the first failed). */
1076 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER2_OFFSET,
1077 pHdr2, sizeof(*pHdr2));
1078 if (RT_SUCCESS(rc))
1079 {
1080 vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr2, pHdr2);
1081
1082 /* Validate checksum. */
1083 u32ChkSumSaved = pHdr2->u32Checksum;
1084 pHdr2->u32Checksum = 0;
1085 u32ChkSum = RTCrc32C(pHdr2, sizeof(VhdxHeader));
1086
1087 if ( pHdr2->u32Signature == VHDX_HEADER_SIGNATURE
1088 && u32ChkSum == u32ChkSumSaved)
1089 fHdr2Valid = true;
1090 }
1091
1092 /* Determine the current header. */
1093 if (fHdr1Valid != fHdr2Valid)
1094 {
1095 /* Only one header is valid - use it. */
1096 rc = vhdxLoadHeader(pImage, fHdr1Valid ? pHdr1 : pHdr2);
1097 }
1098 else if (!fHdr1Valid && !fHdr2Valid)
1099 {
1100 /* Crap, both headers are corrupt, refuse to load the image. */
1101 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1102 "VHDX: Can not load the image because both headers are corrupt");
1103 }
1104 else
1105 {
1106 /* Both headers are valid. Use the sequence number to find the current one. */
1107 if (pHdr1->u64SequenceNumber > pHdr2->u64SequenceNumber)
1108 rc = vhdxLoadHeader(pImage, pHdr1);
1109 else
1110 rc = vhdxLoadHeader(pImage, pHdr2);
1111 }
1112 }
1113 else
1114 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1115 "VHDX: Out of memory while allocating memory for the header");
1116
1117 if (pHdr1)
1118 RTMemFree(pHdr1);
1119 if (pHdr2)
1120 RTMemFree(pHdr2);
1121
1122 LogFlowFunc(("returns rc=%Rrc\n", rc));
1123 return rc;
1124}
1125
1126/**
1127 * Loads the BAT region.
1128 *
1129 * @returns VBox status code.
1130 * @param pImage Image instance data.
1131 * @param offRegion Start offset of the region.
1132 * @param cbRegion Size of the region.
1133 */
1134static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
1135 size_t cbRegion)
1136{
1137 int rc = VINF_SUCCESS;
1138 uint32_t cDataBlocks;
1139 uint32_t uChunkRatio;
1140 uint32_t cSectorBitmapBlocks;
1141 uint32_t cBatEntries;
1142 uint32_t cbBatEntries;
1143 PVhdxBatEntry paBatEntries = NULL;
1144
1145 LogFlowFunc(("pImage=%#p\n", pImage));
1146
1147 /* Calculate required values first. */
1148 uint64_t uChunkRatio64 = (RT_BIT_64(23) * pImage->cbLogicalSector) / pImage->cbBlock;
1149 uChunkRatio = (uint32_t)uChunkRatio64; Assert(uChunkRatio == uChunkRatio64);
1150 uint64_t cDataBlocks64 = pImage->cbSize / pImage->cbBlock;
1151 cDataBlocks = (uint32_t)cDataBlocks64; Assert(cDataBlocks == cDataBlocks64);
1152
1153 if (pImage->cbSize % pImage->cbBlock)
1154 cDataBlocks++;
1155
1156 cSectorBitmapBlocks = cDataBlocks / uChunkRatio;
1157 if (cDataBlocks % uChunkRatio)
1158 cSectorBitmapBlocks++;
1159
1160 cBatEntries = cDataBlocks + (cDataBlocks - 1)/uChunkRatio;
1161 cbBatEntries = cBatEntries * sizeof(VhdxBatEntry);
1162
1163 if (cbBatEntries <= cbRegion)
1164 {
1165 /*
1166 * Load the complete BAT region first, convert to host endianess and process
1167 * it afterwards. The SB entries can be removed because they are not needed yet.
1168 */
1169 paBatEntries = (PVhdxBatEntry)RTMemAlloc(cbBatEntries);
1170 if (paBatEntries)
1171 {
1172 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
1173 paBatEntries, cbBatEntries);
1174 if (RT_SUCCESS(rc))
1175 {
1176 vhdxConvBatTableEndianess(VHDXECONV_F2H, paBatEntries, paBatEntries,
1177 cBatEntries);
1178
1179 /* Go through the table and validate it. */
1180 for (unsigned i = 0; i < cBatEntries; i++)
1181 {
1182 if ( i != 0
1183 && (i % uChunkRatio) == 0)
1184 {
1185/**
1186 * Disabled the verification because there are images out there with the sector bitmap
1187 * marked as present. The entry is never accessed and the image is readonly anyway,
1188 * so no harm done.
1189 */
1190#if 0
1191 /* Sector bitmap block. */
1192 if ( VHDX_BAT_ENTRY_GET_STATE(paBatEntries[i].u64BatEntry)
1193 != VHDX_BAT_ENTRY_SB_BLOCK_NOT_PRESENT)
1194 {
1195 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1196 "VHDX: Sector bitmap block at entry %u of image \'%s\' marked as present, violation of the specification",
1197 i, pImage->pszFilename);
1198 break;
1199 }
1200#endif
1201 }
1202 else
1203 {
1204 /* Payload block. */
1205 if ( VHDX_BAT_ENTRY_GET_STATE(paBatEntries[i].u64BatEntry)
1206 == VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1207 {
1208 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1209 "VHDX: Payload block at entry %u of image \'%s\' marked as partially present, violation of the specification",
1210 i, pImage->pszFilename);
1211 break;
1212 }
1213 }
1214 }
1215
1216 if (RT_SUCCESS(rc))
1217 {
1218 pImage->paBat = paBatEntries;
1219 pImage->uChunkRatio = uChunkRatio;
1220 }
1221 }
1222 else
1223 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1224 "VHDX: Error reading the BAT from image \'%s\'",
1225 pImage->pszFilename);
1226 }
1227 else
1228 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1229 "VHDX: Out of memory allocating memory for %u BAT entries of image \'%s\'",
1230 cBatEntries);
1231 }
1232 else
1233 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1234 "VHDX: Mismatch between calculated number of BAT entries and region size (expected %u got %u) for image \'%s\'",
1235 cbBatEntries, cbRegion, pImage->pszFilename);
1236
1237 if ( RT_FAILURE(rc)
1238 && paBatEntries)
1239 RTMemFree(paBatEntries);
1240
1241 LogFlowFunc(("returns rc=%Rrc\n", rc));
1242 return rc;
1243}
1244
1245/**
1246 * Load the file parameters metadata item from the file.
1247 *
1248 * @returns VBox status code.
1249 * @param pImage Image instance data.
1250 * @param offItem File offset where the data is stored.
1251 * @param cbItem Size of the item in the file.
1252 */
1253static int vhdxLoadFileParametersMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1254{
1255 int rc = VINF_SUCCESS;
1256
1257 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1258
1259 if (cbItem != sizeof(VhdxFileParameters))
1260 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1261 "VHDX: File parameters item size mismatch (expected %u got %zu) in image \'%s\'",
1262 sizeof(VhdxFileParameters), cbItem, pImage->pszFilename);
1263 else
1264 {
1265 VhdxFileParameters FileParameters;
1266
1267 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1268 &FileParameters, sizeof(FileParameters));
1269 if (RT_SUCCESS(rc))
1270 {
1271 vhdxConvFileParamsEndianess(VHDXECONV_F2H, &FileParameters, &FileParameters);
1272 pImage->cbBlock = FileParameters.u32BlockSize;
1273
1274 /* @todo: No support for differencing images yet. */
1275 if (FileParameters.u32Flags & VHDX_FILE_PARAMETERS_FLAGS_HAS_PARENT)
1276 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1277 "VHDX: Image \'%s\' is a differencing image which is not supported yet",
1278 pImage->pszFilename);
1279 }
1280 else
1281 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1282 "VHDX: Reading the file parameters metadata item from image \'%s\' failed",
1283 pImage->pszFilename);
1284 }
1285
1286 LogFlowFunc(("returns rc=%Rrc\n", rc));
1287 return rc;
1288}
1289
1290/**
1291 * Load the virtual disk size metadata item from the file.
1292 *
1293 * @returns VBox status code.
1294 * @param pImage Image instance data.
1295 * @param offItem File offset where the data is stored.
1296 * @param cbItem Size of the item in the file.
1297 */
1298static int vhdxLoadVDiskSizeMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1299{
1300 int rc = VINF_SUCCESS;
1301
1302 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1303
1304 if (cbItem != sizeof(VhdxVDiskSize))
1305 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1306 "VHDX: Virtual disk size item size mismatch (expected %u got %zu) in image \'%s\'",
1307 sizeof(VhdxVDiskSize), cbItem, pImage->pszFilename);
1308 else
1309 {
1310 VhdxVDiskSize VDiskSize;
1311
1312 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1313 &VDiskSize, sizeof(VDiskSize));
1314 if (RT_SUCCESS(rc))
1315 {
1316 vhdxConvVDiskSizeEndianess(VHDXECONV_F2H, &VDiskSize, &VDiskSize);
1317 pImage->cbSize = VDiskSize.u64VDiskSize;
1318 }
1319 else
1320 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1321 "VHDX: Reading the virtual disk size metadata item from image \'%s\' failed",
1322 pImage->pszFilename);
1323 }
1324
1325 LogFlowFunc(("returns rc=%Rrc\n", rc));
1326 return rc;
1327}
1328
1329/**
1330 * Load the logical sector size metadata item from the file.
1331 *
1332 * @returns VBox status code.
1333 * @param pImage Image instance data.
1334 * @param offItem File offset where the data is stored.
1335 * @param cbItem Size of the item in the file.
1336 */
1337static int vhdxLoadVDiskLogSectorSizeMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1338{
1339 int rc = VINF_SUCCESS;
1340
1341 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1342
1343 if (cbItem != sizeof(VhdxVDiskLogicalSectorSize))
1344 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1345 "VHDX: Virtual disk logical sector size item size mismatch (expected %u got %zu) in image \'%s\'",
1346 sizeof(VhdxVDiskLogicalSectorSize), cbItem, pImage->pszFilename);
1347 else
1348 {
1349 VhdxVDiskLogicalSectorSize VDiskLogSectSize;
1350
1351 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1352 &VDiskLogSectSize, sizeof(VDiskLogSectSize));
1353 if (RT_SUCCESS(rc))
1354 {
1355 vhdxConvVDiskLogSectSizeEndianess(VHDXECONV_F2H, &VDiskLogSectSize,
1356 &VDiskLogSectSize);
1357 pImage->cbLogicalSector = VDiskLogSectSize.u32LogicalSectorSize;
1358 }
1359 else
1360 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1361 "VHDX: Reading the virtual disk logical sector size metadata item from image \'%s\' failed",
1362 pImage->pszFilename);
1363 }
1364
1365 LogFlowFunc(("returns rc=%Rrc\n", rc));
1366 return rc;
1367}
1368
1369/**
1370 * Loads the metadata region.
1371 *
1372 * @returns VBox status code.
1373 * @param pImage Image instance data.
1374 * @param offRegion Start offset of the region.
1375 * @param cbRegion Size of the region.
1376 */
1377static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
1378 size_t cbRegion)
1379{
1380 VhdxMetadataTblHdr MetadataTblHdr;
1381 int rc = VINF_SUCCESS;
1382
1383 LogFlowFunc(("pImage=%#p\n", pImage));
1384
1385 /* Load the header first. */
1386 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
1387 &MetadataTblHdr, sizeof(MetadataTblHdr));
1388 if (RT_SUCCESS(rc))
1389 {
1390 vhdxConvMetadataTblHdrEndianess(VHDXECONV_F2H, &MetadataTblHdr, &MetadataTblHdr);
1391
1392 /* Validate structure. */
1393 if (MetadataTblHdr.u64Signature != VHDX_METADATA_TBL_HDR_SIGNATURE)
1394 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1395 "VHDX: Incorrect metadata table header signature for image \'%s\'",
1396 pImage->pszFilename);
1397 else if (MetadataTblHdr.u16EntryCount > VHDX_METADATA_TBL_HDR_ENTRY_COUNT_MAX)
1398 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1399 "VHDX: Incorrect entry count in metadata table header of image \'%s\'",
1400 pImage->pszFilename);
1401 else if (cbRegion < (MetadataTblHdr.u16EntryCount * sizeof(VhdxMetadataTblEntry) + sizeof(VhdxMetadataTblHdr)))
1402 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1403 "VHDX: Metadata table of image \'%s\' exceeds region size",
1404 pImage->pszFilename);
1405
1406 if (RT_SUCCESS(rc))
1407 {
1408 uint64_t offMetadataTblEntry = offRegion + sizeof(VhdxMetadataTblHdr);
1409
1410 for (unsigned i = 0; i < MetadataTblHdr.u16EntryCount; i++)
1411 {
1412 uint64_t offMetadataItem = 0;
1413 VHDXMETADATAITEM enmMetadataItem = VHDXMETADATAITEM_UNKNOWN;
1414 VhdxMetadataTblEntry MetadataTblEntry;
1415
1416 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offMetadataTblEntry,
1417 &MetadataTblEntry, sizeof(MetadataTblEntry));
1418 if (RT_FAILURE(rc))
1419 {
1420 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1421 "VHDX: Reading metadata table entry from image \'%s\' failed",
1422 pImage->pszFilename);
1423 break;
1424 }
1425
1426 vhdxConvMetadataTblEntryEndianess(VHDXECONV_F2H, &MetadataTblEntry, &MetadataTblEntry);
1427
1428 /* Check whether the flags match the expectations. */
1429 for (unsigned idxProp = 0; idxProp < RT_ELEMENTS(s_aVhdxMetadataItemProps); idxProp++)
1430 {
1431 if (!RTUuidCompareStr(&MetadataTblEntry.UuidItem,
1432 s_aVhdxMetadataItemProps[idxProp].pszItemUuid))
1433 {
1434 /*
1435 * Check for specification violations and bail out, except
1436 * for the required flag of the physical sector size metadata item.
1437 * Early images had the required flag not set opposed to the specification.
1438 * We don't want to brerak those images.
1439 */
1440 if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_USER)
1441 != s_aVhdxMetadataItemProps[idxProp].fIsUser)
1442 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1443 "VHDX: User flag of metadata item does not meet expectations \'%s\'",
1444 pImage->pszFilename);
1445 else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_VDISK)
1446 != s_aVhdxMetadataItemProps[idxProp].fIsVDisk)
1447 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1448 "VHDX: Virtual disk flag of metadata item does not meet expectations \'%s\'",
1449 pImage->pszFilename);
1450 else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
1451 != s_aVhdxMetadataItemProps[idxProp].fIsRequired
1452 && (s_aVhdxMetadataItemProps[idxProp].enmMetadataItem != VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE))
1453 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1454 "VHDX: Required flag of metadata item does not meet expectations \'%s\'",
1455 pImage->pszFilename);
1456 else
1457 enmMetadataItem = s_aVhdxMetadataItemProps[idxProp].enmMetadataItem;
1458
1459 break;
1460 }
1461 }
1462
1463 if (RT_FAILURE(rc))
1464 break;
1465
1466 offMetadataItem = offRegion + MetadataTblEntry.u32Offset;
1467
1468 switch (enmMetadataItem)
1469 {
1470 case VHDXMETADATAITEM_FILE_PARAMS:
1471 {
1472 rc = vhdxLoadFileParametersMetadata(pImage, offMetadataItem,
1473 MetadataTblEntry.u32Length);
1474 break;
1475 }
1476 case VHDXMETADATAITEM_VDISK_SIZE:
1477 {
1478 rc = vhdxLoadVDiskSizeMetadata(pImage, offMetadataItem,
1479 MetadataTblEntry.u32Length);
1480 break;
1481 }
1482 case VHDXMETADATAITEM_PAGE83_DATA:
1483 {
1484 /*
1485 * Nothing to do here for now (marked as required but
1486 * there is no API to pass this information to the caller)
1487 * so far.
1488 */
1489 break;
1490 }
1491 case VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE:
1492 {
1493 rc = vhdxLoadVDiskLogSectorSizeMetadata(pImage, offMetadataItem,
1494 MetadataTblEntry.u32Length);
1495 break;
1496 }
1497 case VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE:
1498 {
1499 /*
1500 * Nothing to do here for now (marked as required but
1501 * there is no API to pass this information to the caller)
1502 * so far.
1503 */
1504 break;
1505 }
1506 case VHDXMETADATAITEM_PARENT_LOCATOR:
1507 {
1508 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1509 "VHDX: Image \'%s\' is a differencing image which is not supported yet",
1510 pImage->pszFilename);
1511 break;
1512 }
1513 case VHDXMETADATAITEM_UNKNOWN:
1514 default:
1515 if (MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
1516 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1517 "VHDX: Unsupported but required metadata item in image \'%s\'",
1518 pImage->pszFilename);
1519 }
1520
1521 if (RT_FAILURE(rc))
1522 break;
1523
1524 offMetadataTblEntry += sizeof(MetadataTblEntry);
1525 }
1526 }
1527 }
1528 else
1529 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1530 "VHDX: Reading the metadata table header for image \'%s\' failed",
1531 pImage->pszFilename);
1532
1533 LogFlowFunc(("returns rc=%Rrc\n", rc));
1534 return rc;
1535}
1536
1537/**
1538 * Loads the region table and the associated regions.
1539 *
1540 * @returns VBox status code.
1541 * @param pImage Image instance data.
1542 */
1543static int vhdxLoadRegionTable(PVHDXIMAGE pImage)
1544{
1545 uint8_t *pbRegionTbl = NULL;
1546 int rc = VINF_SUCCESS;
1547
1548 LogFlowFunc(("pImage=%#p\n", pImage));
1549
1550 /* Load the complete region table into memory. */
1551 pbRegionTbl = (uint8_t *)RTMemTmpAlloc(VHDX_REGION_TBL_SIZE_MAX);
1552 if (pbRegionTbl)
1553 {
1554 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_REGION_TBL_HDR_OFFSET,
1555 pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX);
1556 if (RT_SUCCESS(rc))
1557 {
1558 PVhdxRegionTblHdr pRegionTblHdr;
1559 VhdxRegionTblHdr RegionTblHdr;
1560 uint32_t u32ChkSumSaved = 0;
1561 uint32_t u32ChkSum = 0;
1562
1563 /*
1564 * Copy the region table header to a dedicated structure where we can
1565 * convert it to host endianess.
1566 */
1567 memcpy(&RegionTblHdr, pbRegionTbl, sizeof(RegionTblHdr));
1568 vhdxConvRegionTblHdrEndianess(VHDXECONV_F2H, &RegionTblHdr, &RegionTblHdr);
1569
1570 /* Set checksum field to 0 during crc computation. */
1571 pRegionTblHdr = (PVhdxRegionTblHdr)pbRegionTbl;
1572 pRegionTblHdr->u32Checksum = 0;
1573
1574 /* Verify the region table integrity. */
1575 u32ChkSum = RTCrc32C(pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX);
1576
1577 if (RegionTblHdr.u32Signature != VHDX_REGION_TBL_HDR_SIGNATURE)
1578 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1579 "VHDX: Invalid signature for region table header of image \'%s\'",
1580 pImage->pszFilename);
1581 else if (u32ChkSum != RegionTblHdr.u32Checksum)
1582 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1583 "VHDX: CRC32 checksum mismatch for the region table of image \'%s\' (expected %#x got %#x)",
1584 pImage->pszFilename, RegionTblHdr.u32Checksum, u32ChkSum);
1585 else if (RegionTblHdr.u32EntryCount > VHDX_REGION_TBL_HDR_ENTRY_COUNT_MAX)
1586 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1587 "VHDX: Invalid entry count field in the region table header of image \'%s\'",
1588 pImage->pszFilename);
1589
1590 if (RT_SUCCESS(rc))
1591 {
1592 /* Parse the region table entries. */
1593 PVhdxRegionTblEntry pRegTblEntry = (PVhdxRegionTblEntry)(pbRegionTbl + sizeof(VhdxRegionTblHdr));
1594 VhdxRegionTblEntry RegTblEntryBat; /* BAT region table entry. */
1595 bool fBatRegPresent = false;
1596 RT_ZERO(RegTblEntryBat); /* Maybe uninitialized, gcc. */
1597
1598 for (unsigned i = 0; i < RegionTblHdr.u32EntryCount; i++)
1599 {
1600 vhdxConvRegionTblEntryEndianess(VHDXECONV_F2H, pRegTblEntry, pRegTblEntry);
1601
1602 /* Check the uuid for known regions. */
1603 if (!RTUuidCompareStr(&pRegTblEntry->UuidObject, VHDX_REGION_TBL_ENTRY_UUID_BAT))
1604 {
1605 /*
1606 * Save the BAT region and process it later.
1607 * It may come before the metadata region but needs the block size.
1608 */
1609 if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1610 {
1611 fBatRegPresent = true;
1612 RegTblEntryBat.u32Length = pRegTblEntry->u32Length;
1613 RegTblEntryBat.u64FileOffset = pRegTblEntry->u64FileOffset;
1614 }
1615 else
1616 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1617 "VHDX: BAT region not marked as required in image \'%s\'",
1618 pImage->pszFilename);
1619 }
1620 else if (!RTUuidCompareStr(&pRegTblEntry->UuidObject, VHDX_REGION_TBL_ENTRY_UUID_METADATA))
1621 {
1622 if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1623 rc = vhdxLoadMetadataRegion(pImage, pRegTblEntry->u64FileOffset, pRegTblEntry->u32Length);
1624 else
1625 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1626 "VHDX: Metadata region not marked as required in image \'%s\'",
1627 pImage->pszFilename);
1628 }
1629 else if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1630 {
1631 /* The region is not known but marked as required, fail to load the image. */
1632 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1633 "VHDX: Unknown required region in image \'%s\'",
1634 pImage->pszFilename);
1635 }
1636
1637 if (RT_FAILURE(rc))
1638 break;
1639
1640 pRegTblEntry++;
1641 }
1642
1643 if (fBatRegPresent)
1644 rc = vhdxLoadBatRegion(pImage, RegTblEntryBat.u64FileOffset, RegTblEntryBat.u32Length);
1645 else
1646 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1647 "VHDX: BAT region in image \'%s\' is missing",
1648 pImage->pszFilename);
1649 }
1650 }
1651 else
1652 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1653 "VHDX: Reading the region table for image \'%s\' failed",
1654 pImage->pszFilename);
1655 }
1656 else
1657 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1658 "VHDX: Out of memory allocating memory for the region table of image \'%s\'",
1659 pImage->pszFilename);
1660
1661 if (pbRegionTbl)
1662 RTMemTmpFree(pbRegionTbl);
1663
1664 LogFlowFunc(("returns rc=%Rrc\n", rc));
1665 return rc;
1666}
1667
1668/**
1669 * Internal: Open an image, constructing all necessary data structures.
1670 */
1671static int vhdxOpenImage(PVHDXIMAGE pImage, unsigned uOpenFlags)
1672{
1673 uint64_t cbFile = 0;
1674 VhdxFileIdentifier FileIdentifier;
1675 int rc = VINF_SUCCESS;
1676
1677 LogFlowFunc(("pImage=%#p uOpenFlags=%#x\n", pImage, uOpenFlags));
1678 pImage->uOpenFlags = uOpenFlags;
1679
1680 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1681 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1682 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
1683
1684 /* Refuse write access, it is not implemented so far. */
1685 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1686 return VERR_NOT_SUPPORTED;
1687
1688 /*
1689 * Open the image.
1690 */
1691 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1692 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1693 false /* fCreate */),
1694 &pImage->pStorage);
1695
1696 /* Do NOT signal an appropriate error here, as the VD layer has the
1697 * choice of retrying the open if it failed. */
1698 if (RT_SUCCESS(rc))
1699 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1700
1701 if (RT_SUCCESS(rc))
1702 {
1703 if (cbFile > sizeof(FileIdentifier))
1704 {
1705 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
1706 &FileIdentifier, sizeof(FileIdentifier));
1707 if (RT_SUCCESS(rc))
1708 {
1709 vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
1710 &FileIdentifier);
1711 if (FileIdentifier.u64Signature != VHDX_FILE_IDENTIFIER_SIGNATURE)
1712 rc = VERR_VD_GEN_INVALID_HEADER;
1713 else
1714 rc = vhdxFindAndLoadCurrentHeader(pImage);
1715
1716 /* Load the region table. */
1717 if (RT_SUCCESS(rc))
1718 rc = vhdxLoadRegionTable(pImage);
1719 }
1720 }
1721 else
1722 rc = VERR_VD_GEN_INVALID_HEADER;
1723 }
1724
1725 if (RT_FAILURE(rc))
1726 vhdxFreeImage(pImage, false);
1727
1728 LogFlowFunc(("returns rc=%Rrc\n", rc));
1729 return rc;
1730}
1731
1732
1733/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
1734static int vhdxCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1735 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1736{
1737 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1738 PVDIOSTORAGE pStorage = NULL;
1739 uint64_t cbFile;
1740 int rc = VINF_SUCCESS;
1741 VhdxFileIdentifier FileIdentifier;
1742
1743 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1744 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1745
1746 if ( !VALID_PTR(pszFilename)
1747 || !*pszFilename)
1748 rc = VERR_INVALID_PARAMETER;
1749 else
1750 {
1751 /*
1752 * Open the file and read the file identifier.
1753 */
1754 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1755 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1756 false /* fCreate */),
1757 &pStorage);
1758 if (RT_SUCCESS(rc))
1759 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1760
1761 if (RT_SUCCESS(rc))
1762 {
1763 if (cbFile > sizeof(FileIdentifier))
1764 {
1765 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
1766 &FileIdentifier, sizeof(FileIdentifier));
1767 if (RT_SUCCESS(rc))
1768 {
1769 vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
1770 &FileIdentifier);
1771 if (FileIdentifier.u64Signature != VHDX_FILE_IDENTIFIER_SIGNATURE)
1772 rc = VERR_VD_GEN_INVALID_HEADER;
1773 else
1774 *penmType = VDTYPE_HDD;
1775 }
1776 }
1777 else
1778 rc = VERR_VD_GEN_INVALID_HEADER;
1779 }
1780
1781 if (pStorage)
1782 vdIfIoIntFileClose(pIfIo, pStorage);
1783 }
1784
1785 LogFlowFunc(("returns %Rrc\n", rc));
1786 return rc;
1787}
1788
1789/** @copydoc VBOXHDDBACKEND::pfnOpen */
1790static int vhdxOpen(const char *pszFilename, unsigned uOpenFlags,
1791 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1792 VDTYPE enmType, void **ppBackendData)
1793{
1794 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1795 int rc;
1796 PVHDXIMAGE pImage;
1797
1798 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
1799
1800 /* Check open flags. All valid flags are supported. */
1801 if ( uOpenFlags & ~VD_OPEN_FLAGS_MASK
1802 || !VALID_PTR(pszFilename)
1803 || !*pszFilename)
1804 rc = VERR_INVALID_PARAMETER;
1805 else
1806 {
1807 pImage = (PVHDXIMAGE)RTMemAllocZ(sizeof(VHDXIMAGE));
1808 if (!pImage)
1809 rc = VERR_NO_MEMORY;
1810 else
1811 {
1812 pImage->pszFilename = pszFilename;
1813 pImage->pStorage = NULL;
1814 pImage->pVDIfsDisk = pVDIfsDisk;
1815 pImage->pVDIfsImage = pVDIfsImage;
1816
1817 rc = vhdxOpenImage(pImage, uOpenFlags);
1818 if (RT_SUCCESS(rc))
1819 *ppBackendData = pImage;
1820 else
1821 RTMemFree(pImage);
1822 }
1823 }
1824
1825 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1826 return rc;
1827}
1828
1829/** @interface_method_impl{VBOXHDDBACKEND,pfnCreate} */
1830static DECLCALLBACK(int) vhdxCreate(const char *pszFilename, uint64_t cbSize,
1831 unsigned uImageFlags, const char *pszComment,
1832 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1833 PCRTUUID pUuid, unsigned uOpenFlags,
1834 unsigned uPercentStart, unsigned uPercentSpan,
1835 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1836 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1837 void **ppBackendData)
1838{
1839 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p",
1840 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1841 int rc = VERR_NOT_SUPPORTED;
1842
1843 LogFlowFunc(("returns %Rrc\n", rc));
1844 return rc;
1845}
1846
1847/** @copydoc VBOXHDDBACKEND::pfnRename */
1848static int vhdxRename(void *pBackendData, const char *pszFilename)
1849{
1850 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1851 int rc = VINF_SUCCESS;
1852 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1853
1854 /* Check arguments. */
1855 if ( !pImage
1856 || !pszFilename
1857 || !*pszFilename)
1858 rc = VERR_INVALID_PARAMETER;
1859 else
1860 {
1861 /* Close the image. */
1862 rc = vhdxFreeImage(pImage, false);
1863 if (RT_SUCCESS(rc))
1864 {
1865 /* Rename the file. */
1866 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1867 if (RT_FAILURE(rc))
1868 {
1869 /* The move failed, try to reopen the original image. */
1870 int rc2 = vhdxOpenImage(pImage, pImage->uOpenFlags);
1871 if (RT_FAILURE(rc2))
1872 rc = rc2;
1873 }
1874 else
1875 {
1876 /* Update pImage with the new information. */
1877 pImage->pszFilename = pszFilename;
1878
1879 /* Open the old image with new name. */
1880 rc = vhdxOpenImage(pImage, pImage->uOpenFlags);
1881 }
1882 }
1883 }
1884
1885 LogFlowFunc(("returns %Rrc\n", rc));
1886 return rc;
1887}
1888
1889/** @copydoc VBOXHDDBACKEND::pfnClose */
1890static int vhdxClose(void *pBackendData, bool fDelete)
1891{
1892 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1893 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1894 int rc;
1895
1896 rc = vhdxFreeImage(pImage, fDelete);
1897 RTMemFree(pImage);
1898
1899 LogFlowFunc(("returns %Rrc\n", rc));
1900 return rc;
1901}
1902
1903/** @copydoc VBOXHDDBACKEND::pfnRead */
1904static int vhdxRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1905 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1906{
1907 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1908 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1909 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1910 int rc = VINF_SUCCESS;
1911
1912 AssertPtr(pImage);
1913 Assert(uOffset % 512 == 0);
1914 Assert(cbToRead % 512 == 0);
1915
1916 if ( uOffset + cbToRead > pImage->cbSize
1917 || cbToRead == 0)
1918 rc = VERR_INVALID_PARAMETER;
1919 else
1920 {
1921 uint32_t idxBat = (uint32_t)(uOffset / pImage->cbBlock); Assert(idxBat == uOffset / pImage->cbBlock);
1922 uint32_t offRead = uOffset % pImage->cbBlock;
1923 uint64_t uBatEntry;
1924
1925 idxBat += idxBat / pImage->uChunkRatio; /* Add interleaving sector bitmap entries. */
1926 uBatEntry = pImage->paBat[idxBat].u64BatEntry;
1927
1928 cbToRead = RT_MIN(cbToRead, pImage->cbBlock - offRead);
1929
1930 switch (VHDX_BAT_ENTRY_GET_STATE(uBatEntry))
1931 {
1932 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_NOT_PRESENT:
1933 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNDEFINED:
1934 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_ZERO:
1935 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNMAPPED:
1936 {
1937 vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
1938 break;
1939 }
1940 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_FULLY_PRESENT:
1941 {
1942 uint64_t offFile = VHDX_BAT_ENTRY_GET_FILE_OFFSET(uBatEntry) + offRead;
1943 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
1944 pIoCtx, cbToRead);
1945 break;
1946 }
1947 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT:
1948 default:
1949 rc = VERR_INVALID_PARAMETER;
1950 break;
1951 }
1952
1953 if (pcbActuallyRead)
1954 *pcbActuallyRead = cbToRead;
1955 }
1956
1957 LogFlowFunc(("returns %Rrc\n", rc));
1958 return rc;
1959}
1960
1961/** @copydoc VBOXHDDBACKEND::pfnWrite */
1962static int vhdxWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1963 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1964 size_t *pcbPostRead, unsigned fWrite)
1965{
1966 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1967 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1968 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1969 int rc;
1970
1971 AssertPtr(pImage);
1972 Assert(uOffset % 512 == 0);
1973 Assert(cbToWrite % 512 == 0);
1974
1975 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1976 rc = VERR_VD_IMAGE_READ_ONLY;
1977 else if ( uOffset + cbToWrite > pImage->cbSize
1978 || cbToWrite == 0)
1979 rc = VERR_INVALID_PARAMETER;
1980 else
1981 rc = VERR_NOT_SUPPORTED;
1982
1983 LogFlowFunc(("returns %Rrc\n", rc));
1984 return rc;
1985}
1986
1987/** @copydoc VBOXHDDBACKEND::pfnFlush */
1988static int vhdxFlush(void *pBackendData, PVDIOCTX pIoCtx)
1989{
1990 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p\n", pBackendData, pIoCtx));
1991 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1992 int rc;
1993
1994 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1995 rc = VERR_VD_IMAGE_READ_ONLY;
1996 else
1997 rc = VERR_NOT_SUPPORTED;
1998
1999 LogFlowFunc(("returns %Rrc\n", rc));
2000 return rc;
2001}
2002
2003/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
2004static unsigned vhdxGetVersion(void *pBackendData)
2005{
2006 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2007 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2008
2009 AssertPtr(pImage);
2010
2011 if (pImage)
2012 return pImage->uVersion;
2013 else
2014 return 0;
2015}
2016
2017/** @copydoc VBOXHDDBACKEND::pfnGetSectorSize */
2018static uint32_t vhdxGetSectorSize(void *pBackendData)
2019{
2020 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2021 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2022 uint32_t cb = 0;
2023
2024 AssertPtr(pImage);
2025
2026 if (pImage && pImage->pStorage)
2027 cb = pImage->cbLogicalSector;
2028
2029 LogFlowFunc(("returns %u\n", cb));
2030 return cb;
2031}
2032
2033/** @copydoc VBOXHDDBACKEND::pfnGetSize */
2034static uint64_t vhdxGetSize(void *pBackendData)
2035{
2036 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2037 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2038 uint64_t cb = 0;
2039
2040 AssertPtr(pImage);
2041
2042 if (pImage && pImage->pStorage)
2043 cb = pImage->cbSize;
2044
2045 LogFlowFunc(("returns %llu\n", cb));
2046 return cb;
2047}
2048
2049/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
2050static uint64_t vhdxGetFileSize(void *pBackendData)
2051{
2052 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2053 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2054 uint64_t cb = 0;
2055
2056 AssertPtr(pImage);
2057
2058 if (pImage)
2059 {
2060 uint64_t cbFile;
2061 if (pImage->pStorage)
2062 {
2063 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2064 if (RT_SUCCESS(rc))
2065 cb = cbFile;
2066 }
2067 }
2068
2069 LogFlowFunc(("returns %lld\n", cb));
2070 return cb;
2071}
2072
2073/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
2074static int vhdxGetPCHSGeometry(void *pBackendData,
2075 PVDGEOMETRY pPCHSGeometry)
2076{
2077 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
2078 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2079 int rc;
2080
2081 AssertPtr(pImage);
2082
2083 if (pImage)
2084 {
2085 if (pImage->PCHSGeometry.cCylinders)
2086 {
2087 *pPCHSGeometry = pImage->PCHSGeometry;
2088 rc = VINF_SUCCESS;
2089 }
2090 else
2091 rc = VERR_VD_GEOMETRY_NOT_SET;
2092 }
2093 else
2094 rc = VERR_VD_NOT_OPENED;
2095
2096 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2097 return rc;
2098}
2099
2100/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
2101static int vhdxSetPCHSGeometry(void *pBackendData,
2102 PCVDGEOMETRY pPCHSGeometry)
2103{
2104 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2105 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2106 int rc = VINF_SUCCESS;
2107
2108 AssertPtr(pImage);
2109
2110 if (pImage)
2111 {
2112 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2113 rc = VERR_VD_IMAGE_READ_ONLY;
2114 else
2115 pImage->PCHSGeometry = *pPCHSGeometry;
2116 }
2117 else
2118 rc = VERR_VD_NOT_OPENED;
2119
2120 LogFlowFunc(("returns %Rrc\n", rc));
2121 return rc;
2122}
2123
2124/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
2125static int vhdxGetLCHSGeometry(void *pBackendData,
2126 PVDGEOMETRY pLCHSGeometry)
2127{
2128 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2129 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2130 int rc = VINF_SUCCESS;
2131
2132 AssertPtr(pImage);
2133
2134 if (pImage)
2135 {
2136 if (pImage->LCHSGeometry.cCylinders)
2137 *pLCHSGeometry = pImage->LCHSGeometry;
2138 else
2139 rc = VERR_VD_GEOMETRY_NOT_SET;
2140 }
2141 else
2142 rc = VERR_VD_NOT_OPENED;
2143
2144 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2145 return rc;
2146}
2147
2148/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
2149static int vhdxSetLCHSGeometry(void *pBackendData,
2150 PCVDGEOMETRY pLCHSGeometry)
2151{
2152 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2153 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2154 int rc = VINF_SUCCESS;
2155
2156 AssertPtr(pImage);
2157
2158 if (pImage)
2159 {
2160 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2161 rc = VERR_VD_IMAGE_READ_ONLY;
2162 else
2163 pImage->LCHSGeometry = *pLCHSGeometry;
2164 }
2165 else
2166 rc = VERR_VD_NOT_OPENED;
2167
2168 LogFlowFunc(("returns %Rrc\n", rc));
2169 return rc;
2170}
2171
2172/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
2173static unsigned vhdxGetImageFlags(void *pBackendData)
2174{
2175 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2176 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2177 unsigned uImageFlags;
2178
2179 AssertPtr(pImage);
2180
2181 if (pImage)
2182 uImageFlags = pImage->uImageFlags;
2183 else
2184 uImageFlags = 0;
2185
2186 LogFlowFunc(("returns %#x\n", uImageFlags));
2187 return uImageFlags;
2188}
2189
2190/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
2191static unsigned vhdxGetOpenFlags(void *pBackendData)
2192{
2193 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2194 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2195 unsigned uOpenFlags;
2196
2197 AssertPtr(pImage);
2198
2199 if (pImage)
2200 uOpenFlags = pImage->uOpenFlags;
2201 else
2202 uOpenFlags = 0;
2203
2204 LogFlowFunc(("returns %#x\n", uOpenFlags));
2205 return uOpenFlags;
2206}
2207
2208/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
2209static int vhdxSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2210{
2211 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2212 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2213 int rc = VINF_SUCCESS;
2214
2215 /* Image must be opened and the new flags must be valid. */
2216 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2217 rc = VERR_INVALID_PARAMETER;
2218 else
2219 {
2220 /* Implement this operation via reopening the image. */
2221 rc = vhdxFreeImage(pImage, false);
2222 if (RT_SUCCESS(rc))
2223 rc = vhdxOpenImage(pImage, uOpenFlags);
2224 }
2225
2226 LogFlowFunc(("returns %Rrc\n", rc));
2227 return rc;
2228}
2229
2230/** @copydoc VBOXHDDBACKEND::pfnGetComment */
2231static int vhdxGetComment(void *pBackendData, char *pszComment,
2232 size_t cbComment)
2233{
2234 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2235 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2236 int rc;
2237
2238 AssertPtr(pImage);
2239
2240 if (pImage)
2241 rc = VERR_NOT_SUPPORTED;
2242 else
2243 rc = VERR_VD_NOT_OPENED;
2244
2245 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2246 return rc;
2247}
2248
2249/** @copydoc VBOXHDDBACKEND::pfnSetComment */
2250static int vhdxSetComment(void *pBackendData, const char *pszComment)
2251{
2252 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2253 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2254 int rc;
2255
2256 AssertPtr(pImage);
2257
2258 if (pImage)
2259 {
2260 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2261 rc = VERR_VD_IMAGE_READ_ONLY;
2262 else
2263 rc = VERR_NOT_SUPPORTED;
2264 }
2265 else
2266 rc = VERR_VD_NOT_OPENED;
2267
2268 LogFlowFunc(("returns %Rrc\n", rc));
2269 return rc;
2270}
2271
2272/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
2273static int vhdxGetUuid(void *pBackendData, PRTUUID pUuid)
2274{
2275 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2276 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2277 int rc;
2278
2279 AssertPtr(pImage);
2280
2281 if (pImage)
2282 rc = VERR_NOT_SUPPORTED;
2283 else
2284 rc = VERR_VD_NOT_OPENED;
2285
2286 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2287 return rc;
2288}
2289
2290/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
2291static int vhdxSetUuid(void *pBackendData, PCRTUUID pUuid)
2292{
2293 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2294 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2295 int rc;
2296
2297 LogFlowFunc(("%RTuuid\n", pUuid));
2298 AssertPtr(pImage);
2299
2300 if (pImage)
2301 {
2302 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2303 rc = VERR_NOT_SUPPORTED;
2304 else
2305 rc = VERR_VD_IMAGE_READ_ONLY;
2306 }
2307 else
2308 rc = VERR_VD_NOT_OPENED;
2309
2310 LogFlowFunc(("returns %Rrc\n", rc));
2311 return rc;
2312}
2313
2314/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
2315static int vhdxGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2316{
2317 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2318 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2319 int rc;
2320
2321 AssertPtr(pImage);
2322
2323 if (pImage)
2324 rc = VERR_NOT_SUPPORTED;
2325 else
2326 rc = VERR_VD_NOT_OPENED;
2327
2328 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2329 return rc;
2330}
2331
2332/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
2333static int vhdxSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2334{
2335 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2336 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2337 int rc;
2338
2339 AssertPtr(pImage);
2340
2341 if (pImage)
2342 {
2343 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2344 rc = VERR_NOT_SUPPORTED;
2345 else
2346 rc = VERR_VD_IMAGE_READ_ONLY;
2347 }
2348 else
2349 rc = VERR_VD_NOT_OPENED;
2350
2351 LogFlowFunc(("returns %Rrc\n", rc));
2352 return rc;
2353}
2354
2355/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
2356static int vhdxGetParentUuid(void *pBackendData, PRTUUID pUuid)
2357{
2358 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2359 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2360 int rc;
2361
2362 AssertPtr(pImage);
2363
2364 if (pImage)
2365 rc = VERR_NOT_SUPPORTED;
2366 else
2367 rc = VERR_VD_NOT_OPENED;
2368
2369 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2370 return rc;
2371}
2372
2373/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
2374static int vhdxSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2375{
2376 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2377 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2378 int rc;
2379
2380 AssertPtr(pImage);
2381
2382 if (pImage)
2383 {
2384 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2385 rc = VERR_NOT_SUPPORTED;
2386 else
2387 rc = VERR_VD_IMAGE_READ_ONLY;
2388 }
2389 else
2390 rc = VERR_VD_NOT_OPENED;
2391
2392 LogFlowFunc(("returns %Rrc\n", rc));
2393 return rc;
2394}
2395
2396/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
2397static int vhdxGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2398{
2399 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2400 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2401 int rc;
2402
2403 AssertPtr(pImage);
2404
2405 if (pImage)
2406 rc = VERR_NOT_SUPPORTED;
2407 else
2408 rc = VERR_VD_NOT_OPENED;
2409
2410 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2411 return rc;
2412}
2413
2414/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
2415static int vhdxSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2416{
2417 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2418 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2419 int rc;
2420
2421 AssertPtr(pImage);
2422
2423 if (pImage)
2424 {
2425 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2426 rc = VERR_NOT_SUPPORTED;
2427 else
2428 rc = VERR_VD_IMAGE_READ_ONLY;
2429 }
2430 else
2431 rc = VERR_VD_NOT_OPENED;
2432
2433 LogFlowFunc(("returns %Rrc\n", rc));
2434 return rc;
2435}
2436
2437/** @copydoc VBOXHDDBACKEND::pfnDump */
2438static void vhdxDump(void *pBackendData)
2439{
2440 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2441
2442 AssertPtr(pImage);
2443 if (pImage)
2444 {
2445 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
2446 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2447 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2448 pImage->cbLogicalSector);
2449 }
2450}
2451
2452
2453const VBOXHDDBACKEND g_VhdxBackend =
2454{
2455 /* pszBackendName */
2456 "VHDX",
2457 /* cbSize */
2458 sizeof(VBOXHDDBACKEND),
2459 /* uBackendCaps */
2460 VD_CAP_FILE | VD_CAP_VFS,
2461 /* paFileExtensions */
2462 s_aVhdxFileExtensions,
2463 /* paConfigInfo */
2464 NULL,
2465 /* pfnCheckIfValid */
2466 vhdxCheckIfValid,
2467 /* pfnOpen */
2468 vhdxOpen,
2469 /* pfnCreate */
2470 vhdxCreate,
2471 /* pfnRename */
2472 vhdxRename,
2473 /* pfnClose */
2474 vhdxClose,
2475 /* pfnRead */
2476 vhdxRead,
2477 /* pfnWrite */
2478 vhdxWrite,
2479 /* pfnFlush */
2480 vhdxFlush,
2481 /* pfnDiscard */
2482 NULL,
2483 /* pfnGetVersion */
2484 vhdxGetVersion,
2485 /* pfnGetSectorSize */
2486 vhdxGetSectorSize,
2487 /* pfnGetSize */
2488 vhdxGetSize,
2489 /* pfnGetFileSize */
2490 vhdxGetFileSize,
2491 /* pfnGetPCHSGeometry */
2492 vhdxGetPCHSGeometry,
2493 /* pfnSetPCHSGeometry */
2494 vhdxSetPCHSGeometry,
2495 /* pfnGetLCHSGeometry */
2496 vhdxGetLCHSGeometry,
2497 /* pfnSetLCHSGeometry */
2498 vhdxSetLCHSGeometry,
2499 /* pfnGetImageFlags */
2500 vhdxGetImageFlags,
2501 /* pfnGetOpenFlags */
2502 vhdxGetOpenFlags,
2503 /* pfnSetOpenFlags */
2504 vhdxSetOpenFlags,
2505 /* pfnGetComment */
2506 vhdxGetComment,
2507 /* pfnSetComment */
2508 vhdxSetComment,
2509 /* pfnGetUuid */
2510 vhdxGetUuid,
2511 /* pfnSetUuid */
2512 vhdxSetUuid,
2513 /* pfnGetModificationUuid */
2514 vhdxGetModificationUuid,
2515 /* pfnSetModificationUuid */
2516 vhdxSetModificationUuid,
2517 /* pfnGetParentUuid */
2518 vhdxGetParentUuid,
2519 /* pfnSetParentUuid */
2520 vhdxSetParentUuid,
2521 /* pfnGetParentModificationUuid */
2522 vhdxGetParentModificationUuid,
2523 /* pfnSetParentModificationUuid */
2524 vhdxSetParentModificationUuid,
2525 /* pfnDump */
2526 vhdxDump,
2527 /* pfnGetTimeStamp */
2528 NULL,
2529 /* pfnGetParentTimeStamp */
2530 NULL,
2531 /* pfnSetParentTimeStamp */
2532 NULL,
2533 /* pfnGetParentFilename */
2534 NULL,
2535 /* pfnSetParentFilename */
2536 NULL,
2537 /* pfnComposeLocation */
2538 genericFileComposeLocation,
2539 /* pfnComposeName */
2540 genericFileComposeName,
2541 /* pfnCompact */
2542 NULL,
2543 /* pfnResize */
2544 NULL,
2545 /* pfnRepair */
2546 NULL,
2547 /* pfnTraverseMetadata */
2548 NULL
2549};
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