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