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