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