VirtualBox

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

Last change on this file since 66140 was 66110, checked in by vboxsync, 8 years ago

Storage/VD: Implement infrastructure for region lists to be able to support CD/DVD image formats which can contain multiple tracks in the future

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