VirtualBox

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

Last change on this file since 82781 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

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

© 2023 Oracle
ContactPrivacy policyTerms of Use