VirtualBox

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

Last change on this file since 64226 was 63905, checked in by vboxsync, 8 years ago

Storage/VD: Add proper versioning of the backend structures instead of just relying on the structure size to make changing callback signatures possible in the future and still being able to reject incompatible plugins

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.8 KB
Line 
1/* $Id: VHDX.cpp 63905 2016-09-20 08:31:05Z 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 *
806 * @note It is safe to use the same pointer for paBatEntriesConv and paBatEntries.
807 */
808DECLINLINE(void) vhdxConvBatTableEndianess(VHDXECONV enmConv, PVhdxBatEntry paBatEntriesConv,
809 PVhdxBatEntry paBatEntries, uint32_t cBatEntries)
810{
811 for (uint32_t i = 0; i < cBatEntries; i++)
812 paBatEntriesConv[i].u64BatEntry = SET_ENDIAN_U64(paBatEntries[i].u64BatEntry);
813}
814
815/**
816 * Converts a VHDX metadata table header between file and host endianness.
817 *
818 * @returns nothing.
819 * @param enmConv Direction of the conversion.
820 * @param pMetadataTblHdrConv Where to store the converted metadata table header.
821 * @param pMetadataTblHdr The VHDX metadata table header to convert.
822 *
823 * @note It is safe to use the same pointer for pMetadataTblHdrConv and pMetadataTblHdr.
824 */
825DECLINLINE(void) vhdxConvMetadataTblHdrEndianess(VHDXECONV enmConv, PVhdxMetadataTblHdr pMetadataTblHdrConv,
826 PVhdxMetadataTblHdr pMetadataTblHdr)
827{
828 pMetadataTblHdrConv->u64Signature = SET_ENDIAN_U64(pMetadataTblHdr->u64Signature);
829 pMetadataTblHdrConv->u16Reserved = SET_ENDIAN_U16(pMetadataTblHdr->u16Reserved);
830 pMetadataTblHdrConv->u16EntryCount = SET_ENDIAN_U16(pMetadataTblHdr->u16EntryCount);
831 for (unsigned i = 0; i < RT_ELEMENTS(pMetadataTblHdr->u32Reserved2); i++)
832 pMetadataTblHdrConv->u32Reserved2[i] = SET_ENDIAN_U32(pMetadataTblHdr->u32Reserved2[i]);
833}
834
835/**
836 * Converts a VHDX metadata table entry between file and host endianness.
837 *
838 * @returns nothing.
839 * @param enmConv Direction of the conversion.
840 * @param pMetadataTblEntryConv Where to store the converted metadata table entry.
841 * @param pMetadataTblEntry The VHDX metadata table entry to convert.
842 *
843 * @note It is safe to use the same pointer for pMetadataTblEntryConv and pMetadataTblEntry.
844 */
845DECLINLINE(void) vhdxConvMetadataTblEntryEndianess(VHDXECONV enmConv, PVhdxMetadataTblEntry pMetadataTblEntryConv,
846 PVhdxMetadataTblEntry pMetadataTblEntry)
847{
848 vhdxConvUuidEndianess(enmConv, &pMetadataTblEntryConv->UuidItem, &pMetadataTblEntry->UuidItem);
849 pMetadataTblEntryConv->u32Offset = SET_ENDIAN_U32(pMetadataTblEntry->u32Offset);
850 pMetadataTblEntryConv->u32Length = SET_ENDIAN_U32(pMetadataTblEntry->u32Length);
851 pMetadataTblEntryConv->u32Flags = SET_ENDIAN_U32(pMetadataTblEntry->u32Flags);
852 pMetadataTblEntryConv->u32Reserved = SET_ENDIAN_U32(pMetadataTblEntry->u32Reserved);
853}
854
855/**
856 * Converts a VHDX file parameters item between file and host endianness.
857 *
858 * @returns nothing.
859 * @param enmConv Direction of the conversion.
860 * @param pFileParamsConv Where to store the converted file parameters item entry.
861 * @param pFileParams The VHDX file parameters item to convert.
862 *
863 * @note It is safe to use the same pointer for pFileParamsConv and pFileParams.
864 */
865DECLINLINE(void) vhdxConvFileParamsEndianess(VHDXECONV enmConv, PVhdxFileParameters pFileParamsConv,
866 PVhdxFileParameters pFileParams)
867{
868 pFileParamsConv->u32BlockSize = SET_ENDIAN_U32(pFileParams->u32BlockSize);
869 pFileParamsConv->u32Flags = SET_ENDIAN_U32(pFileParams->u32Flags);
870}
871
872/**
873 * Converts a VHDX virtual disk size item between file and host endianness.
874 *
875 * @returns nothing.
876 * @param enmConv Direction of the conversion.
877 * @param pVDiskSizeConv Where to store the converted virtual disk size item entry.
878 * @param pVDiskSize The VHDX virtual disk size item to convert.
879 *
880 * @note It is safe to use the same pointer for pVDiskSizeConv and pVDiskSize.
881 */
882DECLINLINE(void) vhdxConvVDiskSizeEndianess(VHDXECONV enmConv, PVhdxVDiskSize pVDiskSizeConv,
883 PVhdxVDiskSize pVDiskSize)
884{
885 pVDiskSizeConv->u64VDiskSize = SET_ENDIAN_U64(pVDiskSize->u64VDiskSize);
886}
887
888#if 0 /* unused */
889
890/**
891 * Converts a VHDX page 83 data item between file and host endianness.
892 *
893 * @returns nothing.
894 * @param enmConv Direction of the conversion.
895 * @param pPage83DataConv Where to store the converted page 83 data item entry.
896 * @param pPage83Data The VHDX page 83 data item to convert.
897 *
898 * @note It is safe to use the same pointer for pPage83DataConv and pPage83Data.
899 */
900DECLINLINE(void) vhdxConvPage83DataEndianess(VHDXECONV enmConv, PVhdxPage83Data pPage83DataConv,
901 PVhdxPage83Data pPage83Data)
902{
903 vhdxConvUuidEndianess(enmConv, &pPage83DataConv->UuidPage83Data, &pPage83Data->UuidPage83Data);
904}
905#endif /* unused */
906
907/**
908 * Converts a VHDX logical sector size item between file and host endianness.
909 *
910 * @returns nothing.
911 * @param enmConv Direction of the conversion.
912 * @param pVDiskLogSectSizeConv Where to store the converted logical sector size item entry.
913 * @param pVDiskLogSectSize The VHDX logical sector size item to convert.
914 *
915 * @note It is safe to use the same pointer for pVDiskLogSectSizeConv and pVDiskLogSectSize.
916 */
917DECLINLINE(void) vhdxConvVDiskLogSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskLogicalSectorSize pVDiskLogSectSizeConv,
918 PVhdxVDiskLogicalSectorSize pVDiskLogSectSize)
919{
920 pVDiskLogSectSizeConv->u32LogicalSectorSize = SET_ENDIAN_U32(pVDiskLogSectSize->u32LogicalSectorSize);
921}
922
923#if 0 /* unused */
924
925/**
926 * Converts a VHDX physical sector size item between file and host endianness.
927 *
928 * @returns nothing.
929 * @param enmConv Direction of the conversion.
930 * @param pVDiskPhysSectSizeConv Where to store the converted physical sector size item entry.
931 * @param pVDiskPhysSectSize The VHDX physical sector size item to convert.
932 *
933 * @note It is safe to use the same pointer for pVDiskPhysSectSizeConv and pVDiskPhysSectSize.
934 */
935DECLINLINE(void) vhdxConvVDiskPhysSectSizeEndianess(VHDXECONV enmConv, PVhdxVDiskPhysicalSectorSize pVDiskPhysSectSizeConv,
936 PVhdxVDiskPhysicalSectorSize pVDiskPhysSectSize)
937{
938 pVDiskPhysSectSizeConv->u64PhysicalSectorSize = SET_ENDIAN_U64(pVDiskPhysSectSize->u64PhysicalSectorSize);
939}
940
941
942/**
943 * Converts a VHDX parent locator header item between file and host endianness.
944 *
945 * @returns nothing.
946 * @param enmConv Direction of the conversion.
947 * @param pParentLocatorHdrConv Where to store the converted parent locator header item entry.
948 * @param pParentLocatorHdr The VHDX parent locator header item to convert.
949 *
950 * @note It is safe to use the same pointer for pParentLocatorHdrConv and pParentLocatorHdr.
951 */
952DECLINLINE(void) vhdxConvParentLocatorHeaderEndianness(VHDXECONV enmConv, PVhdxParentLocatorHeader pParentLocatorHdrConv,
953 PVhdxParentLocatorHeader pParentLocatorHdr)
954{
955 vhdxConvUuidEndianess(enmConv, &pParentLocatorHdrConv->UuidLocatorType, &pParentLocatorHdr->UuidLocatorType);
956 pParentLocatorHdrConv->u16Reserved = SET_ENDIAN_U16(pParentLocatorHdr->u16Reserved);
957 pParentLocatorHdrConv->u16KeyValueCount = SET_ENDIAN_U16(pParentLocatorHdr->u16KeyValueCount);
958}
959
960
961/**
962 * Converts a VHDX parent locator entry between file and host endianness.
963 *
964 * @returns nothing.
965 * @param enmConv Direction of the conversion.
966 * @param pParentLocatorEntryConv Where to store the converted parent locator entry.
967 * @param pParentLocatorEntry The VHDX parent locator entry to convert.
968 *
969 * @note It is safe to use the same pointer for pParentLocatorEntryConv and pParentLocatorEntry.
970 */
971DECLINLINE(void) vhdxConvParentLocatorEntryEndianess(VHDXECONV enmConv, PVhdxParentLocatorEntry pParentLocatorEntryConv,
972 PVhdxParentLocatorEntry pParentLocatorEntry)
973{
974 pParentLocatorEntryConv->u32KeyOffset = SET_ENDIAN_U32(pParentLocatorEntry->u32KeyOffset);
975 pParentLocatorEntryConv->u32ValueOffset = SET_ENDIAN_U32(pParentLocatorEntry->u32ValueOffset);
976 pParentLocatorEntryConv->u16KeyLength = SET_ENDIAN_U16(pParentLocatorEntry->u16KeyLength);
977 pParentLocatorEntryConv->u16ValueLength = SET_ENDIAN_U16(pParentLocatorEntry->u16ValueLength);
978}
979
980#endif /* unused */
981
982/**
983 * Internal. Free all allocated space for representing an image except pImage,
984 * and optionally delete the image from disk.
985 */
986static int vhdxFreeImage(PVHDXIMAGE pImage, bool fDelete)
987{
988 int rc = VINF_SUCCESS;
989
990 /* Freeing a never allocated image (e.g. because the open failed) is
991 * not signalled as an error. After all nothing bad happens. */
992 if (pImage)
993 {
994 if (pImage->pStorage)
995 {
996 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
997 pImage->pStorage = NULL;
998 }
999
1000 if (pImage->paBat)
1001 {
1002 RTMemFree(pImage->paBat);
1003 pImage->paBat = NULL;
1004 }
1005
1006 if (fDelete && pImage->pszFilename)
1007 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
1008 }
1009
1010 LogFlowFunc(("returns %Rrc\n", rc));
1011 return rc;
1012}
1013
1014/**
1015 * Loads all required fields from the given VHDX header.
1016 * The header must be converted to the host endianess and validated already.
1017 *
1018 * @returns VBox status code.
1019 * @param pImage Image instance data.
1020 * @param pHdr The header to load.
1021 */
1022static int vhdxLoadHeader(PVHDXIMAGE pImage, PVhdxHeader pHdr)
1023{
1024 int rc = VINF_SUCCESS;
1025
1026 LogFlowFunc(("pImage=%#p pHdr=%#p\n", pImage, pHdr));
1027
1028 /*
1029 * Most fields in the header are not required because the backend implements
1030 * readonly access only so far.
1031 * We just have to check that the log is empty, we have to refuse to load the
1032 * image otherwsie because replaying the log is not implemented.
1033 */
1034 if (pHdr->u16Version == VHDX_HEADER_VHDX_VERSION)
1035 {
1036 /* Check that the log UUID is zero. */
1037 pImage->uVersion = pHdr->u16Version;
1038 if (!RTUuidIsNull(&pHdr->UuidLog))
1039 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1040 "VHDX: Image \'%s\' has a non empty log which is not supported",
1041 pImage->pszFilename);
1042 }
1043 else
1044 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1045 "VHDX: Image \'%s\' uses an unsupported version (%u) of the VHDX format",
1046 pImage->pszFilename, pHdr->u16Version);
1047
1048 LogFlowFunc(("return rc=%Rrc\n", rc));
1049 return rc;
1050}
1051
1052/**
1053 * Determines the current header and loads it.
1054 *
1055 * @returns VBox status code.
1056 * @param pImage Image instance data.
1057 */
1058static int vhdxFindAndLoadCurrentHeader(PVHDXIMAGE pImage)
1059{
1060 PVhdxHeader pHdr1, pHdr2;
1061 uint32_t u32ChkSum = 0;
1062 uint32_t u32ChkSumSaved = 0;
1063 bool fHdr1Valid = false;
1064 bool fHdr2Valid = false;
1065 int rc = VINF_SUCCESS;
1066
1067 LogFlowFunc(("pImage=%#p\n", pImage));
1068
1069 /*
1070 * The VHDX format defines two headers at different offsets to provide failure
1071 * consistency. Only one header is current. This can be determined using the
1072 * sequence number and checksum fields in the header.
1073 */
1074 pHdr1 = (PVhdxHeader)RTMemAllocZ(sizeof(VhdxHeader));
1075 pHdr2 = (PVhdxHeader)RTMemAllocZ(sizeof(VhdxHeader));
1076
1077 if (pHdr1 && pHdr2)
1078 {
1079 /* Read the first header. */
1080 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER1_OFFSET,
1081 pHdr1, sizeof(*pHdr1));
1082 if (RT_SUCCESS(rc))
1083 {
1084 vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr1, pHdr1);
1085
1086 /* Validate checksum. */
1087 u32ChkSumSaved = pHdr1->u32Checksum;
1088 pHdr1->u32Checksum = 0;
1089 u32ChkSum = RTCrc32C(pHdr1, sizeof(VhdxHeader));
1090
1091 if ( pHdr1->u32Signature == VHDX_HEADER_SIGNATURE
1092 && u32ChkSum == u32ChkSumSaved)
1093 fHdr1Valid = true;
1094 }
1095
1096 /* Try to read the second header in any case (even if reading the first failed). */
1097 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_HEADER2_OFFSET,
1098 pHdr2, sizeof(*pHdr2));
1099 if (RT_SUCCESS(rc))
1100 {
1101 vhdxConvHeaderEndianess(VHDXECONV_F2H, pHdr2, pHdr2);
1102
1103 /* Validate checksum. */
1104 u32ChkSumSaved = pHdr2->u32Checksum;
1105 pHdr2->u32Checksum = 0;
1106 u32ChkSum = RTCrc32C(pHdr2, sizeof(VhdxHeader));
1107
1108 if ( pHdr2->u32Signature == VHDX_HEADER_SIGNATURE
1109 && u32ChkSum == u32ChkSumSaved)
1110 fHdr2Valid = true;
1111 }
1112
1113 /* Determine the current header. */
1114 if (fHdr1Valid != fHdr2Valid)
1115 {
1116 /* Only one header is valid - use it. */
1117 rc = vhdxLoadHeader(pImage, fHdr1Valid ? pHdr1 : pHdr2);
1118 }
1119 else if (!fHdr1Valid && !fHdr2Valid)
1120 {
1121 /* Crap, both headers are corrupt, refuse to load the image. */
1122 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1123 "VHDX: Can not load the image because both headers are corrupt");
1124 }
1125 else
1126 {
1127 /* Both headers are valid. Use the sequence number to find the current one. */
1128 if (pHdr1->u64SequenceNumber > pHdr2->u64SequenceNumber)
1129 rc = vhdxLoadHeader(pImage, pHdr1);
1130 else
1131 rc = vhdxLoadHeader(pImage, pHdr2);
1132 }
1133 }
1134 else
1135 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1136 "VHDX: Out of memory while allocating memory for the header");
1137
1138 if (pHdr1)
1139 RTMemFree(pHdr1);
1140 if (pHdr2)
1141 RTMemFree(pHdr2);
1142
1143 LogFlowFunc(("returns rc=%Rrc\n", rc));
1144 return rc;
1145}
1146
1147/**
1148 * Loads the BAT region.
1149 *
1150 * @returns VBox status code.
1151 * @param pImage Image instance data.
1152 * @param offRegion Start offset of the region.
1153 * @param cbRegion Size of the region.
1154 */
1155static int vhdxLoadBatRegion(PVHDXIMAGE pImage, uint64_t offRegion,
1156 size_t cbRegion)
1157{
1158 int rc = VINF_SUCCESS;
1159 uint32_t cDataBlocks;
1160 uint32_t uChunkRatio;
1161 uint32_t cSectorBitmapBlocks;
1162 uint32_t cBatEntries;
1163 uint32_t cbBatEntries;
1164 PVhdxBatEntry paBatEntries = NULL;
1165
1166 LogFlowFunc(("pImage=%#p\n", pImage));
1167
1168 /* Calculate required values first. */
1169 uint64_t uChunkRatio64 = (RT_BIT_64(23) * pImage->cbLogicalSector) / pImage->cbBlock;
1170 uChunkRatio = (uint32_t)uChunkRatio64; Assert(uChunkRatio == uChunkRatio64);
1171 uint64_t cDataBlocks64 = pImage->cbSize / pImage->cbBlock;
1172 cDataBlocks = (uint32_t)cDataBlocks64; Assert(cDataBlocks == cDataBlocks64);
1173
1174 if (pImage->cbSize % pImage->cbBlock)
1175 cDataBlocks++;
1176
1177 cSectorBitmapBlocks = cDataBlocks / uChunkRatio;
1178 if (cDataBlocks % uChunkRatio)
1179 cSectorBitmapBlocks++;
1180
1181 cBatEntries = cDataBlocks + (cDataBlocks - 1)/uChunkRatio;
1182 cbBatEntries = cBatEntries * sizeof(VhdxBatEntry);
1183
1184 if (cbBatEntries <= cbRegion)
1185 {
1186 /*
1187 * Load the complete BAT region first, convert to host endianess and process
1188 * it afterwards. The SB entries can be removed because they are not needed yet.
1189 */
1190 paBatEntries = (PVhdxBatEntry)RTMemAlloc(cbBatEntries);
1191 if (paBatEntries)
1192 {
1193 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
1194 paBatEntries, cbBatEntries);
1195 if (RT_SUCCESS(rc))
1196 {
1197 vhdxConvBatTableEndianess(VHDXECONV_F2H, paBatEntries, paBatEntries,
1198 cBatEntries);
1199
1200 /* Go through the table and validate it. */
1201 for (unsigned i = 0; i < cBatEntries; i++)
1202 {
1203 if ( i != 0
1204 && (i % uChunkRatio) == 0)
1205 {
1206/**
1207 * Disabled the verification because there are images out there with the sector bitmap
1208 * marked as present. The entry is never accessed and the image is readonly anyway,
1209 * so no harm done.
1210 */
1211#if 0
1212 /* Sector bitmap block. */
1213 if ( VHDX_BAT_ENTRY_GET_STATE(paBatEntries[i].u64BatEntry)
1214 != VHDX_BAT_ENTRY_SB_BLOCK_NOT_PRESENT)
1215 {
1216 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1217 "VHDX: Sector bitmap block at entry %u of image \'%s\' marked as present, violation of the specification",
1218 i, pImage->pszFilename);
1219 break;
1220 }
1221#endif
1222 }
1223 else
1224 {
1225 /* Payload block. */
1226 if ( VHDX_BAT_ENTRY_GET_STATE(paBatEntries[i].u64BatEntry)
1227 == VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1228 {
1229 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1230 "VHDX: Payload block at entry %u of image \'%s\' marked as partially present, violation of the specification",
1231 i, pImage->pszFilename);
1232 break;
1233 }
1234 }
1235 }
1236
1237 if (RT_SUCCESS(rc))
1238 {
1239 pImage->paBat = paBatEntries;
1240 pImage->uChunkRatio = uChunkRatio;
1241 }
1242 }
1243 else
1244 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1245 "VHDX: Error reading the BAT from image \'%s\'",
1246 pImage->pszFilename);
1247 }
1248 else
1249 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1250 "VHDX: Out of memory allocating memory for %u BAT entries of image \'%s\'",
1251 cBatEntries, pImage->pszFilename);
1252 }
1253 else
1254 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1255 "VHDX: Mismatch between calculated number of BAT entries and region size (expected %u got %u) for image \'%s\'",
1256 cbBatEntries, cbRegion, pImage->pszFilename);
1257
1258 if ( RT_FAILURE(rc)
1259 && paBatEntries)
1260 RTMemFree(paBatEntries);
1261
1262 LogFlowFunc(("returns rc=%Rrc\n", rc));
1263 return rc;
1264}
1265
1266/**
1267 * Load the file parameters metadata item from the file.
1268 *
1269 * @returns VBox status code.
1270 * @param pImage Image instance data.
1271 * @param offItem File offset where the data is stored.
1272 * @param cbItem Size of the item in the file.
1273 */
1274static int vhdxLoadFileParametersMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1275{
1276 int rc = VINF_SUCCESS;
1277
1278 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1279
1280 if (cbItem != sizeof(VhdxFileParameters))
1281 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1282 "VHDX: File parameters item size mismatch (expected %u got %zu) in image \'%s\'",
1283 sizeof(VhdxFileParameters), cbItem, pImage->pszFilename);
1284 else
1285 {
1286 VhdxFileParameters FileParameters;
1287
1288 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1289 &FileParameters, sizeof(FileParameters));
1290 if (RT_SUCCESS(rc))
1291 {
1292 vhdxConvFileParamsEndianess(VHDXECONV_F2H, &FileParameters, &FileParameters);
1293 pImage->cbBlock = FileParameters.u32BlockSize;
1294
1295 /** @todo No support for differencing images yet. */
1296 if (FileParameters.u32Flags & VHDX_FILE_PARAMETERS_FLAGS_HAS_PARENT)
1297 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1298 "VHDX: Image \'%s\' is a differencing image which is not supported yet",
1299 pImage->pszFilename);
1300 }
1301 else
1302 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1303 "VHDX: Reading the file parameters metadata item from image \'%s\' failed",
1304 pImage->pszFilename);
1305 }
1306
1307 LogFlowFunc(("returns rc=%Rrc\n", rc));
1308 return rc;
1309}
1310
1311/**
1312 * Load the virtual disk size metadata item from the file.
1313 *
1314 * @returns VBox status code.
1315 * @param pImage Image instance data.
1316 * @param offItem File offset where the data is stored.
1317 * @param cbItem Size of the item in the file.
1318 */
1319static int vhdxLoadVDiskSizeMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1320{
1321 int rc = VINF_SUCCESS;
1322
1323 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1324
1325 if (cbItem != sizeof(VhdxVDiskSize))
1326 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1327 "VHDX: Virtual disk size item size mismatch (expected %u got %zu) in image \'%s\'",
1328 sizeof(VhdxVDiskSize), cbItem, pImage->pszFilename);
1329 else
1330 {
1331 VhdxVDiskSize VDiskSize;
1332
1333 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1334 &VDiskSize, sizeof(VDiskSize));
1335 if (RT_SUCCESS(rc))
1336 {
1337 vhdxConvVDiskSizeEndianess(VHDXECONV_F2H, &VDiskSize, &VDiskSize);
1338 pImage->cbSize = VDiskSize.u64VDiskSize;
1339 }
1340 else
1341 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1342 "VHDX: Reading the virtual disk size metadata item from image \'%s\' failed",
1343 pImage->pszFilename);
1344 }
1345
1346 LogFlowFunc(("returns rc=%Rrc\n", rc));
1347 return rc;
1348}
1349
1350/**
1351 * Load the logical sector size metadata item from the file.
1352 *
1353 * @returns VBox status code.
1354 * @param pImage Image instance data.
1355 * @param offItem File offset where the data is stored.
1356 * @param cbItem Size of the item in the file.
1357 */
1358static int vhdxLoadVDiskLogSectorSizeMetadata(PVHDXIMAGE pImage, uint64_t offItem, size_t cbItem)
1359{
1360 int rc = VINF_SUCCESS;
1361
1362 LogFlowFunc(("pImage=%#p offItem=%llu cbItem=%zu\n", pImage, offItem, cbItem));
1363
1364 if (cbItem != sizeof(VhdxVDiskLogicalSectorSize))
1365 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1366 "VHDX: Virtual disk logical sector size item size mismatch (expected %u got %zu) in image \'%s\'",
1367 sizeof(VhdxVDiskLogicalSectorSize), cbItem, pImage->pszFilename);
1368 else
1369 {
1370 VhdxVDiskLogicalSectorSize VDiskLogSectSize;
1371
1372 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offItem,
1373 &VDiskLogSectSize, sizeof(VDiskLogSectSize));
1374 if (RT_SUCCESS(rc))
1375 {
1376 vhdxConvVDiskLogSectSizeEndianess(VHDXECONV_F2H, &VDiskLogSectSize,
1377 &VDiskLogSectSize);
1378 pImage->cbLogicalSector = VDiskLogSectSize.u32LogicalSectorSize;
1379 }
1380 else
1381 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1382 "VHDX: Reading the virtual disk logical sector size metadata item from image \'%s\' failed",
1383 pImage->pszFilename);
1384 }
1385
1386 LogFlowFunc(("returns rc=%Rrc\n", rc));
1387 return rc;
1388}
1389
1390/**
1391 * Loads the metadata region.
1392 *
1393 * @returns VBox status code.
1394 * @param pImage Image instance data.
1395 * @param offRegion Start offset of the region.
1396 * @param cbRegion Size of the region.
1397 */
1398static int vhdxLoadMetadataRegion(PVHDXIMAGE pImage, uint64_t offRegion,
1399 size_t cbRegion)
1400{
1401 VhdxMetadataTblHdr MetadataTblHdr;
1402 int rc = VINF_SUCCESS;
1403
1404 LogFlowFunc(("pImage=%#p\n", pImage));
1405
1406 /* Load the header first. */
1407 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offRegion,
1408 &MetadataTblHdr, sizeof(MetadataTblHdr));
1409 if (RT_SUCCESS(rc))
1410 {
1411 vhdxConvMetadataTblHdrEndianess(VHDXECONV_F2H, &MetadataTblHdr, &MetadataTblHdr);
1412
1413 /* Validate structure. */
1414 if (MetadataTblHdr.u64Signature != VHDX_METADATA_TBL_HDR_SIGNATURE)
1415 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1416 "VHDX: Incorrect metadata table header signature for image \'%s\'",
1417 pImage->pszFilename);
1418 else if (MetadataTblHdr.u16EntryCount > VHDX_METADATA_TBL_HDR_ENTRY_COUNT_MAX)
1419 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1420 "VHDX: Incorrect entry count in metadata table header of image \'%s\'",
1421 pImage->pszFilename);
1422 else if (cbRegion < (MetadataTblHdr.u16EntryCount * sizeof(VhdxMetadataTblEntry) + sizeof(VhdxMetadataTblHdr)))
1423 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1424 "VHDX: Metadata table of image \'%s\' exceeds region size",
1425 pImage->pszFilename);
1426
1427 if (RT_SUCCESS(rc))
1428 {
1429 uint64_t offMetadataTblEntry = offRegion + sizeof(VhdxMetadataTblHdr);
1430
1431 for (unsigned i = 0; i < MetadataTblHdr.u16EntryCount; i++)
1432 {
1433 uint64_t offMetadataItem = 0;
1434 VHDXMETADATAITEM enmMetadataItem = VHDXMETADATAITEM_UNKNOWN;
1435 VhdxMetadataTblEntry MetadataTblEntry;
1436
1437 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, offMetadataTblEntry,
1438 &MetadataTblEntry, sizeof(MetadataTblEntry));
1439 if (RT_FAILURE(rc))
1440 {
1441 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1442 "VHDX: Reading metadata table entry from image \'%s\' failed",
1443 pImage->pszFilename);
1444 break;
1445 }
1446
1447 vhdxConvMetadataTblEntryEndianess(VHDXECONV_F2H, &MetadataTblEntry, &MetadataTblEntry);
1448
1449 /* Check whether the flags match the expectations. */
1450 for (unsigned idxProp = 0; idxProp < RT_ELEMENTS(s_aVhdxMetadataItemProps); idxProp++)
1451 {
1452 if (!RTUuidCompareStr(&MetadataTblEntry.UuidItem,
1453 s_aVhdxMetadataItemProps[idxProp].pszItemUuid))
1454 {
1455 /*
1456 * Check for specification violations and bail out, except
1457 * for the required flag of the physical sector size metadata item.
1458 * Early images had the required flag not set opposed to the specification.
1459 * We don't want to brerak those images.
1460 */
1461 if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_USER)
1462 != s_aVhdxMetadataItemProps[idxProp].fIsUser)
1463 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1464 "VHDX: User flag of metadata item does not meet expectations \'%s\'",
1465 pImage->pszFilename);
1466 else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_VDISK)
1467 != s_aVhdxMetadataItemProps[idxProp].fIsVDisk)
1468 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1469 "VHDX: Virtual disk flag of metadata item does not meet expectations \'%s\'",
1470 pImage->pszFilename);
1471 else if ( !!(MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
1472 != s_aVhdxMetadataItemProps[idxProp].fIsRequired
1473 && (s_aVhdxMetadataItemProps[idxProp].enmMetadataItem != VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE))
1474 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1475 "VHDX: Required flag of metadata item does not meet expectations \'%s\'",
1476 pImage->pszFilename);
1477 else
1478 enmMetadataItem = s_aVhdxMetadataItemProps[idxProp].enmMetadataItem;
1479
1480 break;
1481 }
1482 }
1483
1484 if (RT_FAILURE(rc))
1485 break;
1486
1487 offMetadataItem = offRegion + MetadataTblEntry.u32Offset;
1488
1489 switch (enmMetadataItem)
1490 {
1491 case VHDXMETADATAITEM_FILE_PARAMS:
1492 {
1493 rc = vhdxLoadFileParametersMetadata(pImage, offMetadataItem,
1494 MetadataTblEntry.u32Length);
1495 break;
1496 }
1497 case VHDXMETADATAITEM_VDISK_SIZE:
1498 {
1499 rc = vhdxLoadVDiskSizeMetadata(pImage, offMetadataItem,
1500 MetadataTblEntry.u32Length);
1501 break;
1502 }
1503 case VHDXMETADATAITEM_PAGE83_DATA:
1504 {
1505 /*
1506 * Nothing to do here for now (marked as required but
1507 * there is no API to pass this information to the caller)
1508 * so far.
1509 */
1510 break;
1511 }
1512 case VHDXMETADATAITEM_LOGICAL_SECTOR_SIZE:
1513 {
1514 rc = vhdxLoadVDiskLogSectorSizeMetadata(pImage, offMetadataItem,
1515 MetadataTblEntry.u32Length);
1516 break;
1517 }
1518 case VHDXMETADATAITEM_PHYSICAL_SECTOR_SIZE:
1519 {
1520 /*
1521 * Nothing to do here for now (marked as required but
1522 * there is no API to pass this information to the caller)
1523 * so far.
1524 */
1525 break;
1526 }
1527 case VHDXMETADATAITEM_PARENT_LOCATOR:
1528 {
1529 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1530 "VHDX: Image \'%s\' is a differencing image which is not supported yet",
1531 pImage->pszFilename);
1532 break;
1533 }
1534 case VHDXMETADATAITEM_UNKNOWN:
1535 default:
1536 if (MetadataTblEntry.u32Flags & VHDX_METADATA_TBL_ENTRY_FLAGS_IS_REQUIRED)
1537 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1538 "VHDX: Unsupported but required metadata item in image \'%s\'",
1539 pImage->pszFilename);
1540 }
1541
1542 if (RT_FAILURE(rc))
1543 break;
1544
1545 offMetadataTblEntry += sizeof(MetadataTblEntry);
1546 }
1547 }
1548 }
1549 else
1550 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1551 "VHDX: Reading the metadata table header for image \'%s\' failed",
1552 pImage->pszFilename);
1553
1554 LogFlowFunc(("returns rc=%Rrc\n", rc));
1555 return rc;
1556}
1557
1558/**
1559 * Loads the region table and the associated regions.
1560 *
1561 * @returns VBox status code.
1562 * @param pImage Image instance data.
1563 */
1564static int vhdxLoadRegionTable(PVHDXIMAGE pImage)
1565{
1566 uint8_t *pbRegionTbl = NULL;
1567 int rc = VINF_SUCCESS;
1568
1569 LogFlowFunc(("pImage=%#p\n", pImage));
1570
1571 /* Load the complete region table into memory. */
1572 pbRegionTbl = (uint8_t *)RTMemTmpAlloc(VHDX_REGION_TBL_SIZE_MAX);
1573 if (pbRegionTbl)
1574 {
1575 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_REGION_TBL_HDR_OFFSET,
1576 pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX);
1577 if (RT_SUCCESS(rc))
1578 {
1579 PVhdxRegionTblHdr pRegionTblHdr;
1580 VhdxRegionTblHdr RegionTblHdr;
1581 uint32_t u32ChkSum = 0;
1582
1583 /*
1584 * Copy the region table header to a dedicated structure where we can
1585 * convert it to host endianess.
1586 */
1587 memcpy(&RegionTblHdr, pbRegionTbl, sizeof(RegionTblHdr));
1588 vhdxConvRegionTblHdrEndianess(VHDXECONV_F2H, &RegionTblHdr, &RegionTblHdr);
1589
1590 /* Set checksum field to 0 during crc computation. */
1591 pRegionTblHdr = (PVhdxRegionTblHdr)pbRegionTbl;
1592 pRegionTblHdr->u32Checksum = 0;
1593
1594 /* Verify the region table integrity. */
1595 u32ChkSum = RTCrc32C(pbRegionTbl, VHDX_REGION_TBL_SIZE_MAX);
1596
1597 if (RegionTblHdr.u32Signature != VHDX_REGION_TBL_HDR_SIGNATURE)
1598 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1599 "VHDX: Invalid signature for region table header of image \'%s\'",
1600 pImage->pszFilename);
1601 else if (u32ChkSum != RegionTblHdr.u32Checksum)
1602 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1603 "VHDX: CRC32 checksum mismatch for the region table of image \'%s\' (expected %#x got %#x)",
1604 pImage->pszFilename, RegionTblHdr.u32Checksum, u32ChkSum);
1605 else if (RegionTblHdr.u32EntryCount > VHDX_REGION_TBL_HDR_ENTRY_COUNT_MAX)
1606 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1607 "VHDX: Invalid entry count field in the region table header of image \'%s\'",
1608 pImage->pszFilename);
1609
1610 if (RT_SUCCESS(rc))
1611 {
1612 /* Parse the region table entries. */
1613 PVhdxRegionTblEntry pRegTblEntry = (PVhdxRegionTblEntry)(pbRegionTbl + sizeof(VhdxRegionTblHdr));
1614 VhdxRegionTblEntry RegTblEntryBat; /* BAT region table entry. */
1615 bool fBatRegPresent = false;
1616 RT_ZERO(RegTblEntryBat); /* Maybe uninitialized, gcc. */
1617
1618 for (unsigned i = 0; i < RegionTblHdr.u32EntryCount; i++)
1619 {
1620 vhdxConvRegionTblEntryEndianess(VHDXECONV_F2H, pRegTblEntry, pRegTblEntry);
1621
1622 /* Check the uuid for known regions. */
1623 if (!RTUuidCompareStr(&pRegTblEntry->UuidObject, VHDX_REGION_TBL_ENTRY_UUID_BAT))
1624 {
1625 /*
1626 * Save the BAT region and process it later.
1627 * It may come before the metadata region but needs the block size.
1628 */
1629 if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1630 {
1631 fBatRegPresent = true;
1632 RegTblEntryBat.u32Length = pRegTblEntry->u32Length;
1633 RegTblEntryBat.u64FileOffset = pRegTblEntry->u64FileOffset;
1634 }
1635 else
1636 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1637 "VHDX: BAT region not marked as required in image \'%s\'",
1638 pImage->pszFilename);
1639 }
1640 else if (!RTUuidCompareStr(&pRegTblEntry->UuidObject, VHDX_REGION_TBL_ENTRY_UUID_METADATA))
1641 {
1642 if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1643 rc = vhdxLoadMetadataRegion(pImage, pRegTblEntry->u64FileOffset, pRegTblEntry->u32Length);
1644 else
1645 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1646 "VHDX: Metadata region not marked as required in image \'%s\'",
1647 pImage->pszFilename);
1648 }
1649 else if (pRegTblEntry->u32Flags & VHDX_REGION_TBL_ENTRY_FLAGS_IS_REQUIRED)
1650 {
1651 /* The region is not known but marked as required, fail to load the image. */
1652 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
1653 "VHDX: Unknown required region in image \'%s\'",
1654 pImage->pszFilename);
1655 }
1656
1657 if (RT_FAILURE(rc))
1658 break;
1659
1660 pRegTblEntry++;
1661 }
1662
1663 if (fBatRegPresent)
1664 rc = vhdxLoadBatRegion(pImage, RegTblEntryBat.u64FileOffset, RegTblEntryBat.u32Length);
1665 else
1666 rc = vdIfError(pImage->pIfError, VERR_VD_GEN_INVALID_HEADER, RT_SRC_POS,
1667 "VHDX: BAT region in image \'%s\' is missing",
1668 pImage->pszFilename);
1669 }
1670 }
1671 else
1672 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1673 "VHDX: Reading the region table for image \'%s\' failed",
1674 pImage->pszFilename);
1675 }
1676 else
1677 rc = vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
1678 "VHDX: Out of memory allocating memory for the region table of image \'%s\'",
1679 pImage->pszFilename);
1680
1681 if (pbRegionTbl)
1682 RTMemTmpFree(pbRegionTbl);
1683
1684 LogFlowFunc(("returns rc=%Rrc\n", rc));
1685 return rc;
1686}
1687
1688/**
1689 * Internal: Open an image, constructing all necessary data structures.
1690 */
1691static int vhdxOpenImage(PVHDXIMAGE pImage, unsigned uOpenFlags)
1692{
1693 uint64_t cbFile = 0;
1694 VhdxFileIdentifier FileIdentifier;
1695 int rc = VINF_SUCCESS;
1696
1697 LogFlowFunc(("pImage=%#p uOpenFlags=%#x\n", pImage, uOpenFlags));
1698 pImage->uOpenFlags = uOpenFlags;
1699
1700 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
1701 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
1702 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
1703
1704 /* Refuse write access, it is not implemented so far. */
1705 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1706 return VERR_NOT_SUPPORTED;
1707
1708 /*
1709 * Open the image.
1710 */
1711 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
1712 VDOpenFlagsToFileOpenFlags(uOpenFlags,
1713 false /* fCreate */),
1714 &pImage->pStorage);
1715
1716 /* Do NOT signal an appropriate error here, as the VD layer has the
1717 * choice of retrying the open if it failed. */
1718 if (RT_SUCCESS(rc))
1719 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
1720
1721 if (RT_SUCCESS(rc))
1722 {
1723 if (cbFile > sizeof(FileIdentifier))
1724 {
1725 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
1726 &FileIdentifier, sizeof(FileIdentifier));
1727 if (RT_SUCCESS(rc))
1728 {
1729 vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
1730 &FileIdentifier);
1731 if (FileIdentifier.u64Signature != VHDX_FILE_IDENTIFIER_SIGNATURE)
1732 rc = VERR_VD_GEN_INVALID_HEADER;
1733 else
1734 rc = vhdxFindAndLoadCurrentHeader(pImage);
1735
1736 /* Load the region table. */
1737 if (RT_SUCCESS(rc))
1738 rc = vhdxLoadRegionTable(pImage);
1739 }
1740 }
1741 else
1742 rc = VERR_VD_GEN_INVALID_HEADER;
1743 }
1744
1745 if (RT_FAILURE(rc))
1746 vhdxFreeImage(pImage, false);
1747
1748 LogFlowFunc(("returns rc=%Rrc\n", rc));
1749 return rc;
1750}
1751
1752
1753/** @copydoc VDIMAGEBACKEND::pfnProbe */
1754static DECLCALLBACK(int) vhdxProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
1755 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
1756{
1757 RT_NOREF1(pVDIfsDisk);
1758 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p\n", pszFilename, pVDIfsDisk, pVDIfsImage));
1759 PVDIOSTORAGE pStorage = NULL;
1760 uint64_t cbFile;
1761 int rc = VINF_SUCCESS;
1762 VhdxFileIdentifier FileIdentifier;
1763
1764 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
1765 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
1766
1767 if ( !VALID_PTR(pszFilename)
1768 || !*pszFilename)
1769 rc = VERR_INVALID_PARAMETER;
1770 else
1771 {
1772 /*
1773 * Open the file and read the file identifier.
1774 */
1775 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
1776 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
1777 false /* fCreate */),
1778 &pStorage);
1779 if (RT_SUCCESS(rc))
1780 {
1781 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
1782 if (RT_SUCCESS(rc))
1783 {
1784 if (cbFile > sizeof(FileIdentifier))
1785 {
1786 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, VHDX_FILE_IDENTIFIER_OFFSET,
1787 &FileIdentifier, sizeof(FileIdentifier));
1788 if (RT_SUCCESS(rc))
1789 {
1790 vhdxConvFileIdentifierEndianess(VHDXECONV_F2H, &FileIdentifier,
1791 &FileIdentifier);
1792 if (FileIdentifier.u64Signature != VHDX_FILE_IDENTIFIER_SIGNATURE)
1793 rc = VERR_VD_GEN_INVALID_HEADER;
1794 else
1795 *penmType = VDTYPE_HDD;
1796 }
1797 }
1798 else
1799 rc = VERR_VD_GEN_INVALID_HEADER;
1800 }
1801 }
1802
1803 if (pStorage)
1804 vdIfIoIntFileClose(pIfIo, pStorage);
1805 }
1806
1807 LogFlowFunc(("returns %Rrc\n", rc));
1808 return rc;
1809}
1810
1811/** @copydoc VDIMAGEBACKEND::pfnOpen */
1812static DECLCALLBACK(int) vhdxOpen(const char *pszFilename, unsigned uOpenFlags,
1813 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1814 VDTYPE enmType, void **ppBackendData)
1815{
1816 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
1817 int rc;
1818 PVHDXIMAGE pImage;
1819
1820 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
1821
1822 /* Check open flags. All valid flags are supported. */
1823 if ( uOpenFlags & ~VD_OPEN_FLAGS_MASK
1824 || !VALID_PTR(pszFilename)
1825 || !*pszFilename)
1826 rc = VERR_INVALID_PARAMETER;
1827 else
1828 {
1829 pImage = (PVHDXIMAGE)RTMemAllocZ(sizeof(VHDXIMAGE));
1830 if (!pImage)
1831 rc = VERR_NO_MEMORY;
1832 else
1833 {
1834 pImage->pszFilename = pszFilename;
1835 pImage->pStorage = NULL;
1836 pImage->pVDIfsDisk = pVDIfsDisk;
1837 pImage->pVDIfsImage = pVDIfsImage;
1838
1839 rc = vhdxOpenImage(pImage, uOpenFlags);
1840 if (RT_SUCCESS(rc))
1841 *ppBackendData = pImage;
1842 else
1843 RTMemFree(pImage);
1844 }
1845 }
1846
1847 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
1848 return rc;
1849}
1850
1851/** @interface_method_impl{VDIMAGEBACKEND,pfnCreate} */
1852static DECLCALLBACK(int) vhdxCreate(const char *pszFilename, uint64_t cbSize,
1853 unsigned uImageFlags, const char *pszComment,
1854 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
1855 PCRTUUID pUuid, unsigned uOpenFlags,
1856 unsigned uPercentStart, unsigned uPercentSpan,
1857 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
1858 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
1859 void **ppBackendData)
1860{
1861 RT_NOREF8(pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags);
1862 RT_NOREF7(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData);
1863 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",
1864 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
1865 int rc = VERR_NOT_SUPPORTED;
1866
1867 LogFlowFunc(("returns %Rrc\n", rc));
1868 return rc;
1869}
1870
1871/** @copydoc VDIMAGEBACKEND::pfnRename */
1872static DECLCALLBACK(int) vhdxRename(void *pBackendData, const char *pszFilename)
1873{
1874 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
1875 int rc = VINF_SUCCESS;
1876 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1877
1878 /* Check arguments. */
1879 if ( !pImage
1880 || !pszFilename
1881 || !*pszFilename)
1882 rc = VERR_INVALID_PARAMETER;
1883 else
1884 {
1885 /* Close the image. */
1886 rc = vhdxFreeImage(pImage, false);
1887 if (RT_SUCCESS(rc))
1888 {
1889 /* Rename the file. */
1890 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
1891 if (RT_FAILURE(rc))
1892 {
1893 /* The move failed, try to reopen the original image. */
1894 int rc2 = vhdxOpenImage(pImage, pImage->uOpenFlags);
1895 if (RT_FAILURE(rc2))
1896 rc = rc2;
1897 }
1898 else
1899 {
1900 /* Update pImage with the new information. */
1901 pImage->pszFilename = pszFilename;
1902
1903 /* Open the old image with new name. */
1904 rc = vhdxOpenImage(pImage, pImage->uOpenFlags);
1905 }
1906 }
1907 }
1908
1909 LogFlowFunc(("returns %Rrc\n", rc));
1910 return rc;
1911}
1912
1913/** @copydoc VDIMAGEBACKEND::pfnClose */
1914static DECLCALLBACK(int) vhdxClose(void *pBackendData, bool fDelete)
1915{
1916 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
1917 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1918 int rc;
1919
1920 rc = vhdxFreeImage(pImage, fDelete);
1921 RTMemFree(pImage);
1922
1923 LogFlowFunc(("returns %Rrc\n", rc));
1924 return rc;
1925}
1926
1927/** @copydoc VDIMAGEBACKEND::pfnRead */
1928static DECLCALLBACK(int) vhdxRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1929 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1930{
1931 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1932 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1933 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1934 int rc = VINF_SUCCESS;
1935
1936 AssertPtr(pImage);
1937 Assert(uOffset % 512 == 0);
1938 Assert(cbToRead % 512 == 0);
1939
1940 if ( uOffset + cbToRead > pImage->cbSize
1941 || cbToRead == 0)
1942 rc = VERR_INVALID_PARAMETER;
1943 else
1944 {
1945 uint32_t idxBat = (uint32_t)(uOffset / pImage->cbBlock); Assert(idxBat == uOffset / pImage->cbBlock);
1946 uint32_t offRead = uOffset % pImage->cbBlock;
1947 uint64_t uBatEntry;
1948
1949 idxBat += idxBat / pImage->uChunkRatio; /* Add interleaving sector bitmap entries. */
1950 uBatEntry = pImage->paBat[idxBat].u64BatEntry;
1951
1952 cbToRead = RT_MIN(cbToRead, pImage->cbBlock - offRead);
1953
1954 switch (VHDX_BAT_ENTRY_GET_STATE(uBatEntry))
1955 {
1956 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_NOT_PRESENT:
1957 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNDEFINED:
1958 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_ZERO:
1959 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_UNMAPPED:
1960 {
1961 vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
1962 break;
1963 }
1964 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_FULLY_PRESENT:
1965 {
1966 uint64_t offFile = VHDX_BAT_ENTRY_GET_FILE_OFFSET(uBatEntry) + offRead;
1967 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, offFile,
1968 pIoCtx, cbToRead);
1969 break;
1970 }
1971 case VHDX_BAT_ENTRY_PAYLOAD_BLOCK_PARTIALLY_PRESENT:
1972 default:
1973 rc = VERR_INVALID_PARAMETER;
1974 break;
1975 }
1976
1977 if (pcbActuallyRead)
1978 *pcbActuallyRead = cbToRead;
1979 }
1980
1981 LogFlowFunc(("returns %Rrc\n", rc));
1982 return rc;
1983}
1984
1985/** @copydoc VDIMAGEBACKEND::pfnWrite */
1986static DECLCALLBACK(int) vhdxWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1987 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
1988 size_t *pcbPostRead, unsigned fWrite)
1989{
1990 RT_NOREF5(pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
1991 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
1992 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
1993 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
1994 int rc;
1995
1996 AssertPtr(pImage);
1997 Assert(uOffset % 512 == 0);
1998 Assert(cbToWrite % 512 == 0);
1999
2000 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2001 rc = VERR_VD_IMAGE_READ_ONLY;
2002 else if ( uOffset + cbToWrite > pImage->cbSize
2003 || cbToWrite == 0)
2004 rc = VERR_INVALID_PARAMETER;
2005 else
2006 rc = VERR_NOT_SUPPORTED;
2007
2008 LogFlowFunc(("returns %Rrc\n", rc));
2009 return rc;
2010}
2011
2012/** @copydoc VDIMAGEBACKEND::pfnFlush */
2013static DECLCALLBACK(int) vhdxFlush(void *pBackendData, PVDIOCTX pIoCtx)
2014{
2015 RT_NOREF1(pIoCtx);
2016 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p\n", pBackendData, pIoCtx));
2017 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2018 int rc;
2019
2020 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2021 rc = VERR_VD_IMAGE_READ_ONLY;
2022 else
2023 rc = VERR_NOT_SUPPORTED;
2024
2025 LogFlowFunc(("returns %Rrc\n", rc));
2026 return rc;
2027}
2028
2029/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
2030static DECLCALLBACK(unsigned) vhdxGetVersion(void *pBackendData)
2031{
2032 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2033 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2034
2035 AssertPtr(pImage);
2036
2037 if (pImage)
2038 return pImage->uVersion;
2039 else
2040 return 0;
2041}
2042
2043/** @copydoc VDIMAGEBACKEND::pfnGetSectorSize */
2044static DECLCALLBACK(uint32_t) vhdxGetSectorSize(void *pBackendData)
2045{
2046 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2047 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2048 uint32_t cb = 0;
2049
2050 AssertPtr(pImage);
2051
2052 if (pImage && pImage->pStorage)
2053 cb = pImage->cbLogicalSector;
2054
2055 LogFlowFunc(("returns %u\n", cb));
2056 return cb;
2057}
2058
2059/** @copydoc VDIMAGEBACKEND::pfnGetSize */
2060static DECLCALLBACK(uint64_t) vhdxGetSize(void *pBackendData)
2061{
2062 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2063 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2064 uint64_t cb = 0;
2065
2066 AssertPtr(pImage);
2067
2068 if (pImage && pImage->pStorage)
2069 cb = pImage->cbSize;
2070
2071 LogFlowFunc(("returns %llu\n", cb));
2072 return cb;
2073}
2074
2075/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
2076static DECLCALLBACK(uint64_t) vhdxGetFileSize(void *pBackendData)
2077{
2078 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2079 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2080 uint64_t cb = 0;
2081
2082 AssertPtr(pImage);
2083
2084 if (pImage)
2085 {
2086 uint64_t cbFile;
2087 if (pImage->pStorage)
2088 {
2089 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbFile);
2090 if (RT_SUCCESS(rc))
2091 cb = cbFile;
2092 }
2093 }
2094
2095 LogFlowFunc(("returns %lld\n", cb));
2096 return cb;
2097}
2098
2099/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
2100static DECLCALLBACK(int) vhdxGetPCHSGeometry(void *pBackendData,
2101 PVDGEOMETRY pPCHSGeometry)
2102{
2103 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
2104 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2105 int rc;
2106
2107 AssertPtr(pImage);
2108
2109 if (pImage)
2110 {
2111 if (pImage->PCHSGeometry.cCylinders)
2112 {
2113 *pPCHSGeometry = pImage->PCHSGeometry;
2114 rc = VINF_SUCCESS;
2115 }
2116 else
2117 rc = VERR_VD_GEOMETRY_NOT_SET;
2118 }
2119 else
2120 rc = VERR_VD_NOT_OPENED;
2121
2122 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2123 return rc;
2124}
2125
2126/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
2127static DECLCALLBACK(int) vhdxSetPCHSGeometry(void *pBackendData,
2128 PCVDGEOMETRY pPCHSGeometry)
2129{
2130 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
2131 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2132 int rc = VINF_SUCCESS;
2133
2134 AssertPtr(pImage);
2135
2136 if (pImage)
2137 {
2138 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2139 rc = VERR_VD_IMAGE_READ_ONLY;
2140 else
2141 pImage->PCHSGeometry = *pPCHSGeometry;
2142 }
2143 else
2144 rc = VERR_VD_NOT_OPENED;
2145
2146 LogFlowFunc(("returns %Rrc\n", rc));
2147 return rc;
2148}
2149
2150/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
2151static DECLCALLBACK(int) vhdxGetLCHSGeometry(void *pBackendData,
2152 PVDGEOMETRY pLCHSGeometry)
2153{
2154 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
2155 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2156 int rc = VINF_SUCCESS;
2157
2158 AssertPtr(pImage);
2159
2160 if (pImage)
2161 {
2162 if (pImage->LCHSGeometry.cCylinders)
2163 *pLCHSGeometry = pImage->LCHSGeometry;
2164 else
2165 rc = VERR_VD_GEOMETRY_NOT_SET;
2166 }
2167 else
2168 rc = VERR_VD_NOT_OPENED;
2169
2170 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2171 return rc;
2172}
2173
2174/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
2175static DECLCALLBACK(int) vhdxSetLCHSGeometry(void *pBackendData,
2176 PCVDGEOMETRY pLCHSGeometry)
2177{
2178 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2179 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2180 int rc = VINF_SUCCESS;
2181
2182 AssertPtr(pImage);
2183
2184 if (pImage)
2185 {
2186 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2187 rc = VERR_VD_IMAGE_READ_ONLY;
2188 else
2189 pImage->LCHSGeometry = *pLCHSGeometry;
2190 }
2191 else
2192 rc = VERR_VD_NOT_OPENED;
2193
2194 LogFlowFunc(("returns %Rrc\n", rc));
2195 return rc;
2196}
2197
2198/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
2199static DECLCALLBACK(unsigned) vhdxGetImageFlags(void *pBackendData)
2200{
2201 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2202 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2203 unsigned uImageFlags;
2204
2205 AssertPtr(pImage);
2206
2207 if (pImage)
2208 uImageFlags = pImage->uImageFlags;
2209 else
2210 uImageFlags = 0;
2211
2212 LogFlowFunc(("returns %#x\n", uImageFlags));
2213 return uImageFlags;
2214}
2215
2216/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
2217static DECLCALLBACK(unsigned) vhdxGetOpenFlags(void *pBackendData)
2218{
2219 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
2220 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2221 unsigned uOpenFlags;
2222
2223 AssertPtr(pImage);
2224
2225 if (pImage)
2226 uOpenFlags = pImage->uOpenFlags;
2227 else
2228 uOpenFlags = 0;
2229
2230 LogFlowFunc(("returns %#x\n", uOpenFlags));
2231 return uOpenFlags;
2232}
2233
2234/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
2235static DECLCALLBACK(int) vhdxSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
2236{
2237 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
2238 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2239 int rc = VINF_SUCCESS;
2240
2241 /* Image must be opened and the new flags must be valid. */
2242 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
2243 rc = VERR_INVALID_PARAMETER;
2244 else
2245 {
2246 /* Implement this operation via reopening the image. */
2247 rc = vhdxFreeImage(pImage, false);
2248 if (RT_SUCCESS(rc))
2249 rc = vhdxOpenImage(pImage, uOpenFlags);
2250 }
2251
2252 LogFlowFunc(("returns %Rrc\n", rc));
2253 return rc;
2254}
2255
2256/** @copydoc VDIMAGEBACKEND::pfnGetComment */
2257static DECLCALLBACK(int) vhdxGetComment(void *pBackendData, char *pszComment,
2258 size_t cbComment)
2259{
2260 RT_NOREF2(pszComment, cbComment);
2261 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
2262 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2263 int rc;
2264
2265 AssertPtr(pImage);
2266
2267 if (pImage)
2268 rc = VERR_NOT_SUPPORTED;
2269 else
2270 rc = VERR_VD_NOT_OPENED;
2271
2272 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
2273 return rc;
2274}
2275
2276/** @copydoc VDIMAGEBACKEND::pfnSetComment */
2277static DECLCALLBACK(int) vhdxSetComment(void *pBackendData, const char *pszComment)
2278{
2279 RT_NOREF1(pszComment);
2280 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
2281 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2282 int rc;
2283
2284 AssertPtr(pImage);
2285
2286 if (pImage)
2287 {
2288 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2289 rc = VERR_VD_IMAGE_READ_ONLY;
2290 else
2291 rc = VERR_NOT_SUPPORTED;
2292 }
2293 else
2294 rc = VERR_VD_NOT_OPENED;
2295
2296 LogFlowFunc(("returns %Rrc\n", rc));
2297 return rc;
2298}
2299
2300/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
2301static DECLCALLBACK(int) vhdxGetUuid(void *pBackendData, PRTUUID pUuid)
2302{
2303 RT_NOREF1(pUuid);
2304 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2305 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2306 int rc;
2307
2308 AssertPtr(pImage);
2309
2310 if (pImage)
2311 rc = VERR_NOT_SUPPORTED;
2312 else
2313 rc = VERR_VD_NOT_OPENED;
2314
2315 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2316 return rc;
2317}
2318
2319/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
2320static DECLCALLBACK(int) vhdxSetUuid(void *pBackendData, PCRTUUID pUuid)
2321{
2322 RT_NOREF1(pUuid);
2323 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2324 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2325 int rc;
2326
2327 LogFlowFunc(("%RTuuid\n", pUuid));
2328 AssertPtr(pImage);
2329
2330 if (pImage)
2331 {
2332 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2333 rc = VERR_NOT_SUPPORTED;
2334 else
2335 rc = VERR_VD_IMAGE_READ_ONLY;
2336 }
2337 else
2338 rc = VERR_VD_NOT_OPENED;
2339
2340 LogFlowFunc(("returns %Rrc\n", rc));
2341 return rc;
2342}
2343
2344/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
2345static DECLCALLBACK(int) vhdxGetModificationUuid(void *pBackendData, PRTUUID pUuid)
2346{
2347 RT_NOREF1(pUuid);
2348 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2349 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2350 int rc;
2351
2352 AssertPtr(pImage);
2353
2354 if (pImage)
2355 rc = VERR_NOT_SUPPORTED;
2356 else
2357 rc = VERR_VD_NOT_OPENED;
2358
2359 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2360 return rc;
2361}
2362
2363/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
2364static DECLCALLBACK(int) vhdxSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
2365{
2366 RT_NOREF1(pUuid);
2367 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2368 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2369 int rc;
2370
2371 AssertPtr(pImage);
2372
2373 if (pImage)
2374 {
2375 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2376 rc = VERR_NOT_SUPPORTED;
2377 else
2378 rc = VERR_VD_IMAGE_READ_ONLY;
2379 }
2380 else
2381 rc = VERR_VD_NOT_OPENED;
2382
2383 LogFlowFunc(("returns %Rrc\n", rc));
2384 return rc;
2385}
2386
2387/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
2388static DECLCALLBACK(int) vhdxGetParentUuid(void *pBackendData, PRTUUID pUuid)
2389{
2390 RT_NOREF1(pUuid);
2391 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2392 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2393 int rc;
2394
2395 AssertPtr(pImage);
2396
2397 if (pImage)
2398 rc = VERR_NOT_SUPPORTED;
2399 else
2400 rc = VERR_VD_NOT_OPENED;
2401
2402 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2403 return rc;
2404}
2405
2406/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
2407static DECLCALLBACK(int) vhdxSetParentUuid(void *pBackendData, PCRTUUID pUuid)
2408{
2409 RT_NOREF1(pUuid);
2410 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2411 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2412 int rc;
2413
2414 AssertPtr(pImage);
2415
2416 if (pImage)
2417 {
2418 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2419 rc = VERR_NOT_SUPPORTED;
2420 else
2421 rc = VERR_VD_IMAGE_READ_ONLY;
2422 }
2423 else
2424 rc = VERR_VD_NOT_OPENED;
2425
2426 LogFlowFunc(("returns %Rrc\n", rc));
2427 return rc;
2428}
2429
2430/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
2431static DECLCALLBACK(int) vhdxGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
2432{
2433 RT_NOREF1(pUuid);
2434 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
2435 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2436 int rc;
2437
2438 AssertPtr(pImage);
2439
2440 if (pImage)
2441 rc = VERR_NOT_SUPPORTED;
2442 else
2443 rc = VERR_VD_NOT_OPENED;
2444
2445 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
2446 return rc;
2447}
2448
2449/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
2450static DECLCALLBACK(int) vhdxSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
2451{
2452 RT_NOREF1(pUuid);
2453 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
2454 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2455 int rc;
2456
2457 AssertPtr(pImage);
2458
2459 if (pImage)
2460 {
2461 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2462 rc = VERR_NOT_SUPPORTED;
2463 else
2464 rc = VERR_VD_IMAGE_READ_ONLY;
2465 }
2466 else
2467 rc = VERR_VD_NOT_OPENED;
2468
2469 LogFlowFunc(("returns %Rrc\n", rc));
2470 return rc;
2471}
2472
2473/** @copydoc VDIMAGEBACKEND::pfnDump */
2474static DECLCALLBACK(void) vhdxDump(void *pBackendData)
2475{
2476 PVHDXIMAGE pImage = (PVHDXIMAGE)pBackendData;
2477
2478 AssertPtr(pImage);
2479 if (pImage)
2480 {
2481 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%u\n",
2482 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
2483 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
2484 pImage->cbLogicalSector);
2485 }
2486}
2487
2488
2489const VDIMAGEBACKEND g_VhdxBackend =
2490{
2491 /* u32Version */
2492 VD_IMGBACKEND_VERSION,
2493 /* pszBackendName */
2494 "VHDX",
2495 /* uBackendCaps */
2496 VD_CAP_FILE | VD_CAP_VFS,
2497 /* paFileExtensions */
2498 s_aVhdxFileExtensions,
2499 /* paConfigInfo */
2500 NULL,
2501 /* pfnProbe */
2502 vhdxProbe,
2503 /* pfnOpen */
2504 vhdxOpen,
2505 /* pfnCreate */
2506 vhdxCreate,
2507 /* pfnRename */
2508 vhdxRename,
2509 /* pfnClose */
2510 vhdxClose,
2511 /* pfnRead */
2512 vhdxRead,
2513 /* pfnWrite */
2514 vhdxWrite,
2515 /* pfnFlush */
2516 vhdxFlush,
2517 /* pfnDiscard */
2518 NULL,
2519 /* pfnGetVersion */
2520 vhdxGetVersion,
2521 /* pfnGetSectorSize */
2522 vhdxGetSectorSize,
2523 /* pfnGetSize */
2524 vhdxGetSize,
2525 /* pfnGetFileSize */
2526 vhdxGetFileSize,
2527 /* pfnGetPCHSGeometry */
2528 vhdxGetPCHSGeometry,
2529 /* pfnSetPCHSGeometry */
2530 vhdxSetPCHSGeometry,
2531 /* pfnGetLCHSGeometry */
2532 vhdxGetLCHSGeometry,
2533 /* pfnSetLCHSGeometry */
2534 vhdxSetLCHSGeometry,
2535 /* pfnGetImageFlags */
2536 vhdxGetImageFlags,
2537 /* pfnGetOpenFlags */
2538 vhdxGetOpenFlags,
2539 /* pfnSetOpenFlags */
2540 vhdxSetOpenFlags,
2541 /* pfnGetComment */
2542 vhdxGetComment,
2543 /* pfnSetComment */
2544 vhdxSetComment,
2545 /* pfnGetUuid */
2546 vhdxGetUuid,
2547 /* pfnSetUuid */
2548 vhdxSetUuid,
2549 /* pfnGetModificationUuid */
2550 vhdxGetModificationUuid,
2551 /* pfnSetModificationUuid */
2552 vhdxSetModificationUuid,
2553 /* pfnGetParentUuid */
2554 vhdxGetParentUuid,
2555 /* pfnSetParentUuid */
2556 vhdxSetParentUuid,
2557 /* pfnGetParentModificationUuid */
2558 vhdxGetParentModificationUuid,
2559 /* pfnSetParentModificationUuid */
2560 vhdxSetParentModificationUuid,
2561 /* pfnDump */
2562 vhdxDump,
2563 /* pfnGetTimestamp */
2564 NULL,
2565 /* pfnGetParentTimestamp */
2566 NULL,
2567 /* pfnSetParentTimestamp */
2568 NULL,
2569 /* pfnGetParentFilename */
2570 NULL,
2571 /* pfnSetParentFilename */
2572 NULL,
2573 /* pfnComposeLocation */
2574 genericFileComposeLocation,
2575 /* pfnComposeName */
2576 genericFileComposeName,
2577 /* pfnCompact */
2578 NULL,
2579 /* pfnResize */
2580 NULL,
2581 /* pfnRepair */
2582 NULL,
2583 /* pfnTraverseMetadata */
2584 NULL,
2585 /* u32VersionEnd */
2586 VD_IMGBACKEND_VERSION
2587};
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