Statistics
| Branch: | Revision:

root / block / vhdx.c @ f53ec699

History | View | Annotate | Download (30.4 kB)

1 e8d4e5ff Jeff Cody
/*
2 e8d4e5ff Jeff Cody
 * Block driver for Hyper-V VHDX Images
3 e8d4e5ff Jeff Cody
 *
4 e8d4e5ff Jeff Cody
 * Copyright (c) 2013 Red Hat, Inc.,
5 e8d4e5ff Jeff Cody
 *
6 e8d4e5ff Jeff Cody
 * Authors:
7 e8d4e5ff Jeff Cody
 *  Jeff Cody <jcody@redhat.com>
8 e8d4e5ff Jeff Cody
 *
9 e8d4e5ff Jeff Cody
 *  This is based on the "VHDX Format Specification v0.95", published 4/12/2012
10 e8d4e5ff Jeff Cody
 *  by Microsoft:
11 e8d4e5ff Jeff Cody
 *      https://www.microsoft.com/en-us/download/details.aspx?id=29681
12 e8d4e5ff Jeff Cody
 *
13 e8d4e5ff Jeff Cody
 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
14 e8d4e5ff Jeff Cody
 * See the COPYING.LIB file in the top-level directory.
15 e8d4e5ff Jeff Cody
 *
16 e8d4e5ff Jeff Cody
 */
17 e8d4e5ff Jeff Cody
18 e8d4e5ff Jeff Cody
#include "qemu-common.h"
19 e8d4e5ff Jeff Cody
#include "block/block_int.h"
20 e8d4e5ff Jeff Cody
#include "qemu/module.h"
21 e8d4e5ff Jeff Cody
#include "qemu/crc32c.h"
22 e8d4e5ff Jeff Cody
#include "block/vhdx.h"
23 e8d4e5ff Jeff Cody
24 e8d4e5ff Jeff Cody
25 e8d4e5ff Jeff Cody
/* Several metadata and region table data entries are identified by
26 e8d4e5ff Jeff Cody
 * guids in  a MS-specific GUID format. */
27 e8d4e5ff Jeff Cody
28 e8d4e5ff Jeff Cody
29 e8d4e5ff Jeff Cody
/* ------- Known Region Table GUIDs ---------------------- */
30 e8d4e5ff Jeff Cody
static const MSGUID bat_guid =      { .data1 = 0x2dc27766,
31 e8d4e5ff Jeff Cody
                                      .data2 = 0xf623,
32 e8d4e5ff Jeff Cody
                                      .data3 = 0x4200,
33 e8d4e5ff Jeff Cody
                                      .data4 = { 0x9d, 0x64, 0x11, 0x5e,
34 e8d4e5ff Jeff Cody
                                                 0x9b, 0xfd, 0x4a, 0x08} };
35 e8d4e5ff Jeff Cody
36 e8d4e5ff Jeff Cody
static const MSGUID metadata_guid = { .data1 = 0x8b7ca206,
37 e8d4e5ff Jeff Cody
                                      .data2 = 0x4790,
38 e8d4e5ff Jeff Cody
                                      .data3 = 0x4b9a,
39 e8d4e5ff Jeff Cody
                                      .data4 = { 0xb8, 0xfe, 0x57, 0x5f,
40 e8d4e5ff Jeff Cody
                                                 0x05, 0x0f, 0x88, 0x6e} };
41 e8d4e5ff Jeff Cody
42 e8d4e5ff Jeff Cody
43 e8d4e5ff Jeff Cody
44 e8d4e5ff Jeff Cody
/* ------- Known Metadata Entry GUIDs ---------------------- */
45 e8d4e5ff Jeff Cody
static const MSGUID file_param_guid =   { .data1 = 0xcaa16737,
46 e8d4e5ff Jeff Cody
                                          .data2 = 0xfa36,
47 e8d4e5ff Jeff Cody
                                          .data3 = 0x4d43,
48 e8d4e5ff Jeff Cody
                                          .data4 = { 0xb3, 0xb6, 0x33, 0xf0,
49 e8d4e5ff Jeff Cody
                                                     0xaa, 0x44, 0xe7, 0x6b} };
50 e8d4e5ff Jeff Cody
51 e8d4e5ff Jeff Cody
static const MSGUID virtual_size_guid = { .data1 = 0x2FA54224,
52 e8d4e5ff Jeff Cody
                                          .data2 = 0xcd1b,
53 e8d4e5ff Jeff Cody
                                          .data3 = 0x4876,
54 e8d4e5ff Jeff Cody
                                          .data4 = { 0xb2, 0x11, 0x5d, 0xbe,
55 e8d4e5ff Jeff Cody
                                                     0xd8, 0x3b, 0xf4, 0xb8} };
56 e8d4e5ff Jeff Cody
57 e8d4e5ff Jeff Cody
static const MSGUID page83_guid =       { .data1 = 0xbeca12ab,
58 e8d4e5ff Jeff Cody
                                          .data2 = 0xb2e6,
59 e8d4e5ff Jeff Cody
                                          .data3 = 0x4523,
60 e8d4e5ff Jeff Cody
                                          .data4 = { 0x93, 0xef, 0xc3, 0x09,
61 e8d4e5ff Jeff Cody
                                                     0xe0, 0x00, 0xc7, 0x46} };
62 e8d4e5ff Jeff Cody
63 e8d4e5ff Jeff Cody
64 e8d4e5ff Jeff Cody
static const MSGUID phys_sector_guid =  { .data1 = 0xcda348c7,
65 e8d4e5ff Jeff Cody
                                          .data2 = 0x445d,
66 e8d4e5ff Jeff Cody
                                          .data3 = 0x4471,
67 e8d4e5ff Jeff Cody
                                          .data4 = { 0x9c, 0xc9, 0xe9, 0x88,
68 e8d4e5ff Jeff Cody
                                                     0x52, 0x51, 0xc5, 0x56} };
69 e8d4e5ff Jeff Cody
70 e8d4e5ff Jeff Cody
static const MSGUID parent_locator_guid = { .data1 = 0xa8d35f2d,
71 e8d4e5ff Jeff Cody
                                            .data2 = 0xb30b,
72 e8d4e5ff Jeff Cody
                                            .data3 = 0x454d,
73 e8d4e5ff Jeff Cody
                                            .data4 = { 0xab, 0xf7, 0xd3,
74 e8d4e5ff Jeff Cody
                                                       0xd8, 0x48, 0x34,
75 e8d4e5ff Jeff Cody
                                                       0xab, 0x0c} };
76 e8d4e5ff Jeff Cody
77 e8d4e5ff Jeff Cody
static const MSGUID logical_sector_guid = { .data1 = 0x8141bf1d,
78 e8d4e5ff Jeff Cody
                                            .data2 = 0xa96f,
79 e8d4e5ff Jeff Cody
                                            .data3 = 0x4709,
80 e8d4e5ff Jeff Cody
                                            .data4 = { 0xba, 0x47, 0xf2,
81 e8d4e5ff Jeff Cody
                                                       0x33, 0xa8, 0xfa,
82 e8d4e5ff Jeff Cody
                                                       0xab, 0x5f} };
83 e8d4e5ff Jeff Cody
84 e8d4e5ff Jeff Cody
/* Each parent type must have a valid GUID; this is for parent images
85 e8d4e5ff Jeff Cody
 * of type 'VHDX'.  If we were to allow e.g. a QCOW2 parent, we would
86 e8d4e5ff Jeff Cody
 * need to make up our own QCOW2 GUID type */
87 e8d4e5ff Jeff Cody
static const MSGUID parent_vhdx_guid = { .data1 = 0xb04aefb7,
88 e8d4e5ff Jeff Cody
                                         .data2 = 0xd19e,
89 e8d4e5ff Jeff Cody
                                         .data3 = 0x4a81,
90 e8d4e5ff Jeff Cody
                                         .data4 = { 0xb7, 0x89, 0x25, 0xb8,
91 e8d4e5ff Jeff Cody
                                                    0xe9, 0x44, 0x59, 0x13} };
92 e8d4e5ff Jeff Cody
93 e8d4e5ff Jeff Cody
94 e8d4e5ff Jeff Cody
#define META_FILE_PARAMETER_PRESENT      0x01
95 e8d4e5ff Jeff Cody
#define META_VIRTUAL_DISK_SIZE_PRESENT   0x02
96 e8d4e5ff Jeff Cody
#define META_PAGE_83_PRESENT             0x04
97 e8d4e5ff Jeff Cody
#define META_LOGICAL_SECTOR_SIZE_PRESENT 0x08
98 e8d4e5ff Jeff Cody
#define META_PHYS_SECTOR_SIZE_PRESENT    0x10
99 e8d4e5ff Jeff Cody
#define META_PARENT_LOCATOR_PRESENT      0x20
100 e8d4e5ff Jeff Cody
101 e8d4e5ff Jeff Cody
#define META_ALL_PRESENT    \
102 e8d4e5ff Jeff Cody
    (META_FILE_PARAMETER_PRESENT | META_VIRTUAL_DISK_SIZE_PRESENT | \
103 e8d4e5ff Jeff Cody
     META_PAGE_83_PRESENT | META_LOGICAL_SECTOR_SIZE_PRESENT | \
104 e8d4e5ff Jeff Cody
     META_PHYS_SECTOR_SIZE_PRESENT)
105 e8d4e5ff Jeff Cody
106 e8d4e5ff Jeff Cody
typedef struct VHDXMetadataEntries {
107 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry file_parameters_entry;
108 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry virtual_disk_size_entry;
109 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry page83_data_entry;
110 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry logical_sector_size_entry;
111 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry phys_sector_size_entry;
112 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry parent_locator_entry;
113 e8d4e5ff Jeff Cody
    uint16_t present;
114 e8d4e5ff Jeff Cody
} VHDXMetadataEntries;
115 e8d4e5ff Jeff Cody
116 e8d4e5ff Jeff Cody
117 059e2fbb Jeff Cody
typedef struct VHDXSectorInfo {
118 059e2fbb Jeff Cody
    uint32_t bat_idx;       /* BAT entry index */
119 059e2fbb Jeff Cody
    uint32_t sectors_avail; /* sectors available in payload block */
120 059e2fbb Jeff Cody
    uint32_t bytes_left;    /* bytes left in the block after data to r/w */
121 059e2fbb Jeff Cody
    uint32_t bytes_avail;   /* bytes available in payload block */
122 059e2fbb Jeff Cody
    uint64_t file_offset;   /* absolute offset in bytes, in file */
123 059e2fbb Jeff Cody
    uint64_t block_offset;  /* block offset, in bytes */
124 059e2fbb Jeff Cody
} VHDXSectorInfo;
125 059e2fbb Jeff Cody
126 059e2fbb Jeff Cody
127 059e2fbb Jeff Cody
128 e8d4e5ff Jeff Cody
typedef struct BDRVVHDXState {
129 e8d4e5ff Jeff Cody
    CoMutex lock;
130 e8d4e5ff Jeff Cody
131 e8d4e5ff Jeff Cody
    int curr_header;
132 e8d4e5ff Jeff Cody
    VHDXHeader *headers[2];
133 e8d4e5ff Jeff Cody
134 e8d4e5ff Jeff Cody
    VHDXRegionTableHeader rt;
135 e8d4e5ff Jeff Cody
    VHDXRegionTableEntry bat_rt;         /* region table for the BAT */
136 e8d4e5ff Jeff Cody
    VHDXRegionTableEntry metadata_rt;    /* region table for the metadata */
137 e8d4e5ff Jeff Cody
138 e8d4e5ff Jeff Cody
    VHDXMetadataTableHeader metadata_hdr;
139 e8d4e5ff Jeff Cody
    VHDXMetadataEntries metadata_entries;
140 e8d4e5ff Jeff Cody
141 e8d4e5ff Jeff Cody
    VHDXFileParameters params;
142 e8d4e5ff Jeff Cody
    uint32_t block_size;
143 e8d4e5ff Jeff Cody
    uint32_t block_size_bits;
144 e8d4e5ff Jeff Cody
    uint32_t sectors_per_block;
145 e8d4e5ff Jeff Cody
    uint32_t sectors_per_block_bits;
146 e8d4e5ff Jeff Cody
147 e8d4e5ff Jeff Cody
    uint64_t virtual_disk_size;
148 e8d4e5ff Jeff Cody
    uint32_t logical_sector_size;
149 e8d4e5ff Jeff Cody
    uint32_t physical_sector_size;
150 e8d4e5ff Jeff Cody
151 e8d4e5ff Jeff Cody
    uint64_t chunk_ratio;
152 e8d4e5ff Jeff Cody
    uint32_t chunk_ratio_bits;
153 e8d4e5ff Jeff Cody
    uint32_t logical_sector_size_bits;
154 e8d4e5ff Jeff Cody
155 e8d4e5ff Jeff Cody
    uint32_t bat_entries;
156 e8d4e5ff Jeff Cody
    VHDXBatEntry *bat;
157 e8d4e5ff Jeff Cody
    uint64_t bat_offset;
158 e8d4e5ff Jeff Cody
159 e8d4e5ff Jeff Cody
    VHDXParentLocatorHeader parent_header;
160 e8d4e5ff Jeff Cody
    VHDXParentLocatorEntry *parent_entries;
161 e8d4e5ff Jeff Cody
162 e8d4e5ff Jeff Cody
} BDRVVHDXState;
163 e8d4e5ff Jeff Cody
164 e8d4e5ff Jeff Cody
uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
165 e8d4e5ff Jeff Cody
                            int crc_offset)
166 e8d4e5ff Jeff Cody
{
167 e8d4e5ff Jeff Cody
    uint32_t crc_new;
168 e8d4e5ff Jeff Cody
    uint32_t crc_orig;
169 e8d4e5ff Jeff Cody
    assert(buf != NULL);
170 e8d4e5ff Jeff Cody
171 e8d4e5ff Jeff Cody
    if (crc_offset > 0) {
172 e8d4e5ff Jeff Cody
        memcpy(&crc_orig, buf + crc_offset, sizeof(crc_orig));
173 e8d4e5ff Jeff Cody
        memset(buf + crc_offset, 0, sizeof(crc_orig));
174 e8d4e5ff Jeff Cody
    }
175 e8d4e5ff Jeff Cody
176 e8d4e5ff Jeff Cody
    crc_new = crc32c(crc, buf, size);
177 e8d4e5ff Jeff Cody
    if (crc_offset > 0) {
178 e8d4e5ff Jeff Cody
        memcpy(buf + crc_offset, &crc_orig, sizeof(crc_orig));
179 e8d4e5ff Jeff Cody
    }
180 e8d4e5ff Jeff Cody
181 e8d4e5ff Jeff Cody
    return crc_new;
182 e8d4e5ff Jeff Cody
}
183 e8d4e5ff Jeff Cody
184 e8d4e5ff Jeff Cody
/* Validates the checksum of the buffer, with an in-place CRC.
185 e8d4e5ff Jeff Cody
 *
186 e8d4e5ff Jeff Cody
 * Zero is substituted during crc calculation for the original crc field,
187 e8d4e5ff Jeff Cody
 * and the crc field is restored afterwards.  But the buffer will be modifed
188 e8d4e5ff Jeff Cody
 * during the calculation, so this may not be not suitable for multi-threaded
189 e8d4e5ff Jeff Cody
 * use.
190 e8d4e5ff Jeff Cody
 *
191 e8d4e5ff Jeff Cody
 * crc_offset: byte offset in buf of the buffer crc
192 e8d4e5ff Jeff Cody
 * buf: buffer pointer
193 e8d4e5ff Jeff Cody
 * size: size of buffer (must be > crc_offset+4)
194 e8d4e5ff Jeff Cody
 *
195 e8d4e5ff Jeff Cody
 * returns true if checksum is valid, false otherwise
196 e8d4e5ff Jeff Cody
 */
197 e8d4e5ff Jeff Cody
bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset)
198 e8d4e5ff Jeff Cody
{
199 e8d4e5ff Jeff Cody
    uint32_t crc_orig;
200 e8d4e5ff Jeff Cody
    uint32_t crc;
201 e8d4e5ff Jeff Cody
202 e8d4e5ff Jeff Cody
    assert(buf != NULL);
203 e8d4e5ff Jeff Cody
    assert(size > (crc_offset + 4));
204 e8d4e5ff Jeff Cody
205 e8d4e5ff Jeff Cody
    memcpy(&crc_orig, buf + crc_offset, sizeof(crc_orig));
206 e8d4e5ff Jeff Cody
    crc_orig = le32_to_cpu(crc_orig);
207 e8d4e5ff Jeff Cody
208 e8d4e5ff Jeff Cody
    crc = vhdx_checksum_calc(0xffffffff, buf, size, crc_offset);
209 e8d4e5ff Jeff Cody
210 e8d4e5ff Jeff Cody
    return crc == crc_orig;
211 e8d4e5ff Jeff Cody
}
212 e8d4e5ff Jeff Cody
213 e8d4e5ff Jeff Cody
214 e8d4e5ff Jeff Cody
/*
215 e8d4e5ff Jeff Cody
 * Per the MS VHDX Specification, for every VHDX file:
216 e8d4e5ff Jeff Cody
 *      - The header section is fixed size - 1 MB
217 e8d4e5ff Jeff Cody
 *      - The header section is always the first "object"
218 e8d4e5ff Jeff Cody
 *      - The first 64KB of the header is the File Identifier
219 e8d4e5ff Jeff Cody
 *      - The first uint64 (8 bytes) is the VHDX Signature ("vhdxfile")
220 e8d4e5ff Jeff Cody
 *      - The following 512 bytes constitute a UTF-16 string identifiying the
221 e8d4e5ff Jeff Cody
 *        software that created the file, and is optional and diagnostic only.
222 e8d4e5ff Jeff Cody
 *
223 e8d4e5ff Jeff Cody
 *  Therefore, we probe by looking for the vhdxfile signature "vhdxfile"
224 e8d4e5ff Jeff Cody
 */
225 e8d4e5ff Jeff Cody
static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename)
226 e8d4e5ff Jeff Cody
{
227 e8d4e5ff Jeff Cody
    if (buf_size >= 8 && !memcmp(buf, "vhdxfile", 8)) {
228 e8d4e5ff Jeff Cody
        return 100;
229 e8d4e5ff Jeff Cody
    }
230 e8d4e5ff Jeff Cody
    return 0;
231 e8d4e5ff Jeff Cody
}
232 e8d4e5ff Jeff Cody
233 e8d4e5ff Jeff Cody
/* All VHDX structures on disk are little endian */
234 e8d4e5ff Jeff Cody
static void vhdx_header_le_import(VHDXHeader *h)
235 e8d4e5ff Jeff Cody
{
236 e8d4e5ff Jeff Cody
    assert(h != NULL);
237 e8d4e5ff Jeff Cody
238 e8d4e5ff Jeff Cody
    le32_to_cpus(&h->signature);
239 e8d4e5ff Jeff Cody
    le32_to_cpus(&h->checksum);
240 e8d4e5ff Jeff Cody
    le64_to_cpus(&h->sequence_number);
241 e8d4e5ff Jeff Cody
242 e8d4e5ff Jeff Cody
    leguid_to_cpus(&h->file_write_guid);
243 e8d4e5ff Jeff Cody
    leguid_to_cpus(&h->data_write_guid);
244 e8d4e5ff Jeff Cody
    leguid_to_cpus(&h->log_guid);
245 e8d4e5ff Jeff Cody
246 e8d4e5ff Jeff Cody
    le16_to_cpus(&h->log_version);
247 e8d4e5ff Jeff Cody
    le16_to_cpus(&h->version);
248 e8d4e5ff Jeff Cody
    le32_to_cpus(&h->log_length);
249 e8d4e5ff Jeff Cody
    le64_to_cpus(&h->log_offset);
250 e8d4e5ff Jeff Cody
}
251 e8d4e5ff Jeff Cody
252 e8d4e5ff Jeff Cody
253 e8d4e5ff Jeff Cody
/* opens the specified header block from the VHDX file header section */
254 e8d4e5ff Jeff Cody
static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
255 e8d4e5ff Jeff Cody
{
256 e8d4e5ff Jeff Cody
    int ret = 0;
257 e8d4e5ff Jeff Cody
    VHDXHeader *header1;
258 e8d4e5ff Jeff Cody
    VHDXHeader *header2;
259 e8d4e5ff Jeff Cody
    bool h1_valid = false;
260 e8d4e5ff Jeff Cody
    bool h2_valid = false;
261 e8d4e5ff Jeff Cody
    uint64_t h1_seq = 0;
262 e8d4e5ff Jeff Cody
    uint64_t h2_seq = 0;
263 e8d4e5ff Jeff Cody
    uint8_t *buffer;
264 e8d4e5ff Jeff Cody
265 e8d4e5ff Jeff Cody
    header1 = qemu_blockalign(bs, sizeof(VHDXHeader));
266 e8d4e5ff Jeff Cody
    header2 = qemu_blockalign(bs, sizeof(VHDXHeader));
267 e8d4e5ff Jeff Cody
268 e8d4e5ff Jeff Cody
    buffer = qemu_blockalign(bs, VHDX_HEADER_SIZE);
269 e8d4e5ff Jeff Cody
270 e8d4e5ff Jeff Cody
    s->headers[0] = header1;
271 e8d4e5ff Jeff Cody
    s->headers[1] = header2;
272 e8d4e5ff Jeff Cody
273 e8d4e5ff Jeff Cody
    /* We have to read the whole VHDX_HEADER_SIZE instead of
274 e8d4e5ff Jeff Cody
     * sizeof(VHDXHeader), because the checksum is over the whole
275 e8d4e5ff Jeff Cody
     * region */
276 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, VHDX_HEADER_SIZE);
277 e8d4e5ff Jeff Cody
    if (ret < 0) {
278 e8d4e5ff Jeff Cody
        goto fail;
279 e8d4e5ff Jeff Cody
    }
280 e8d4e5ff Jeff Cody
    /* copy over just the relevant portion that we need */
281 e8d4e5ff Jeff Cody
    memcpy(header1, buffer, sizeof(VHDXHeader));
282 e8d4e5ff Jeff Cody
    vhdx_header_le_import(header1);
283 e8d4e5ff Jeff Cody
284 e8d4e5ff Jeff Cody
    if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
285 e8d4e5ff Jeff Cody
        !memcmp(&header1->signature, "head", 4)             &&
286 e8d4e5ff Jeff Cody
        header1->version == 1) {
287 e8d4e5ff Jeff Cody
        h1_seq = header1->sequence_number;
288 e8d4e5ff Jeff Cody
        h1_valid = true;
289 e8d4e5ff Jeff Cody
    }
290 e8d4e5ff Jeff Cody
291 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE);
292 e8d4e5ff Jeff Cody
    if (ret < 0) {
293 e8d4e5ff Jeff Cody
        goto fail;
294 e8d4e5ff Jeff Cody
    }
295 e8d4e5ff Jeff Cody
    /* copy over just the relevant portion that we need */
296 e8d4e5ff Jeff Cody
    memcpy(header2, buffer, sizeof(VHDXHeader));
297 e8d4e5ff Jeff Cody
    vhdx_header_le_import(header2);
298 e8d4e5ff Jeff Cody
299 e8d4e5ff Jeff Cody
    if (vhdx_checksum_is_valid(buffer, VHDX_HEADER_SIZE, 4) &&
300 e8d4e5ff Jeff Cody
        !memcmp(&header2->signature, "head", 4)             &&
301 e8d4e5ff Jeff Cody
        header2->version == 1) {
302 e8d4e5ff Jeff Cody
        h2_seq = header2->sequence_number;
303 e8d4e5ff Jeff Cody
        h2_valid = true;
304 e8d4e5ff Jeff Cody
    }
305 e8d4e5ff Jeff Cody
306 e8d4e5ff Jeff Cody
    /* If there is only 1 valid header (or no valid headers), we
307 e8d4e5ff Jeff Cody
     * don't care what the sequence numbers are */
308 e8d4e5ff Jeff Cody
    if (h1_valid && !h2_valid) {
309 e8d4e5ff Jeff Cody
        s->curr_header = 0;
310 e8d4e5ff Jeff Cody
    } else if (!h1_valid && h2_valid) {
311 e8d4e5ff Jeff Cody
        s->curr_header = 1;
312 e8d4e5ff Jeff Cody
    } else if (!h1_valid && !h2_valid) {
313 e8d4e5ff Jeff Cody
        ret = -EINVAL;
314 e8d4e5ff Jeff Cody
        goto fail;
315 e8d4e5ff Jeff Cody
    } else {
316 e8d4e5ff Jeff Cody
        /* If both headers are valid, then we choose the active one by the
317 e8d4e5ff Jeff Cody
         * highest sequence number.  If the sequence numbers are equal, that is
318 e8d4e5ff Jeff Cody
         * invalid */
319 e8d4e5ff Jeff Cody
        if (h1_seq > h2_seq) {
320 e8d4e5ff Jeff Cody
            s->curr_header = 0;
321 e8d4e5ff Jeff Cody
        } else if (h2_seq > h1_seq) {
322 e8d4e5ff Jeff Cody
            s->curr_header = 1;
323 e8d4e5ff Jeff Cody
        } else {
324 e8d4e5ff Jeff Cody
            ret = -EINVAL;
325 e8d4e5ff Jeff Cody
            goto fail;
326 e8d4e5ff Jeff Cody
        }
327 e8d4e5ff Jeff Cody
    }
328 e8d4e5ff Jeff Cody
329 e8d4e5ff Jeff Cody
    ret = 0;
330 e8d4e5ff Jeff Cody
331 e8d4e5ff Jeff Cody
    goto exit;
332 e8d4e5ff Jeff Cody
333 e8d4e5ff Jeff Cody
fail:
334 e8d4e5ff Jeff Cody
    qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found");
335 e8d4e5ff Jeff Cody
    qemu_vfree(header1);
336 e8d4e5ff Jeff Cody
    qemu_vfree(header2);
337 e8d4e5ff Jeff Cody
    s->headers[0] = NULL;
338 e8d4e5ff Jeff Cody
    s->headers[1] = NULL;
339 e8d4e5ff Jeff Cody
exit:
340 e8d4e5ff Jeff Cody
    qemu_vfree(buffer);
341 e8d4e5ff Jeff Cody
    return ret;
342 e8d4e5ff Jeff Cody
}
343 e8d4e5ff Jeff Cody
344 e8d4e5ff Jeff Cody
345 e8d4e5ff Jeff Cody
static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s)
346 e8d4e5ff Jeff Cody
{
347 e8d4e5ff Jeff Cody
    int ret = 0;
348 e8d4e5ff Jeff Cody
    uint8_t *buffer;
349 e8d4e5ff Jeff Cody
    int offset = 0;
350 e8d4e5ff Jeff Cody
    VHDXRegionTableEntry rt_entry;
351 e8d4e5ff Jeff Cody
    uint32_t i;
352 e8d4e5ff Jeff Cody
    bool bat_rt_found = false;
353 e8d4e5ff Jeff Cody
    bool metadata_rt_found = false;
354 e8d4e5ff Jeff Cody
355 e8d4e5ff Jeff Cody
    /* We have to read the whole 64KB block, because the crc32 is over the
356 e8d4e5ff Jeff Cody
     * whole block */
357 e8d4e5ff Jeff Cody
    buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE);
358 e8d4e5ff Jeff Cody
359 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer,
360 e8d4e5ff Jeff Cody
                     VHDX_HEADER_BLOCK_SIZE);
361 e8d4e5ff Jeff Cody
    if (ret < 0) {
362 e8d4e5ff Jeff Cody
        goto fail;
363 e8d4e5ff Jeff Cody
    }
364 e8d4e5ff Jeff Cody
    memcpy(&s->rt, buffer, sizeof(s->rt));
365 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->rt.signature);
366 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->rt.checksum);
367 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->rt.entry_count);
368 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->rt.reserved);
369 e8d4e5ff Jeff Cody
    offset += sizeof(s->rt);
370 e8d4e5ff Jeff Cody
371 e8d4e5ff Jeff Cody
    if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) ||
372 e8d4e5ff Jeff Cody
        memcmp(&s->rt.signature, "regi", 4)) {
373 e8d4e5ff Jeff Cody
        ret = -EINVAL;
374 e8d4e5ff Jeff Cody
        goto fail;
375 e8d4e5ff Jeff Cody
    }
376 e8d4e5ff Jeff Cody
377 e8d4e5ff Jeff Cody
    /* Per spec, maximum region table entry count is 2047 */
378 e8d4e5ff Jeff Cody
    if (s->rt.entry_count > 2047) {
379 e8d4e5ff Jeff Cody
        ret = -EINVAL;
380 e8d4e5ff Jeff Cody
        goto fail;
381 e8d4e5ff Jeff Cody
    }
382 e8d4e5ff Jeff Cody
383 e8d4e5ff Jeff Cody
    for (i = 0; i < s->rt.entry_count; i++) {
384 e8d4e5ff Jeff Cody
        memcpy(&rt_entry, buffer + offset, sizeof(rt_entry));
385 e8d4e5ff Jeff Cody
        offset += sizeof(rt_entry);
386 e8d4e5ff Jeff Cody
387 e8d4e5ff Jeff Cody
        leguid_to_cpus(&rt_entry.guid);
388 e8d4e5ff Jeff Cody
        le64_to_cpus(&rt_entry.file_offset);
389 e8d4e5ff Jeff Cody
        le32_to_cpus(&rt_entry.length);
390 e8d4e5ff Jeff Cody
        le32_to_cpus(&rt_entry.data_bits);
391 e8d4e5ff Jeff Cody
392 e8d4e5ff Jeff Cody
        /* see if we recognize the entry */
393 e8d4e5ff Jeff Cody
        if (guid_eq(rt_entry.guid, bat_guid)) {
394 e8d4e5ff Jeff Cody
            /* must be unique; if we have already found it this is invalid */
395 e8d4e5ff Jeff Cody
            if (bat_rt_found) {
396 e8d4e5ff Jeff Cody
                ret = -EINVAL;
397 e8d4e5ff Jeff Cody
                goto fail;
398 e8d4e5ff Jeff Cody
            }
399 e8d4e5ff Jeff Cody
            bat_rt_found = true;
400 e8d4e5ff Jeff Cody
            s->bat_rt = rt_entry;
401 e8d4e5ff Jeff Cody
            continue;
402 e8d4e5ff Jeff Cody
        }
403 e8d4e5ff Jeff Cody
404 e8d4e5ff Jeff Cody
        if (guid_eq(rt_entry.guid, metadata_guid)) {
405 e8d4e5ff Jeff Cody
            /* must be unique; if we have already found it this is invalid */
406 e8d4e5ff Jeff Cody
            if (metadata_rt_found) {
407 e8d4e5ff Jeff Cody
                ret = -EINVAL;
408 e8d4e5ff Jeff Cody
                goto fail;
409 e8d4e5ff Jeff Cody
            }
410 e8d4e5ff Jeff Cody
            metadata_rt_found = true;
411 e8d4e5ff Jeff Cody
            s->metadata_rt = rt_entry;
412 e8d4e5ff Jeff Cody
            continue;
413 e8d4e5ff Jeff Cody
        }
414 e8d4e5ff Jeff Cody
415 e8d4e5ff Jeff Cody
        if (rt_entry.data_bits & VHDX_REGION_ENTRY_REQUIRED) {
416 e8d4e5ff Jeff Cody
            /* cannot read vhdx file - required region table entry that
417 e8d4e5ff Jeff Cody
             * we do not understand.  per spec, we must fail to open */
418 e8d4e5ff Jeff Cody
            ret = -ENOTSUP;
419 e8d4e5ff Jeff Cody
            goto fail;
420 e8d4e5ff Jeff Cody
        }
421 e8d4e5ff Jeff Cody
    }
422 e8d4e5ff Jeff Cody
    ret = 0;
423 e8d4e5ff Jeff Cody
424 e8d4e5ff Jeff Cody
fail:
425 e8d4e5ff Jeff Cody
    qemu_vfree(buffer);
426 e8d4e5ff Jeff Cody
    return ret;
427 e8d4e5ff Jeff Cody
}
428 e8d4e5ff Jeff Cody
429 e8d4e5ff Jeff Cody
430 e8d4e5ff Jeff Cody
431 e8d4e5ff Jeff Cody
/* Metadata initial parser
432 e8d4e5ff Jeff Cody
 *
433 e8d4e5ff Jeff Cody
 * This loads all the metadata entry fields.  This may cause additional
434 e8d4e5ff Jeff Cody
 * fields to be processed (e.g. parent locator, etc..).
435 e8d4e5ff Jeff Cody
 *
436 e8d4e5ff Jeff Cody
 * There are 5 Metadata items that are always required:
437 e8d4e5ff Jeff Cody
 *      - File Parameters (block size, has a parent)
438 e8d4e5ff Jeff Cody
 *      - Virtual Disk Size (size, in bytes, of the virtual drive)
439 e8d4e5ff Jeff Cody
 *      - Page 83 Data (scsi page 83 guid)
440 e8d4e5ff Jeff Cody
 *      - Logical Sector Size (logical sector size in bytes, either 512 or
441 e8d4e5ff Jeff Cody
 *                             4096.  We only support 512 currently)
442 e8d4e5ff Jeff Cody
 *      - Physical Sector Size (512 or 4096)
443 e8d4e5ff Jeff Cody
 *
444 e8d4e5ff Jeff Cody
 * Also, if the File Parameters indicate this is a differencing file,
445 e8d4e5ff Jeff Cody
 * we must also look for the Parent Locator metadata item.
446 e8d4e5ff Jeff Cody
 */
447 e8d4e5ff Jeff Cody
static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
448 e8d4e5ff Jeff Cody
{
449 e8d4e5ff Jeff Cody
    int ret = 0;
450 e8d4e5ff Jeff Cody
    uint8_t *buffer;
451 e8d4e5ff Jeff Cody
    int offset = 0;
452 e8d4e5ff Jeff Cody
    uint32_t i = 0;
453 e8d4e5ff Jeff Cody
    VHDXMetadataTableEntry md_entry;
454 e8d4e5ff Jeff Cody
455 e8d4e5ff Jeff Cody
    buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE);
456 e8d4e5ff Jeff Cody
457 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer,
458 e8d4e5ff Jeff Cody
                     VHDX_METADATA_TABLE_MAX_SIZE);
459 e8d4e5ff Jeff Cody
    if (ret < 0) {
460 e8d4e5ff Jeff Cody
        goto exit;
461 e8d4e5ff Jeff Cody
    }
462 e8d4e5ff Jeff Cody
    memcpy(&s->metadata_hdr, buffer, sizeof(s->metadata_hdr));
463 e8d4e5ff Jeff Cody
    offset += sizeof(s->metadata_hdr);
464 e8d4e5ff Jeff Cody
465 e8d4e5ff Jeff Cody
    le64_to_cpus(&s->metadata_hdr.signature);
466 e8d4e5ff Jeff Cody
    le16_to_cpus(&s->metadata_hdr.reserved);
467 e8d4e5ff Jeff Cody
    le16_to_cpus(&s->metadata_hdr.entry_count);
468 e8d4e5ff Jeff Cody
469 e8d4e5ff Jeff Cody
    if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) {
470 e8d4e5ff Jeff Cody
        ret = -EINVAL;
471 e8d4e5ff Jeff Cody
        goto exit;
472 e8d4e5ff Jeff Cody
    }
473 e8d4e5ff Jeff Cody
474 e8d4e5ff Jeff Cody
    s->metadata_entries.present = 0;
475 e8d4e5ff Jeff Cody
476 e8d4e5ff Jeff Cody
    if ((s->metadata_hdr.entry_count * sizeof(md_entry)) >
477 e8d4e5ff Jeff Cody
        (VHDX_METADATA_TABLE_MAX_SIZE - offset)) {
478 e8d4e5ff Jeff Cody
        ret = -EINVAL;
479 e8d4e5ff Jeff Cody
        goto exit;
480 e8d4e5ff Jeff Cody
    }
481 e8d4e5ff Jeff Cody
482 e8d4e5ff Jeff Cody
    for (i = 0; i < s->metadata_hdr.entry_count; i++) {
483 e8d4e5ff Jeff Cody
        memcpy(&md_entry, buffer + offset, sizeof(md_entry));
484 e8d4e5ff Jeff Cody
        offset += sizeof(md_entry);
485 e8d4e5ff Jeff Cody
486 e8d4e5ff Jeff Cody
        leguid_to_cpus(&md_entry.item_id);
487 e8d4e5ff Jeff Cody
        le32_to_cpus(&md_entry.offset);
488 e8d4e5ff Jeff Cody
        le32_to_cpus(&md_entry.length);
489 e8d4e5ff Jeff Cody
        le32_to_cpus(&md_entry.data_bits);
490 e8d4e5ff Jeff Cody
        le32_to_cpus(&md_entry.reserved2);
491 e8d4e5ff Jeff Cody
492 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, file_param_guid)) {
493 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present & META_FILE_PARAMETER_PRESENT) {
494 e8d4e5ff Jeff Cody
                ret = -EINVAL;
495 e8d4e5ff Jeff Cody
                goto exit;
496 e8d4e5ff Jeff Cody
            }
497 e8d4e5ff Jeff Cody
            s->metadata_entries.file_parameters_entry = md_entry;
498 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_FILE_PARAMETER_PRESENT;
499 e8d4e5ff Jeff Cody
            continue;
500 e8d4e5ff Jeff Cody
        }
501 e8d4e5ff Jeff Cody
502 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, virtual_size_guid)) {
503 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present & META_VIRTUAL_DISK_SIZE_PRESENT) {
504 e8d4e5ff Jeff Cody
                ret = -EINVAL;
505 e8d4e5ff Jeff Cody
                goto exit;
506 e8d4e5ff Jeff Cody
            }
507 e8d4e5ff Jeff Cody
            s->metadata_entries.virtual_disk_size_entry = md_entry;
508 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_VIRTUAL_DISK_SIZE_PRESENT;
509 e8d4e5ff Jeff Cody
            continue;
510 e8d4e5ff Jeff Cody
        }
511 e8d4e5ff Jeff Cody
512 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, page83_guid)) {
513 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present & META_PAGE_83_PRESENT) {
514 e8d4e5ff Jeff Cody
                ret = -EINVAL;
515 e8d4e5ff Jeff Cody
                goto exit;
516 e8d4e5ff Jeff Cody
            }
517 e8d4e5ff Jeff Cody
            s->metadata_entries.page83_data_entry = md_entry;
518 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_PAGE_83_PRESENT;
519 e8d4e5ff Jeff Cody
            continue;
520 e8d4e5ff Jeff Cody
        }
521 e8d4e5ff Jeff Cody
522 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, logical_sector_guid)) {
523 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present &
524 e8d4e5ff Jeff Cody
                META_LOGICAL_SECTOR_SIZE_PRESENT) {
525 e8d4e5ff Jeff Cody
                ret = -EINVAL;
526 e8d4e5ff Jeff Cody
                goto exit;
527 e8d4e5ff Jeff Cody
            }
528 e8d4e5ff Jeff Cody
            s->metadata_entries.logical_sector_size_entry = md_entry;
529 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_LOGICAL_SECTOR_SIZE_PRESENT;
530 e8d4e5ff Jeff Cody
            continue;
531 e8d4e5ff Jeff Cody
        }
532 e8d4e5ff Jeff Cody
533 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, phys_sector_guid)) {
534 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present & META_PHYS_SECTOR_SIZE_PRESENT) {
535 e8d4e5ff Jeff Cody
                ret = -EINVAL;
536 e8d4e5ff Jeff Cody
                goto exit;
537 e8d4e5ff Jeff Cody
            }
538 e8d4e5ff Jeff Cody
            s->metadata_entries.phys_sector_size_entry = md_entry;
539 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_PHYS_SECTOR_SIZE_PRESENT;
540 e8d4e5ff Jeff Cody
            continue;
541 e8d4e5ff Jeff Cody
        }
542 e8d4e5ff Jeff Cody
543 e8d4e5ff Jeff Cody
        if (guid_eq(md_entry.item_id, parent_locator_guid)) {
544 e8d4e5ff Jeff Cody
            if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) {
545 e8d4e5ff Jeff Cody
                ret = -EINVAL;
546 e8d4e5ff Jeff Cody
                goto exit;
547 e8d4e5ff Jeff Cody
            }
548 e8d4e5ff Jeff Cody
            s->metadata_entries.parent_locator_entry = md_entry;
549 e8d4e5ff Jeff Cody
            s->metadata_entries.present |= META_PARENT_LOCATOR_PRESENT;
550 e8d4e5ff Jeff Cody
            continue;
551 e8d4e5ff Jeff Cody
        }
552 e8d4e5ff Jeff Cody
553 e8d4e5ff Jeff Cody
        if (md_entry.data_bits & VHDX_META_FLAGS_IS_REQUIRED) {
554 e8d4e5ff Jeff Cody
            /* cannot read vhdx file - required region table entry that
555 e8d4e5ff Jeff Cody
             * we do not understand.  per spec, we must fail to open */
556 e8d4e5ff Jeff Cody
            ret = -ENOTSUP;
557 e8d4e5ff Jeff Cody
            goto exit;
558 e8d4e5ff Jeff Cody
        }
559 e8d4e5ff Jeff Cody
    }
560 e8d4e5ff Jeff Cody
561 e8d4e5ff Jeff Cody
    if (s->metadata_entries.present != META_ALL_PRESENT) {
562 e8d4e5ff Jeff Cody
        ret = -ENOTSUP;
563 e8d4e5ff Jeff Cody
        goto exit;
564 e8d4e5ff Jeff Cody
    }
565 e8d4e5ff Jeff Cody
566 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file,
567 e8d4e5ff Jeff Cody
                     s->metadata_entries.file_parameters_entry.offset
568 e8d4e5ff Jeff Cody
                                         + s->metadata_rt.file_offset,
569 e8d4e5ff Jeff Cody
                     &s->params,
570 e8d4e5ff Jeff Cody
                     sizeof(s->params));
571 e8d4e5ff Jeff Cody
572 e8d4e5ff Jeff Cody
    if (ret < 0) {
573 e8d4e5ff Jeff Cody
        goto exit;
574 e8d4e5ff Jeff Cody
    }
575 e8d4e5ff Jeff Cody
576 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->params.block_size);
577 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->params.data_bits);
578 e8d4e5ff Jeff Cody
579 e8d4e5ff Jeff Cody
580 e8d4e5ff Jeff Cody
    /* We now have the file parameters, so we can tell if this is a
581 e8d4e5ff Jeff Cody
     * differencing file (i.e.. has_parent), is dynamic or fixed
582 e8d4e5ff Jeff Cody
     * sized (leave_blocks_allocated), and the block size */
583 e8d4e5ff Jeff Cody
584 e8d4e5ff Jeff Cody
    /* The parent locator required iff the file parameters has_parent set */
585 e8d4e5ff Jeff Cody
    if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) {
586 e8d4e5ff Jeff Cody
        if (s->metadata_entries.present & META_PARENT_LOCATOR_PRESENT) {
587 e8d4e5ff Jeff Cody
            /* TODO: parse  parent locator fields */
588 e8d4e5ff Jeff Cody
            ret = -ENOTSUP; /* temp, until differencing files are supported */
589 e8d4e5ff Jeff Cody
            goto exit;
590 e8d4e5ff Jeff Cody
        } else {
591 e8d4e5ff Jeff Cody
            /* if has_parent is set, but there is not parent locator present,
592 e8d4e5ff Jeff Cody
             * then that is an invalid combination */
593 e8d4e5ff Jeff Cody
            ret = -EINVAL;
594 e8d4e5ff Jeff Cody
            goto exit;
595 e8d4e5ff Jeff Cody
        }
596 e8d4e5ff Jeff Cody
    }
597 e8d4e5ff Jeff Cody
598 e8d4e5ff Jeff Cody
    /* determine virtual disk size, logical sector size,
599 e8d4e5ff Jeff Cody
     * and phys sector size */
600 e8d4e5ff Jeff Cody
601 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file,
602 e8d4e5ff Jeff Cody
                     s->metadata_entries.virtual_disk_size_entry.offset
603 e8d4e5ff Jeff Cody
                                           + s->metadata_rt.file_offset,
604 e8d4e5ff Jeff Cody
                     &s->virtual_disk_size,
605 e8d4e5ff Jeff Cody
                     sizeof(uint64_t));
606 e8d4e5ff Jeff Cody
    if (ret < 0) {
607 e8d4e5ff Jeff Cody
        goto exit;
608 e8d4e5ff Jeff Cody
    }
609 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file,
610 e8d4e5ff Jeff Cody
                     s->metadata_entries.logical_sector_size_entry.offset
611 e8d4e5ff Jeff Cody
                                             + s->metadata_rt.file_offset,
612 e8d4e5ff Jeff Cody
                     &s->logical_sector_size,
613 e8d4e5ff Jeff Cody
                     sizeof(uint32_t));
614 e8d4e5ff Jeff Cody
    if (ret < 0) {
615 e8d4e5ff Jeff Cody
        goto exit;
616 e8d4e5ff Jeff Cody
    }
617 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file,
618 e8d4e5ff Jeff Cody
                     s->metadata_entries.phys_sector_size_entry.offset
619 e8d4e5ff Jeff Cody
                                          + s->metadata_rt.file_offset,
620 e8d4e5ff Jeff Cody
                     &s->physical_sector_size,
621 e8d4e5ff Jeff Cody
                     sizeof(uint32_t));
622 e8d4e5ff Jeff Cody
    if (ret < 0) {
623 e8d4e5ff Jeff Cody
        goto exit;
624 e8d4e5ff Jeff Cody
    }
625 e8d4e5ff Jeff Cody
626 e8d4e5ff Jeff Cody
    le64_to_cpus(&s->virtual_disk_size);
627 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->logical_sector_size);
628 e8d4e5ff Jeff Cody
    le32_to_cpus(&s->physical_sector_size);
629 e8d4e5ff Jeff Cody
630 e8d4e5ff Jeff Cody
    if (s->logical_sector_size == 0 || s->params.block_size == 0) {
631 e8d4e5ff Jeff Cody
        ret = -EINVAL;
632 e8d4e5ff Jeff Cody
        goto exit;
633 e8d4e5ff Jeff Cody
    }
634 e8d4e5ff Jeff Cody
635 e8d4e5ff Jeff Cody
    /* both block_size and sector_size are guaranteed powers of 2 */
636 e8d4e5ff Jeff Cody
    s->sectors_per_block = s->params.block_size / s->logical_sector_size;
637 e8d4e5ff Jeff Cody
    s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
638 e8d4e5ff Jeff Cody
                     (uint64_t)s->logical_sector_size /
639 e8d4e5ff Jeff Cody
                     (uint64_t)s->params.block_size;
640 e8d4e5ff Jeff Cody
641 e8d4e5ff Jeff Cody
    /* These values are ones we will want to use for division / multiplication
642 e8d4e5ff Jeff Cody
     * later on, and they are all guaranteed (per the spec) to be powers of 2,
643 e8d4e5ff Jeff Cody
     * so we can take advantage of that for shift operations during
644 e8d4e5ff Jeff Cody
     * reads/writes */
645 e8d4e5ff Jeff Cody
    if (s->logical_sector_size & (s->logical_sector_size - 1)) {
646 e8d4e5ff Jeff Cody
        ret = -EINVAL;
647 e8d4e5ff Jeff Cody
        goto exit;
648 e8d4e5ff Jeff Cody
    }
649 e8d4e5ff Jeff Cody
    if (s->sectors_per_block & (s->sectors_per_block - 1)) {
650 e8d4e5ff Jeff Cody
        ret = -EINVAL;
651 e8d4e5ff Jeff Cody
        goto exit;
652 e8d4e5ff Jeff Cody
    }
653 e8d4e5ff Jeff Cody
    if (s->chunk_ratio & (s->chunk_ratio - 1)) {
654 e8d4e5ff Jeff Cody
        ret = -EINVAL;
655 e8d4e5ff Jeff Cody
        goto exit;
656 e8d4e5ff Jeff Cody
    }
657 e8d4e5ff Jeff Cody
    s->block_size = s->params.block_size;
658 e8d4e5ff Jeff Cody
    if (s->block_size & (s->block_size - 1)) {
659 e8d4e5ff Jeff Cody
        ret = -EINVAL;
660 e8d4e5ff Jeff Cody
        goto exit;
661 e8d4e5ff Jeff Cody
    }
662 e8d4e5ff Jeff Cody
663 e8d4e5ff Jeff Cody
    s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
664 e8d4e5ff Jeff Cody
    s->sectors_per_block_bits =   31 - clz32(s->sectors_per_block);
665 e8d4e5ff Jeff Cody
    s->chunk_ratio_bits =         63 - clz64(s->chunk_ratio);
666 e8d4e5ff Jeff Cody
    s->block_size_bits =          31 - clz32(s->block_size);
667 e8d4e5ff Jeff Cody
668 e8d4e5ff Jeff Cody
    ret = 0;
669 e8d4e5ff Jeff Cody
670 e8d4e5ff Jeff Cody
exit:
671 e8d4e5ff Jeff Cody
    qemu_vfree(buffer);
672 e8d4e5ff Jeff Cody
    return ret;
673 e8d4e5ff Jeff Cody
}
674 e8d4e5ff Jeff Cody
675 e8d4e5ff Jeff Cody
/* Parse the replay log.  Per the VHDX spec, if the log is present
676 e8d4e5ff Jeff Cody
 * it must be replayed prior to opening the file, even read-only.
677 e8d4e5ff Jeff Cody
 *
678 e8d4e5ff Jeff Cody
 * If read-only, we must replay the log in RAM (or refuse to open
679 e8d4e5ff Jeff Cody
 * a dirty VHDX file read-only */
680 e8d4e5ff Jeff Cody
static int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s)
681 e8d4e5ff Jeff Cody
{
682 e8d4e5ff Jeff Cody
    int ret = 0;
683 e8d4e5ff Jeff Cody
    int i;
684 e8d4e5ff Jeff Cody
    VHDXHeader *hdr;
685 e8d4e5ff Jeff Cody
686 e8d4e5ff Jeff Cody
    hdr = s->headers[s->curr_header];
687 e8d4e5ff Jeff Cody
688 e8d4e5ff Jeff Cody
    /* either the log guid, or log length is zero,
689 e8d4e5ff Jeff Cody
     * then a replay log is present */
690 e8d4e5ff Jeff Cody
    for (i = 0; i < sizeof(hdr->log_guid.data4); i++) {
691 e8d4e5ff Jeff Cody
        ret |= hdr->log_guid.data4[i];
692 e8d4e5ff Jeff Cody
    }
693 e8d4e5ff Jeff Cody
    if (hdr->log_guid.data1 == 0 &&
694 e8d4e5ff Jeff Cody
        hdr->log_guid.data2 == 0 &&
695 e8d4e5ff Jeff Cody
        hdr->log_guid.data3 == 0 &&
696 e8d4e5ff Jeff Cody
        ret == 0) {
697 e8d4e5ff Jeff Cody
        goto exit;
698 e8d4e5ff Jeff Cody
    }
699 e8d4e5ff Jeff Cody
700 e8d4e5ff Jeff Cody
    /* per spec, only log version of 0 is supported */
701 e8d4e5ff Jeff Cody
    if (hdr->log_version != 0) {
702 e8d4e5ff Jeff Cody
        ret = -EINVAL;
703 e8d4e5ff Jeff Cody
        goto exit;
704 e8d4e5ff Jeff Cody
    }
705 e8d4e5ff Jeff Cody
706 e8d4e5ff Jeff Cody
    if (hdr->log_length == 0) {
707 e8d4e5ff Jeff Cody
        goto exit;
708 e8d4e5ff Jeff Cody
    }
709 e8d4e5ff Jeff Cody
710 e8d4e5ff Jeff Cody
    /* We currently do not support images with logs to replay */
711 e8d4e5ff Jeff Cody
    ret = -ENOTSUP;
712 e8d4e5ff Jeff Cody
713 e8d4e5ff Jeff Cody
exit:
714 e8d4e5ff Jeff Cody
    return ret;
715 e8d4e5ff Jeff Cody
}
716 e8d4e5ff Jeff Cody
717 e8d4e5ff Jeff Cody
718 e8d4e5ff Jeff Cody
static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
719 e8d4e5ff Jeff Cody
{
720 e8d4e5ff Jeff Cody
    BDRVVHDXState *s = bs->opaque;
721 e8d4e5ff Jeff Cody
    int ret = 0;
722 e8d4e5ff Jeff Cody
    uint32_t i;
723 e8d4e5ff Jeff Cody
    uint64_t signature;
724 e8d4e5ff Jeff Cody
    uint32_t data_blocks_cnt, bitmap_blocks_cnt;
725 e8d4e5ff Jeff Cody
726 e8d4e5ff Jeff Cody
727 e8d4e5ff Jeff Cody
    s->bat = NULL;
728 e8d4e5ff Jeff Cody
729 e8d4e5ff Jeff Cody
    qemu_co_mutex_init(&s->lock);
730 e8d4e5ff Jeff Cody
731 e8d4e5ff Jeff Cody
    /* validate the file signature */
732 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t));
733 e8d4e5ff Jeff Cody
    if (ret < 0) {
734 e8d4e5ff Jeff Cody
        goto fail;
735 e8d4e5ff Jeff Cody
    }
736 e8d4e5ff Jeff Cody
    if (memcmp(&signature, "vhdxfile", 8)) {
737 e8d4e5ff Jeff Cody
        ret = -EINVAL;
738 e8d4e5ff Jeff Cody
        goto fail;
739 e8d4e5ff Jeff Cody
    }
740 e8d4e5ff Jeff Cody
741 e8d4e5ff Jeff Cody
    ret = vhdx_parse_header(bs, s);
742 e8d4e5ff Jeff Cody
    if (ret) {
743 e8d4e5ff Jeff Cody
        goto fail;
744 e8d4e5ff Jeff Cody
    }
745 e8d4e5ff Jeff Cody
746 e8d4e5ff Jeff Cody
    ret = vhdx_parse_log(bs, s);
747 e8d4e5ff Jeff Cody
    if (ret) {
748 e8d4e5ff Jeff Cody
        goto fail;
749 e8d4e5ff Jeff Cody
    }
750 e8d4e5ff Jeff Cody
751 e8d4e5ff Jeff Cody
    ret = vhdx_open_region_tables(bs, s);
752 e8d4e5ff Jeff Cody
    if (ret) {
753 e8d4e5ff Jeff Cody
        goto fail;
754 e8d4e5ff Jeff Cody
    }
755 e8d4e5ff Jeff Cody
756 e8d4e5ff Jeff Cody
    ret = vhdx_parse_metadata(bs, s);
757 e8d4e5ff Jeff Cody
    if (ret) {
758 e8d4e5ff Jeff Cody
        goto fail;
759 e8d4e5ff Jeff Cody
    }
760 e8d4e5ff Jeff Cody
    s->block_size = s->params.block_size;
761 e8d4e5ff Jeff Cody
762 e8d4e5ff Jeff Cody
    /* the VHDX spec dictates that virtual_disk_size is always a multiple of
763 e8d4e5ff Jeff Cody
     * logical_sector_size */
764 e8d4e5ff Jeff Cody
    bs->total_sectors = s->virtual_disk_size >> s->logical_sector_size_bits;
765 e8d4e5ff Jeff Cody
766 e8d4e5ff Jeff Cody
    data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
767 e8d4e5ff Jeff Cody
    if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
768 e8d4e5ff Jeff Cody
        data_blocks_cnt++;
769 e8d4e5ff Jeff Cody
    }
770 e8d4e5ff Jeff Cody
    bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
771 e8d4e5ff Jeff Cody
    if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
772 e8d4e5ff Jeff Cody
        bitmap_blocks_cnt++;
773 e8d4e5ff Jeff Cody
    }
774 e8d4e5ff Jeff Cody
775 e8d4e5ff Jeff Cody
    if (s->parent_entries) {
776 e8d4e5ff Jeff Cody
        s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
777 e8d4e5ff Jeff Cody
    } else {
778 e8d4e5ff Jeff Cody
        s->bat_entries = data_blocks_cnt +
779 e8d4e5ff Jeff Cody
                         ((data_blocks_cnt - 1) >> s->chunk_ratio_bits);
780 e8d4e5ff Jeff Cody
    }
781 e8d4e5ff Jeff Cody
782 e8d4e5ff Jeff Cody
    s->bat_offset = s->bat_rt.file_offset;
783 e8d4e5ff Jeff Cody
784 e8d4e5ff Jeff Cody
    if (s->bat_entries > s->bat_rt.length / sizeof(VHDXBatEntry)) {
785 e8d4e5ff Jeff Cody
        /* BAT allocation is not large enough for all entries */
786 e8d4e5ff Jeff Cody
        ret = -EINVAL;
787 e8d4e5ff Jeff Cody
        goto fail;
788 e8d4e5ff Jeff Cody
    }
789 e8d4e5ff Jeff Cody
790 e8d4e5ff Jeff Cody
    s->bat = qemu_blockalign(bs, s->bat_rt.length);
791 e8d4e5ff Jeff Cody
792 e8d4e5ff Jeff Cody
    ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length);
793 e8d4e5ff Jeff Cody
    if (ret < 0) {
794 e8d4e5ff Jeff Cody
        goto fail;
795 e8d4e5ff Jeff Cody
    }
796 e8d4e5ff Jeff Cody
797 e8d4e5ff Jeff Cody
    for (i = 0; i < s->bat_entries; i++) {
798 e8d4e5ff Jeff Cody
        le64_to_cpus(&s->bat[i]);
799 e8d4e5ff Jeff Cody
    }
800 e8d4e5ff Jeff Cody
801 e8d4e5ff Jeff Cody
    if (flags & BDRV_O_RDWR) {
802 e8d4e5ff Jeff Cody
        ret = -ENOTSUP;
803 e8d4e5ff Jeff Cody
        goto fail;
804 e8d4e5ff Jeff Cody
    }
805 e8d4e5ff Jeff Cody
806 059e2fbb Jeff Cody
    /* TODO: differencing files, write */
807 e8d4e5ff Jeff Cody
808 e8d4e5ff Jeff Cody
    return 0;
809 e8d4e5ff Jeff Cody
fail:
810 e8d4e5ff Jeff Cody
    qemu_vfree(s->headers[0]);
811 e8d4e5ff Jeff Cody
    qemu_vfree(s->headers[1]);
812 e8d4e5ff Jeff Cody
    qemu_vfree(s->bat);
813 e8d4e5ff Jeff Cody
    qemu_vfree(s->parent_entries);
814 e8d4e5ff Jeff Cody
    return ret;
815 e8d4e5ff Jeff Cody
}
816 e8d4e5ff Jeff Cody
817 e8d4e5ff Jeff Cody
static int vhdx_reopen_prepare(BDRVReopenState *state,
818 e8d4e5ff Jeff Cody
                               BlockReopenQueue *queue, Error **errp)
819 e8d4e5ff Jeff Cody
{
820 e8d4e5ff Jeff Cody
    return 0;
821 e8d4e5ff Jeff Cody
}
822 e8d4e5ff Jeff Cody
823 e8d4e5ff Jeff Cody
824 059e2fbb Jeff Cody
/*
825 059e2fbb Jeff Cody
 * Perform sector to block offset translations, to get various
826 059e2fbb Jeff Cody
 * sector and file offsets into the image.  See VHDXSectorInfo
827 059e2fbb Jeff Cody
 */
828 059e2fbb Jeff Cody
static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
829 059e2fbb Jeff Cody
                                 int nb_sectors, VHDXSectorInfo *sinfo)
830 059e2fbb Jeff Cody
{
831 059e2fbb Jeff Cody
    uint32_t block_offset;
832 059e2fbb Jeff Cody
833 059e2fbb Jeff Cody
    sinfo->bat_idx = sector_num >> s->sectors_per_block_bits;
834 059e2fbb Jeff Cody
    /* effectively a modulo - this gives us the offset into the block
835 059e2fbb Jeff Cody
     * (in sector sizes) for our sector number */
836 059e2fbb Jeff Cody
    block_offset = sector_num - (sinfo->bat_idx << s->sectors_per_block_bits);
837 059e2fbb Jeff Cody
    /* the chunk ratio gives us the interleaving of the sector
838 059e2fbb Jeff Cody
     * bitmaps, so we need to advance our page block index by the
839 059e2fbb Jeff Cody
     * sector bitmaps entry number */
840 059e2fbb Jeff Cody
    sinfo->bat_idx += sinfo->bat_idx >> s->chunk_ratio_bits;
841 059e2fbb Jeff Cody
842 059e2fbb Jeff Cody
    /* the number of sectors we can read/write in this cycle */
843 059e2fbb Jeff Cody
    sinfo->sectors_avail = s->sectors_per_block - block_offset;
844 059e2fbb Jeff Cody
845 059e2fbb Jeff Cody
    sinfo->bytes_left = sinfo->sectors_avail << s->logical_sector_size_bits;
846 059e2fbb Jeff Cody
847 059e2fbb Jeff Cody
    if (sinfo->sectors_avail > nb_sectors) {
848 059e2fbb Jeff Cody
        sinfo->sectors_avail = nb_sectors;
849 059e2fbb Jeff Cody
    }
850 059e2fbb Jeff Cody
851 059e2fbb Jeff Cody
    sinfo->bytes_avail = sinfo->sectors_avail << s->logical_sector_size_bits;
852 059e2fbb Jeff Cody
853 059e2fbb Jeff Cody
    sinfo->file_offset = s->bat[sinfo->bat_idx] >> VHDX_BAT_FILE_OFF_BITS;
854 059e2fbb Jeff Cody
855 059e2fbb Jeff Cody
    sinfo->block_offset = block_offset << s->logical_sector_size_bits;
856 059e2fbb Jeff Cody
857 059e2fbb Jeff Cody
    /* The file offset must be past the header section, so must be > 0 */
858 059e2fbb Jeff Cody
    if (sinfo->file_offset == 0) {
859 059e2fbb Jeff Cody
        return;
860 059e2fbb Jeff Cody
    }
861 059e2fbb Jeff Cody
862 059e2fbb Jeff Cody
    /* block offset is the offset in vhdx logical sectors, in
863 059e2fbb Jeff Cody
     * the payload data block. Convert that to a byte offset
864 059e2fbb Jeff Cody
     * in the block, and add in the payload data block offset
865 059e2fbb Jeff Cody
     * in the file, in bytes, to get the final read address */
866 059e2fbb Jeff Cody
867 059e2fbb Jeff Cody
    sinfo->file_offset <<= 20;  /* now in bytes, rather than 1MB units */
868 059e2fbb Jeff Cody
    sinfo->file_offset += sinfo->block_offset;
869 059e2fbb Jeff Cody
}
870 059e2fbb Jeff Cody
871 059e2fbb Jeff Cody
872 059e2fbb Jeff Cody
873 e8d4e5ff Jeff Cody
static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
874 e8d4e5ff Jeff Cody
                                      int nb_sectors, QEMUIOVector *qiov)
875 e8d4e5ff Jeff Cody
{
876 059e2fbb Jeff Cody
    BDRVVHDXState *s = bs->opaque;
877 059e2fbb Jeff Cody
    int ret = 0;
878 059e2fbb Jeff Cody
    VHDXSectorInfo sinfo;
879 059e2fbb Jeff Cody
    uint64_t bytes_done = 0;
880 059e2fbb Jeff Cody
    QEMUIOVector hd_qiov;
881 059e2fbb Jeff Cody
882 059e2fbb Jeff Cody
    qemu_iovec_init(&hd_qiov, qiov->niov);
883 059e2fbb Jeff Cody
884 059e2fbb Jeff Cody
    qemu_co_mutex_lock(&s->lock);
885 059e2fbb Jeff Cody
886 059e2fbb Jeff Cody
    while (nb_sectors > 0) {
887 059e2fbb Jeff Cody
        /* We are a differencing file, so we need to inspect the sector bitmap
888 059e2fbb Jeff Cody
         * to see if we have the data or not */
889 059e2fbb Jeff Cody
        if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) {
890 059e2fbb Jeff Cody
            /* not supported yet */
891 059e2fbb Jeff Cody
            ret = -ENOTSUP;
892 059e2fbb Jeff Cody
            goto exit;
893 059e2fbb Jeff Cody
        } else {
894 059e2fbb Jeff Cody
            vhdx_block_translate(s, sector_num, nb_sectors, &sinfo);
895 059e2fbb Jeff Cody
896 059e2fbb Jeff Cody
            qemu_iovec_reset(&hd_qiov);
897 059e2fbb Jeff Cody
            qemu_iovec_concat(&hd_qiov, qiov,  bytes_done, sinfo.bytes_avail);
898 059e2fbb Jeff Cody
899 059e2fbb Jeff Cody
            /* check the payload block state */
900 059e2fbb Jeff Cody
            switch (s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK) {
901 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */
902 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_UNDEFINED:   /* fall through */
903 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_UNMAPPED:    /* fall through */
904 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_ZERO:
905 059e2fbb Jeff Cody
                /* return zero */
906 059e2fbb Jeff Cody
                qemu_iovec_memset(&hd_qiov, 0, 0, sinfo.bytes_avail);
907 059e2fbb Jeff Cody
                break;
908 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_FULL_PRESENT:
909 059e2fbb Jeff Cody
                qemu_co_mutex_unlock(&s->lock);
910 059e2fbb Jeff Cody
                ret = bdrv_co_readv(bs->file,
911 059e2fbb Jeff Cody
                                    sinfo.file_offset >> BDRV_SECTOR_BITS,
912 059e2fbb Jeff Cody
                                    sinfo.sectors_avail, &hd_qiov);
913 059e2fbb Jeff Cody
                qemu_co_mutex_lock(&s->lock);
914 059e2fbb Jeff Cody
                if (ret < 0) {
915 059e2fbb Jeff Cody
                    goto exit;
916 059e2fbb Jeff Cody
                }
917 059e2fbb Jeff Cody
                break;
918 059e2fbb Jeff Cody
            case PAYLOAD_BLOCK_PARTIALLY_PRESENT:
919 059e2fbb Jeff Cody
                /* we don't yet support difference files, fall through
920 059e2fbb Jeff Cody
                 * to error */
921 059e2fbb Jeff Cody
            default:
922 059e2fbb Jeff Cody
                ret = -EIO;
923 059e2fbb Jeff Cody
                goto exit;
924 059e2fbb Jeff Cody
                break;
925 059e2fbb Jeff Cody
            }
926 059e2fbb Jeff Cody
            nb_sectors -= sinfo.sectors_avail;
927 059e2fbb Jeff Cody
            sector_num += sinfo.sectors_avail;
928 059e2fbb Jeff Cody
            bytes_done += sinfo.bytes_avail;
929 059e2fbb Jeff Cody
        }
930 059e2fbb Jeff Cody
    }
931 059e2fbb Jeff Cody
    ret = 0;
932 059e2fbb Jeff Cody
exit:
933 059e2fbb Jeff Cody
    qemu_co_mutex_unlock(&s->lock);
934 059e2fbb Jeff Cody
    qemu_iovec_destroy(&hd_qiov);
935 059e2fbb Jeff Cody
    return ret;
936 e8d4e5ff Jeff Cody
}
937 e8d4e5ff Jeff Cody
938 e8d4e5ff Jeff Cody
939 e8d4e5ff Jeff Cody
940 e8d4e5ff Jeff Cody
static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
941 e8d4e5ff Jeff Cody
                                      int nb_sectors, QEMUIOVector *qiov)
942 e8d4e5ff Jeff Cody
{
943 e8d4e5ff Jeff Cody
    return -ENOTSUP;
944 e8d4e5ff Jeff Cody
}
945 e8d4e5ff Jeff Cody
946 e8d4e5ff Jeff Cody
947 e8d4e5ff Jeff Cody
static void vhdx_close(BlockDriverState *bs)
948 e8d4e5ff Jeff Cody
{
949 e8d4e5ff Jeff Cody
    BDRVVHDXState *s = bs->opaque;
950 e8d4e5ff Jeff Cody
    qemu_vfree(s->headers[0]);
951 e8d4e5ff Jeff Cody
    qemu_vfree(s->headers[1]);
952 e8d4e5ff Jeff Cody
    qemu_vfree(s->bat);
953 e8d4e5ff Jeff Cody
    qemu_vfree(s->parent_entries);
954 e8d4e5ff Jeff Cody
}
955 e8d4e5ff Jeff Cody
956 e8d4e5ff Jeff Cody
static BlockDriver bdrv_vhdx = {
957 e8d4e5ff Jeff Cody
    .format_name            = "vhdx",
958 e8d4e5ff Jeff Cody
    .instance_size          = sizeof(BDRVVHDXState),
959 e8d4e5ff Jeff Cody
    .bdrv_probe             = vhdx_probe,
960 e8d4e5ff Jeff Cody
    .bdrv_open              = vhdx_open,
961 e8d4e5ff Jeff Cody
    .bdrv_close             = vhdx_close,
962 e8d4e5ff Jeff Cody
    .bdrv_reopen_prepare    = vhdx_reopen_prepare,
963 e8d4e5ff Jeff Cody
    .bdrv_co_readv          = vhdx_co_readv,
964 e8d4e5ff Jeff Cody
    .bdrv_co_writev         = vhdx_co_writev,
965 e8d4e5ff Jeff Cody
};
966 e8d4e5ff Jeff Cody
967 e8d4e5ff Jeff Cody
static void bdrv_vhdx_init(void)
968 e8d4e5ff Jeff Cody
{
969 e8d4e5ff Jeff Cody
    bdrv_register(&bdrv_vhdx);
970 e8d4e5ff Jeff Cody
}
971 e8d4e5ff Jeff Cody
972 e8d4e5ff Jeff Cody
block_init(bdrv_vhdx_init);