xref: /freebsd/usr.bin/mkimg/vhdx.c (revision 315ee00f)
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
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
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
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
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
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
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
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
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