VirtualBox

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

Last change on this file since 91933 was 90802, checked in by vboxsync, 3 years ago

Storage: More VALID_PTR -> RT_VALID_PTR/AssertPtr.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use