xref: /freebsd/usr.bin/mkimg/vhdx.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
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 	memset(&entry, 0, sizeof(entry));
335 
336 	le64enc(&header.signature, VHDX_METADATA_TABLE_HEADER_SIGNATURE);
337 	le16enc(&header.entry_count, 5);
338 	memcpy(metadata, &header, sizeof(header));
339 	header_ptr = sizeof(header);
340 	data_ptr = SIZE_64KB;
341 
342 	/* File parameters */
343 	mkimg_uuid_enc(&entry.item_id, &vhdx_meta_file_parameters_guid);
344 	le32enc(&entry.offset, data_ptr);
345 	le32enc(&entry.length, 8);
346 	le32enc(&entry.flags, META_IS_REQUIRED);
347 	memcpy(metadata + header_ptr, &entry, sizeof(entry));
348 	header_ptr += sizeof(entry);
349 	le32enc(metadata + data_ptr, PAYLOAD_BLOCK_SIZE);
350 	data_ptr += 4;
351 	le32enc(metadata + data_ptr, 0);
352 	data_ptr += 4;
353 
354 	/* Virtual Disk Size */
355 	mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_size_guid);
356 	le32enc(&entry.offset, data_ptr);
357 	le32enc(&entry.length, 8);
358 	le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
359 	memcpy(metadata + header_ptr, &entry, sizeof(entry));
360 	header_ptr += sizeof(entry);
361 	le64enc(metadata + data_ptr, image_size);
362 	data_ptr += 8;
363 
364 	/* Virtual Disk ID */
365 	mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_id_guid);
366 	le32enc(&entry.offset, data_ptr);
367 	le32enc(&entry.length, 16);
368 	le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
369 	memcpy(metadata + header_ptr, &entry, sizeof(entry));
370 	header_ptr += sizeof(entry);
371 	mkimg_uuid(&id);
372 	mkimg_uuid_enc(metadata + data_ptr, &id);
373 	data_ptr += 16;
374 
375 	/* Logical Sector Size*/
376 	mkimg_uuid_enc(&entry.item_id, &vhdx_meta_logical_ssize_guid);
377 	le32enc(&entry.offset, data_ptr);
378 	le32enc(&entry.length, 4);
379 	le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
380 	memcpy(metadata + header_ptr, &entry, sizeof(entry));
381 	header_ptr += sizeof(entry);
382 	le32enc(metadata + data_ptr, secsz);
383 	data_ptr += 4;
384 
385 	/* Physical Sector Size*/
386 	mkimg_uuid_enc(&entry.item_id, &vhdx_meta_phys_ssize_guid);
387 	le32enc(&entry.offset, data_ptr);
388 	le32enc(&entry.length, 4);
389 	le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK);
390 	memcpy(metadata + header_ptr, &entry, sizeof(entry));
391 	header_ptr += sizeof(entry);
392 	le32enc(metadata + data_ptr, blksz);
393 	data_ptr += 4;
394 
395 	if (sparse_write(fd, metadata, SIZE_1MB) < 0) {
396 		error = errno;
397 		free(metadata);
398 		return error;
399 	}
400 
401 	free(metadata);
402 	return (0);
403 }
404 
405 static int
406 vhdx_write_bat(int fd, uint64_t image_size)
407 {
408 	int error;
409 	uint8_t *bat;
410 	int chunk_ratio;
411 	uint64_t bat_size, data_block_count, total_bat_entries;
412 	uint64_t idx, payload_offset, bat_ptr;
413 
414 	bat = malloc(SIZE_1MB);
415 	if (bat == NULL)
416 		return errno;
417 	memset(bat, 0, SIZE_1MB);
418 
419 	chunk_ratio = ((1024*1024*8ULL) * secsz) / PAYLOAD_BLOCK_SIZE;
420 	data_block_count = (image_size + PAYLOAD_BLOCK_SIZE - 1) / PAYLOAD_BLOCK_SIZE;
421 	total_bat_entries = data_block_count + (data_block_count - 1)/chunk_ratio;
422 	bat_size = total_bat_entries * 8;
423 	/* round it up to 1Mb */
424 	bat_size = (bat_size + SIZE_1MB - 1) & ~(SIZE_1MB - 1);
425 
426 	/*
427 	 * Offset to the first payload block
428 	 * 1Mb of header + 1Mb of log + 1Mb of metadata + XMb BAT
429 	 */
430 	payload_offset = 3 + (bat_size / SIZE_1MB);
431 	bat_ptr = 0;
432 	for (idx = 0; idx < data_block_count; idx++) {
433 		le64enc(bat + bat_ptr,
434 		    BAT_ENTRY(payload_offset, PAYLOAD_BLOCK_FULLY_PRESENT));
435 		bat_ptr += 8;
436 		payload_offset += (PAYLOAD_BLOCK_SIZE / SIZE_1MB);
437 
438 		/* Flush the BAT buffer if required */
439 		if (bat_ptr == SIZE_1MB) {
440 			if (sparse_write(fd, bat, SIZE_1MB) < 0) {
441 				error = errno;
442 				free(bat);
443 				return error;
444 			}
445 			memset(bat, 0, SIZE_1MB);
446 			bat_ptr = 0;
447 		}
448 
449 		if (((idx + 1) % chunk_ratio) == 0 &&
450 		    (idx != data_block_count - 1)) {
451 			le64enc(bat + bat_ptr,
452 			    BAT_ENTRY(0, SB_BLOCK_NOT_PRESENT));
453 			bat_ptr += 8;
454 
455 			/* Flush the BAT buffer if required */
456 			if (bat_ptr == SIZE_1MB) {
457 				if (sparse_write(fd, bat, SIZE_1MB) < 0) {
458 					error = errno;
459 					free(bat);
460 					return error;
461 				}
462 				memset(bat, 0, SIZE_1MB);
463 				bat_ptr = 0;
464 			}
465 		}
466 	}
467 
468 	if (bat_ptr != 0) {
469 		if (sparse_write(fd, bat, SIZE_1MB) < 0) {
470 			error = errno;
471 			free(bat);
472 			return error;
473 		}
474 	}
475 
476 	free(bat);
477 	return (0);
478 }
479 
480 static int
481 vhdx_write(int fd)
482 {
483 	int error;
484 	uint64_t imgsz, rawsz;
485 	struct vhdx_filetype_identifier identifier;
486 
487 	rawsz = image_get_size() * secsz;
488 	imgsz = (rawsz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1);
489 
490 	memset(&identifier, 0, sizeof(identifier));
491 	le64enc(&identifier.signature, VHDX_FILETYPE_ID_SIGNATURE);
492 	error = vhdx_write_and_pad(fd, &identifier, sizeof(identifier), SIZE_64KB);
493 	if (error)
494 		return (error);
495 
496 	error = vhdx_write_headers(fd);
497 	if (error)
498 		return (error);
499 
500 
501 	error = vhdx_write_region_tables(fd);
502 	if (error)
503 		return (error);
504 
505 	/* Reserved area */
506 	error = image_copyout_zeroes(fd, SIZE_1MB - 5*SIZE_64KB);
507 
508 	/* Log */
509 	error = image_copyout_zeroes(fd, SIZE_1MB);
510 	if (error)
511 		return (error);
512 
513 	error = vhdx_write_metadata(fd, imgsz);
514 	if (error)
515 		return (error);
516 
517 	error = vhdx_write_bat(fd, imgsz);
518 	if (error)
519 		return (error);
520 
521 	error = image_copyout(fd);
522 	if (error)
523 		return (error);
524 
525 	return (0);
526 }
527 
528 static struct mkimg_format vhdx_format = {
529 	.name = "vhdx",
530 	.description = "Virtual Hard Disk, version 2",
531 	.resize = vhdx_resize,
532 	.write = vhdx_write,
533 };
534 
535 FORMAT_DEFINE(vhdx_format);
536