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}}
53static mkimg_uuid_t vhdx_bat_guid = VHDX_REGION_BAT_GUID;
54static 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
68static mkimg_uuid_t vhdx_meta_file_parameters_guid = VHDX_META_FILE_PARAMETERS;
69static mkimg_uuid_t vhdx_meta_vdisk_size_guid = VHDX_META_VDISK_SIZE;
70static mkimg_uuid_t vhdx_meta_vdisk_id_guid = VHDX_META_VDISK_ID;
71static mkimg_uuid_t vhdx_meta_logical_ssize_guid = VHDX_META_LOGICAL_SSIZE;
72static mkimg_uuid_t vhdx_meta_phys_ssize_guid = VHDX_META_PHYS_SSIZE;
73
74struct vhdx_filetype_identifier {
75	uint64_t	signature;
76#define	VHDX_FILETYPE_ID_SIGNATURE	0x656C696678646876
77	uint8_t		creator[512];
78};
79
80struct 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
95struct 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
103struct vhdx_region_table_entry {
104	mkimg_uuid_t	guid;
105	uint64_t	file_offset;
106	uint32_t	length;
107	uint32_t	required;
108};
109
110struct 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
118struct 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
128static 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
195static uint32_t
196crc32c(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
208static int
209vhdx_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
218static int
219vhdx_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
233static int
234vhdx_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
262static int
263vhdx_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
316static int
317vhdx_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
403static int
404vhdx_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
478static int
479vhdx_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
526static 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
533FORMAT_DEFINE(vhdx_format);
534