2 * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/errno.h>
39 #define PAYLOAD_BLOCK_SIZE (16*1024*1024)
40 #define SIZE_64KB (64*1024)
41 #define SIZE_1MB (1024*1024)
43 #define META_IS_REQUIRED (1 << 2)
44 #define META_IS_VIRTUAL_DISK (1 << 1)
46 #define PAYLOAD_BLOCK_FULLY_PRESENT 6
47 #define SB_BLOCK_NOT_PRESENT 0
48 #define BAT_ENTRY(offset, flags) (((offset) << 20) | (flags))
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;
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}}
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;
76 struct vhdx_filetype_identifier {
78 #define VHDX_FILETYPE_ID_SIGNATURE 0x656C696678646876
84 #define VHDX_HEADER_SIGNATURE 0x64616568
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;
94 uint8_t _reserved[4016];
97 struct vhdx_region_table_header {
99 #define VHDX_REGION_TABLE_HEADER_SIGNATURE 0x69676572
101 uint32_t entry_count;
105 struct vhdx_region_table_entry {
107 uint64_t file_offset;
112 struct vhdx_metadata_table_header {
114 #define VHDX_METADATA_TABLE_HEADER_SIGNATURE 0x617461646174656D
116 uint16_t entry_count;
117 uint8_t _reserved2[20];
120 struct vhdx_metadata_table_entry {
121 mkimg_uuid_t item_id;
128 #define CRC32C(c, d) (c = (c>>8) ^ crc_c[(c^(d))&0xFF])
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
198 crc32c(const void *data, uint32_t len)
201 const uint8_t *buf = (const uint8_t *)data;
204 for (i = 0; i < len; i++)
211 vhdx_resize(lba_t imgsz)
215 imagesz = imgsz * secsz;
216 imagesz = (imagesz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1);
217 return (image_set_size(imagesz / secsz));
221 vhdx_write_and_pad(int fd, const void *data, size_t data_size, size_t align)
225 if (sparse_write(fd, data, data_size) < 0)
228 if (data_size % align == 0)
231 pad_size = align - (data_size % align);
232 return image_copyout_zeroes(fd, pad_size);
236 vhdx_write_headers(int fd)
239 struct vhdx_header header;
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);
256 /* Write header 2, and make it active */
257 le32enc(&header.sequence_number, 1);
259 checksum = crc32c(&header, sizeof(header));
260 le32enc(&header.checksum, checksum);
261 return vhdx_write_and_pad(fd, &header, sizeof(header), SIZE_64KB);
265 vhdx_write_region_tables(int fd)
268 uint8_t *region_table;
269 struct vhdx_region_table_header header;
270 struct vhdx_region_table_entry entry;
273 region_table = malloc(SIZE_64KB);
274 if (region_table == NULL)
276 memset(region_table, 0, SIZE_64KB);
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));
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));
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));
297 checksum = crc32c(region_table, SIZE_64KB);
298 le32enc(region_table + 4, checksum);
301 if (sparse_write(fd, region_table, SIZE_64KB) < 0) {
308 if (sparse_write(fd, region_table, SIZE_64KB) < 0) {
319 vhdx_write_metadata(int fd, uint64_t image_size)
323 struct vhdx_metadata_table_header header;
324 struct vhdx_metadata_table_entry entry;
325 int header_ptr, data_ptr;
328 metadata = malloc(SIZE_1MB);
329 if (metadata == NULL)
331 memset(metadata, 0, SIZE_1MB);
333 memset(&header, 0, sizeof(header));
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;
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);
350 le32enc(metadata + data_ptr, 0);
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);
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);
371 mkimg_uuid_enc(metadata + data_ptr, &id);
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);
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);
394 if (sparse_write(fd, metadata, SIZE_1MB) < 0) {
405 vhdx_write_bat(int fd, uint64_t image_size)
410 uint64_t bat_size, data_block_count, total_bat_entries;
411 uint64_t idx, payload_offset, bat_ptr;
413 bat = malloc(SIZE_1MB);
416 memset(bat, 0, SIZE_1MB);
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);
426 * Offset to the first payload block
427 * 1Mb of header + 1Mb of log + 1Mb of metadata + XMb BAT
429 payload_offset = 3 + (bat_size / SIZE_1MB);
431 for (idx = 0; idx < data_block_count; idx++) {
432 le64enc(bat + bat_ptr,
433 BAT_ENTRY(payload_offset, PAYLOAD_BLOCK_FULLY_PRESENT));
435 payload_offset += (PAYLOAD_BLOCK_SIZE / SIZE_1MB);
437 /* Flush the BAT buffer if required */
438 if (bat_ptr == SIZE_1MB) {
439 if (sparse_write(fd, bat, SIZE_1MB) < 0) {
444 memset(bat, 0, SIZE_1MB);
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));
454 /* Flush the BAT buffer if required */
455 if (bat_ptr == SIZE_1MB) {
456 if (sparse_write(fd, bat, SIZE_1MB) < 0) {
461 memset(bat, 0, SIZE_1MB);
468 if (sparse_write(fd, bat, SIZE_1MB) < 0) {
483 uint64_t imgsz, rawsz;
484 struct vhdx_filetype_identifier identifier;
486 rawsz = image_get_size() * secsz;
487 imgsz = (rawsz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1);
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);
495 error = vhdx_write_headers(fd);
500 error = vhdx_write_region_tables(fd);
505 error = image_copyout_zeroes(fd, SIZE_1MB - 5*SIZE_64KB);
508 error = image_copyout_zeroes(fd, SIZE_1MB);
512 error = vhdx_write_metadata(fd, imgsz);
516 error = vhdx_write_bat(fd, imgsz);
520 error = image_copyout(fd);
527 static struct mkimg_format vhdx_format = {
529 .description = "Virtual Hard Disk, version 2",
530 .resize = vhdx_resize,
534 FORMAT_DEFINE(vhdx_format);