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