]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mkimg/vhdx.c
bluetooth: Fix a mandoc related issues
[FreeBSD/FreeBSD.git] / usr.bin / mkimg / vhdx.c
1 /*-
2  * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #include "endian.h"
35 #include "image.h"
36 #include "format.h"
37 #include "mkimg.h"
38
39 #define PAYLOAD_BLOCK_SIZE      (16*1024*1024)
40 #define SIZE_64KB               (64*1024)
41 #define SIZE_1MB                (1024*1024)
42
43 #define META_IS_REQUIRED        (1 << 2)
44 #define META_IS_VIRTUAL_DISK    (1 << 1)
45
46 #define PAYLOAD_BLOCK_FULLY_PRESENT     6
47 #define SB_BLOCK_NOT_PRESENT            0
48 #define BAT_ENTRY(offset, flags)        (((offset) << 20) | (flags))
49
50 /* Regions' UUIDs */
51 #define VHDX_REGION_BAT_GUID    \
52         {0x2dc27766,0xf623,0x4200,0x9d,0x64,{0x11,0x5e,0x9b,0xfd,0x4a,0x08}}
53 #define VHDX_REGION_METADATA_GUID       \
54         {0x8b7ca206,0x4790,0x4b9a,0xb8,0xfe,{0x57,0x5f,0x05,0x0f,0x88,0x6e}}
55 static mkimg_uuid_t vhdx_bat_guid = VHDX_REGION_BAT_GUID;
56 static mkimg_uuid_t vhdx_metadata_guid = VHDX_REGION_METADATA_GUID;
57
58 /* Metadata UUIDs */
59 #define VHDX_META_FILE_PARAMETERS       \
60         {0xcaa16737,0xfa36,0x4d43,0xb3,0xb6,{0x33,0xf0,0xaa,0x44,0xe7,0x6b}}
61 #define VHDX_META_VDISK_SIZE    \
62         {0x2fa54224,0xcd1b,0x4876,0xb2,0x11,{0x5d,0xbe,0xd8,0x3b,0xf4,0xb8}}
63 #define VHDX_META_VDISK_ID      \
64         {0xbeca12ab,0xb2e6,0x4523,0x93,0xef,{0xc3,0x09,0xe0,0x00,0xc7,0x46}}
65 #define VHDX_META_LOGICAL_SSIZE \
66         {0x8141bf1D,0xa96f,0x4709,0xba,0x47,{0xf2,0x33,0xa8,0xfa,0xab,0x5f}}
67 #define VHDX_META_PHYS_SSIZE    \
68         {0xcda348c7,0x445d,0x4471,0x9c,0xc9,{0xe9,0x88,0x52,0x51,0xc5,0x56}}
69
70 static mkimg_uuid_t vhdx_meta_file_parameters_guid = VHDX_META_FILE_PARAMETERS;
71 static mkimg_uuid_t vhdx_meta_vdisk_size_guid = VHDX_META_VDISK_SIZE;
72 static mkimg_uuid_t vhdx_meta_vdisk_id_guid = VHDX_META_VDISK_ID;
73 static mkimg_uuid_t vhdx_meta_logical_ssize_guid = VHDX_META_LOGICAL_SSIZE;
74 static mkimg_uuid_t vhdx_meta_phys_ssize_guid = VHDX_META_PHYS_SSIZE;
75
76 struct vhdx_filetype_identifier {
77         uint64_t        signature;
78 #define VHDX_FILETYPE_ID_SIGNATURE      0x656C696678646876
79         uint8_t         creator[512];
80 };
81
82 struct vhdx_header {
83         uint32_t        signature;
84 #define VHDX_HEADER_SIGNATURE           0x64616568
85         uint32_t        checksum;
86         uint64_t        sequence_number;
87         mkimg_uuid_t    file_write_guid;
88         mkimg_uuid_t    data_write_guid;
89         mkimg_uuid_t    log_guid;
90         uint16_t        log_version;
91         uint16_t        version;
92         uint32_t        log_length;
93         uint64_t        log_offset;
94         uint8_t         _reserved[4016];
95 };
96
97 struct vhdx_region_table_header {
98         uint32_t        signature;
99 #define VHDX_REGION_TABLE_HEADER_SIGNATURE      0x69676572
100         uint32_t        checksum;
101         uint32_t        entry_count;
102         uint32_t        _reserved;
103 };
104
105 struct vhdx_region_table_entry {
106         mkimg_uuid_t    guid;
107         uint64_t        file_offset;
108         uint32_t        length;
109         uint32_t        required;
110 };
111
112 struct vhdx_metadata_table_header {
113         uint64_t        signature;
114 #define VHDX_METADATA_TABLE_HEADER_SIGNATURE            0x617461646174656D
115         uint16_t        _reserved;
116         uint16_t        entry_count;
117         uint8_t         _reserved2[20];
118 };
119
120 struct vhdx_metadata_table_entry {
121         mkimg_uuid_t    item_id;
122         uint32_t        offset;
123         uint32_t        length;
124         uint32_t        flags;
125         uint32_t        _reserved;
126 };
127
128 #define CRC32C(c, d) (c = (c>>8) ^ crc_c[(c^(d))&0xFF])
129
130 static uint32_t crc_c[256] = {
131         0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
132         0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
133         0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
134         0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
135         0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
136         0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
137         0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
138         0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
139         0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
140         0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
141         0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
142         0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
143         0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
144         0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
145         0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
146         0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
147         0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
148         0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
149         0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
150         0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
151         0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
152         0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
153         0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
154         0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
155         0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
156         0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
157         0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
158         0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
159         0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
160         0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
161         0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
162         0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
163         0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
164         0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
165         0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
166         0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
167         0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
168         0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
169         0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
170         0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
171         0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
172         0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
173         0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
174         0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
175         0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
176         0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
177         0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
178         0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
179         0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
180         0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
181         0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
182         0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
183         0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
184         0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
185         0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
186         0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
187         0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
188         0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
189         0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
190         0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
191         0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
192         0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
193         0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
194         0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
195 };
196
197 static uint32_t
198 crc32c(const void *data, uint32_t len)
199 {
200         uint32_t i, crc;
201         const uint8_t *buf = (const uint8_t *)data;
202
203         crc = ~0;
204         for (i = 0; i < len; i++)
205                 CRC32C(crc, buf[i]);
206         crc = ~crc;
207         return crc;
208 }
209
210 static int
211 vhdx_resize(lba_t imgsz)
212 {
213         uint64_t imagesz;
214
215         imagesz = imgsz * secsz;
216         imagesz = (imagesz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1);
217         return (image_set_size(imagesz / secsz));
218 }
219
220 static int
221 vhdx_write_and_pad(int fd, const void *data, size_t data_size, size_t align)
222 {
223         size_t pad_size;
224
225         if (sparse_write(fd, data, data_size) < 0)
226                 return (errno);
227
228         if (data_size % align == 0)
229                 return (0);
230
231         pad_size = align - (data_size % align);
232         return  image_copyout_zeroes(fd, pad_size);
233 }
234
235 static int
236 vhdx_write_headers(int fd)
237 {
238         int error;
239         struct vhdx_header header;
240         uint32_t checksum;
241
242         /* Write header 1 */
243         memset(&header, 0, sizeof(header));
244         le32enc(&header.signature, VHDX_HEADER_SIGNATURE);
245         le32enc(&header.sequence_number, 0);
246         le16enc(&header.log_version, 0);
247         le16enc(&header.version, 1);
248         le32enc(&header.log_length, SIZE_1MB);
249         le64enc(&header.log_offset, SIZE_1MB);
250         checksum = crc32c(&header, sizeof(header));
251         le32enc(&header.checksum, checksum);
252         error = vhdx_write_and_pad(fd, &header, sizeof(header), SIZE_64KB);
253         if (error)
254                 return (error);
255
256         /* Write header 2, and make it active */
257         le32enc(&header.sequence_number, 1);
258         header.checksum = 0;
259         checksum = crc32c(&header, sizeof(header));
260         le32enc(&header.checksum, checksum);
261         return vhdx_write_and_pad(fd, &header, sizeof(header), SIZE_64KB);
262 }
263
264 static int
265 vhdx_write_region_tables(int fd)
266 {
267         int error;
268         uint8_t *region_table;
269         struct vhdx_region_table_header header;
270         struct vhdx_region_table_entry entry;
271         uint32_t checksum;
272
273         region_table = malloc(SIZE_64KB);
274         if (region_table == NULL)
275                 return errno;
276         memset(region_table, 0, SIZE_64KB);
277
278         memset(&header, 0, sizeof(header));
279         le32enc(&header.signature, VHDX_REGION_TABLE_HEADER_SIGNATURE);
280         le32enc(&header.entry_count, 2);
281         memcpy(region_table, &header, sizeof(header));
282
283         /* Metadata region entry */
284         mkimg_uuid_enc(&entry.guid, &vhdx_metadata_guid);
285         le64enc(&entry.file_offset, 2*SIZE_1MB);
286         le64enc(&entry.length, SIZE_1MB);
287         memcpy(region_table + sizeof(header),
288             &entry, sizeof(entry));
289
290         /* BAT region entry */
291         mkimg_uuid_enc(&entry.guid, &vhdx_bat_guid);
292         le64enc(&entry.file_offset, 3*SIZE_1MB);
293         le64enc(&entry.length, SIZE_1MB);
294         memcpy(region_table + sizeof(header) + sizeof(entry),
295             &entry, sizeof(entry));
296
297         checksum = crc32c(region_table, SIZE_64KB);
298         le32enc(region_table + 4, checksum);
299
300         /* Region Table 1 */
301         if (sparse_write(fd, region_table, SIZE_64KB) < 0) {
302                 error = errno;
303                 free(region_table);
304                 return error;
305         }
306
307         /* Region Table 2 */
308         if (sparse_write(fd, region_table, SIZE_64KB) < 0) {
309                 error = errno;
310                 free(region_table);
311                 return error;
312         }
313
314         free(region_table);
315         return (0);
316 }
317
318 static int
319 vhdx_write_metadata(int fd, uint64_t image_size)
320 {
321         int error;
322         uint8_t *metadata;
323         struct vhdx_metadata_table_header header;
324         struct vhdx_metadata_table_entry entry;
325         int header_ptr, data_ptr;
326         mkimg_uuid_t id;
327
328         metadata = malloc(SIZE_1MB);
329         if (metadata == NULL)
330                 return errno;
331         memset(metadata, 0, SIZE_1MB);
332
333         memset(&header, 0, sizeof(header));
334
335         le64enc(&header.signature, VHDX_METADATA_TABLE_HEADER_SIGNATURE);
336         le16enc(&header.entry_count, 5);
337         memcpy(metadata, &header, sizeof(header));
338         header_ptr = sizeof(header);
339         data_ptr = SIZE_64KB;
340
341         /* File parameters */
342         mkimg_uuid_enc(&entry.item_id, &vhdx_meta_file_parameters_guid);
343         le32enc(&entry.offset, data_ptr);
344         le32enc(&entry.length, 8);
345         le32enc(&entry.flags, META_IS_REQUIRED);
346         memcpy(metadata + header_ptr, &entry, sizeof(entry));
347         header_ptr += sizeof(entry);
348         le32enc(metadata + data_ptr, PAYLOAD_BLOCK_SIZE);
349         data_ptr += 4;
350         le32enc(metadata + data_ptr, 0);
351         data_ptr += 4;
352
353         /* Virtual Disk Size */
354         mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_size_guid);
355         le32enc(&entry.offset, data_ptr);
356         le32enc(&entry.length, 8);
357         le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
358         memcpy(metadata + header_ptr, &entry, sizeof(entry));
359         header_ptr += sizeof(entry);
360         le64enc(metadata + data_ptr, image_size);
361         data_ptr += 8;
362
363         /* Virtual Disk ID */
364         mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_id_guid);
365         le32enc(&entry.offset, data_ptr);
366         le32enc(&entry.length, 16);
367         le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
368         memcpy(metadata + header_ptr, &entry, sizeof(entry));
369         header_ptr += sizeof(entry);
370         mkimg_uuid(&id);
371         mkimg_uuid_enc(metadata + data_ptr, &id);
372         data_ptr += 16;
373
374         /* Logical Sector Size*/
375         mkimg_uuid_enc(&entry.item_id, &vhdx_meta_logical_ssize_guid);
376         le32enc(&entry.offset, data_ptr);
377         le32enc(&entry.length, 4);
378         le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
379         memcpy(metadata + header_ptr, &entry, sizeof(entry));
380         header_ptr += sizeof(entry);
381         le32enc(metadata + data_ptr, secsz);
382         data_ptr += 4;
383
384         /* Physical Sector Size*/
385         mkimg_uuid_enc(&entry.item_id, &vhdx_meta_phys_ssize_guid);
386         le32enc(&entry.offset, data_ptr);
387         le32enc(&entry.length, 4);
388         le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
389         memcpy(metadata + header_ptr, &entry, sizeof(entry));
390         header_ptr += sizeof(entry);
391         le32enc(metadata + data_ptr, blksz);
392         data_ptr += 4;
393
394         if (sparse_write(fd, metadata, SIZE_1MB) < 0) {
395                 error = errno;
396                 free(metadata);
397                 return error;
398         }
399
400         free(metadata);
401         return (0);
402 }
403
404 static int
405 vhdx_write_bat(int fd, uint64_t image_size)
406 {
407         int error;
408         uint8_t *bat;
409         int chunk_ratio;
410         uint64_t bat_size, data_block_count, total_bat_entries;
411         uint64_t idx, payload_offset, bat_ptr;
412
413         bat = malloc(SIZE_1MB);
414         if (bat == NULL)
415                 return errno;
416         memset(bat, 0, SIZE_1MB);
417
418         chunk_ratio = ((1024*1024*8ULL) * secsz) / PAYLOAD_BLOCK_SIZE;
419         data_block_count = (image_size + PAYLOAD_BLOCK_SIZE - 1) / PAYLOAD_BLOCK_SIZE;
420         total_bat_entries = data_block_count + (data_block_count - 1)/chunk_ratio;
421         bat_size = total_bat_entries * 8;
422         /* round it up to 1Mb */
423         bat_size = (bat_size + SIZE_1MB - 1) & ~(SIZE_1MB - 1);
424
425         /*
426          * Offset to the first payload block
427          * 1Mb of header + 1Mb of log + 1Mb of metadata + XMb BAT
428          */
429         payload_offset = 3 + (bat_size / SIZE_1MB);
430         bat_ptr = 0;
431         for (idx = 0; idx < data_block_count; idx++) {
432                 le64enc(bat + bat_ptr,
433                     BAT_ENTRY(payload_offset, PAYLOAD_BLOCK_FULLY_PRESENT));
434                 bat_ptr += 8;
435                 payload_offset += (PAYLOAD_BLOCK_SIZE / SIZE_1MB);
436
437                 /* Flush the BAT buffer if required */
438                 if (bat_ptr == SIZE_1MB) {
439                         if (sparse_write(fd, bat, SIZE_1MB) < 0) {
440                                 error = errno;
441                                 free(bat);
442                                 return error;
443                         }
444                         memset(bat, 0, SIZE_1MB);
445                         bat_ptr = 0;
446                 }
447
448                 if (((idx + 1) % chunk_ratio) == 0 &&
449                     (idx != data_block_count - 1)) {
450                         le64enc(bat + bat_ptr,
451                             BAT_ENTRY(0, SB_BLOCK_NOT_PRESENT));
452                         bat_ptr += 8;
453
454                         /* Flush the BAT buffer if required */
455                         if (bat_ptr == SIZE_1MB) {
456                                 if (sparse_write(fd, bat, SIZE_1MB) < 0) {
457                                         error = errno;
458                                         free(bat);
459                                         return error;
460                                 }
461                                 memset(bat, 0, SIZE_1MB);
462                                 bat_ptr = 0;
463                         }
464                 }
465         }
466
467         if (bat_ptr != 0) {
468                 if (sparse_write(fd, bat, SIZE_1MB) < 0) {
469                         error = errno;
470                         free(bat);
471                         return error;
472                 }
473         }
474
475         free(bat);
476         return (0);
477 }
478
479 static int
480 vhdx_write(int fd)
481 {
482         int error;
483         uint64_t imgsz, rawsz;
484         struct vhdx_filetype_identifier identifier;
485
486         rawsz = image_get_size() * secsz;
487         imgsz = (rawsz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1);
488
489         memset(&identifier, 0, sizeof(identifier));
490         le64enc(&identifier.signature, VHDX_FILETYPE_ID_SIGNATURE);
491         error = vhdx_write_and_pad(fd, &identifier, sizeof(identifier), SIZE_64KB);
492         if (error)
493                 return (error);
494
495         error = vhdx_write_headers(fd);
496         if (error)
497                 return (error);
498
499
500         error = vhdx_write_region_tables(fd);
501         if (error)
502                 return (error);
503
504         /* Reserved area */
505         error = image_copyout_zeroes(fd, SIZE_1MB - 5*SIZE_64KB);
506
507         /* Log */
508         error = image_copyout_zeroes(fd, SIZE_1MB);
509         if (error)
510                 return (error);
511
512         error = vhdx_write_metadata(fd, imgsz);
513         if (error)
514                 return (error);
515
516         error = vhdx_write_bat(fd, imgsz);
517         if (error)
518                 return (error);
519
520         error = image_copyout(fd);
521         if (error)
522                 return (error);
523
524         return (0);
525 }
526
527 static struct mkimg_format vhdx_format = {
528         .name = "vhdx",
529         .description = "Virtual Hard Disk, version 2",
530         .resize = vhdx_resize,
531         .write = vhdx_write,
532 };
533
534 FORMAT_DEFINE(vhdx_format);