VirtualBox

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

Last change on this file since 67981 was 66494, checked in by vboxsync, 7 years ago

Storage: Get rid of duplicated code in the backends for unsupported features

  • 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 66494 2017-04-10 09:57:25Z vboxsync $ */
2/** @file
3 * VHDX - VHDX Disk image, Core Code.
4 */
5
6/*
7 * Copyright (C) 2012-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_VD_VHDX
23#include <VBox/vd-plugin.h>
24#include <VBox/err.h>
25
26#include <VBox/log.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/alloc.h>
30#include <iprt/path.h>
31#include <iprt/uuid.h>
32#include <iprt/crc.h>
33
34#include "VDBackends.h"
35#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 *penmType)
1773{
1774 RT_NOREF1(pVDIfsDisk);
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