VirtualBox

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

Last change on this file since 63811 was 63802, checked in by vboxsync, 8 years ago

Storage/VD: Rename pfnCheckIfValid to pfnProbe as it is shorter and easier to spell

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette