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