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