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 memset(&entry, 0, sizeof(entry)); 335 336 le64enc(&header.signature, VHDX_METADATA_TABLE_HEADER_SIGNATURE); 337 le16enc(&header.entry_count, 5); 338 memcpy(metadata, &header, sizeof(header)); 339 header_ptr = sizeof(header); 340 data_ptr = SIZE_64KB; 341 342 /* File parameters */ 343 mkimg_uuid_enc(&entry.item_id, &vhdx_meta_file_parameters_guid); 344 le32enc(&entry.offset, data_ptr); 345 le32enc(&entry.length, 8); 346 le32enc(&entry.flags, META_IS_REQUIRED); 347 memcpy(metadata + header_ptr, &entry, sizeof(entry)); 348 header_ptr += sizeof(entry); 349 le32enc(metadata + data_ptr, PAYLOAD_BLOCK_SIZE); 350 data_ptr += 4; 351 le32enc(metadata + data_ptr, 0); 352 data_ptr += 4; 353 354 /* Virtual Disk Size */ 355 mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_size_guid); 356 le32enc(&entry.offset, data_ptr); 357 le32enc(&entry.length, 8); 358 le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK); 359 memcpy(metadata + header_ptr, &entry, sizeof(entry)); 360 header_ptr += sizeof(entry); 361 le64enc(metadata + data_ptr, image_size); 362 data_ptr += 8; 363 364 /* Virtual Disk ID */ 365 mkimg_uuid_enc(&entry.item_id, &vhdx_meta_vdisk_id_guid); 366 le32enc(&entry.offset, data_ptr); 367 le32enc(&entry.length, 16); 368 le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK); 369 memcpy(metadata + header_ptr, &entry, sizeof(entry)); 370 header_ptr += sizeof(entry); 371 mkimg_uuid(&id); 372 mkimg_uuid_enc(metadata + data_ptr, &id); 373 data_ptr += 16; 374 375 /* Logical Sector Size*/ 376 mkimg_uuid_enc(&entry.item_id, &vhdx_meta_logical_ssize_guid); 377 le32enc(&entry.offset, data_ptr); 378 le32enc(&entry.length, 4); 379 le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK); 380 memcpy(metadata + header_ptr, &entry, sizeof(entry)); 381 header_ptr += sizeof(entry); 382 le32enc(metadata + data_ptr, secsz); 383 data_ptr += 4; 384 385 /* Physical Sector Size*/ 386 mkimg_uuid_enc(&entry.item_id, &vhdx_meta_phys_ssize_guid); 387 le32enc(&entry.offset, data_ptr); 388 le32enc(&entry.length, 4); 389 le32enc(&entry.flags, META_IS_REQUIRED | META_IS_VIRTUAL_DISK); 390 memcpy(metadata + header_ptr, &entry, sizeof(entry)); 391 header_ptr += sizeof(entry); 392 le32enc(metadata + data_ptr, blksz); 393 data_ptr += 4; 394 395 if (sparse_write(fd, metadata, SIZE_1MB) < 0) { 396 error = errno; 397 free(metadata); 398 return error; 399 } 400 401 free(metadata); 402 return (0); 403 } 404 405 static int 406 vhdx_write_bat(int fd, uint64_t image_size) 407 { 408 int error; 409 uint8_t *bat; 410 int chunk_ratio; 411 uint64_t bat_size, data_block_count, total_bat_entries; 412 uint64_t idx, payload_offset, bat_ptr; 413 414 bat = malloc(SIZE_1MB); 415 if (bat == NULL) 416 return errno; 417 memset(bat, 0, SIZE_1MB); 418 419 chunk_ratio = ((1024*1024*8ULL) * secsz) / PAYLOAD_BLOCK_SIZE; 420 data_block_count = (image_size + PAYLOAD_BLOCK_SIZE - 1) / PAYLOAD_BLOCK_SIZE; 421 total_bat_entries = data_block_count + (data_block_count - 1)/chunk_ratio; 422 bat_size = total_bat_entries * 8; 423 /* round it up to 1Mb */ 424 bat_size = (bat_size + SIZE_1MB - 1) & ~(SIZE_1MB - 1); 425 426 /* 427 * Offset to the first payload block 428 * 1Mb of header + 1Mb of log + 1Mb of metadata + XMb BAT 429 */ 430 payload_offset = 3 + (bat_size / SIZE_1MB); 431 bat_ptr = 0; 432 for (idx = 0; idx < data_block_count; idx++) { 433 le64enc(bat + bat_ptr, 434 BAT_ENTRY(payload_offset, PAYLOAD_BLOCK_FULLY_PRESENT)); 435 bat_ptr += 8; 436 payload_offset += (PAYLOAD_BLOCK_SIZE / SIZE_1MB); 437 438 /* Flush the BAT buffer if required */ 439 if (bat_ptr == SIZE_1MB) { 440 if (sparse_write(fd, bat, SIZE_1MB) < 0) { 441 error = errno; 442 free(bat); 443 return error; 444 } 445 memset(bat, 0, SIZE_1MB); 446 bat_ptr = 0; 447 } 448 449 if (((idx + 1) % chunk_ratio) == 0 && 450 (idx != data_block_count - 1)) { 451 le64enc(bat + bat_ptr, 452 BAT_ENTRY(0, SB_BLOCK_NOT_PRESENT)); 453 bat_ptr += 8; 454 455 /* Flush the BAT buffer if required */ 456 if (bat_ptr == SIZE_1MB) { 457 if (sparse_write(fd, bat, SIZE_1MB) < 0) { 458 error = errno; 459 free(bat); 460 return error; 461 } 462 memset(bat, 0, SIZE_1MB); 463 bat_ptr = 0; 464 } 465 } 466 } 467 468 if (bat_ptr != 0) { 469 if (sparse_write(fd, bat, SIZE_1MB) < 0) { 470 error = errno; 471 free(bat); 472 return error; 473 } 474 } 475 476 free(bat); 477 return (0); 478 } 479 480 static int 481 vhdx_write(int fd) 482 { 483 int error; 484 uint64_t imgsz, rawsz; 485 struct vhdx_filetype_identifier identifier; 486 487 rawsz = image_get_size() * secsz; 488 imgsz = (rawsz + PAYLOAD_BLOCK_SIZE - 1) & ~(PAYLOAD_BLOCK_SIZE - 1); 489 490 memset(&identifier, 0, sizeof(identifier)); 491 le64enc(&identifier.signature, VHDX_FILETYPE_ID_SIGNATURE); 492 error = vhdx_write_and_pad(fd, &identifier, sizeof(identifier), SIZE_64KB); 493 if (error) 494 return (error); 495 496 error = vhdx_write_headers(fd); 497 if (error) 498 return (error); 499 500 501 error = vhdx_write_region_tables(fd); 502 if (error) 503 return (error); 504 505 /* Reserved area */ 506 error = image_copyout_zeroes(fd, SIZE_1MB - 5*SIZE_64KB); 507 508 /* Log */ 509 error = image_copyout_zeroes(fd, SIZE_1MB); 510 if (error) 511 return (error); 512 513 error = vhdx_write_metadata(fd, imgsz); 514 if (error) 515 return (error); 516 517 error = vhdx_write_bat(fd, imgsz); 518 if (error) 519 return (error); 520 521 error = image_copyout(fd); 522 if (error) 523 return (error); 524 525 return (0); 526 } 527 528 static struct mkimg_format vhdx_format = { 529 .name = "vhdx", 530 .description = "Virtual Hard Disk, version 2", 531 .resize = vhdx_resize, 532 .write = vhdx_write, 533 }; 534 535 FORMAT_DEFINE(vhdx_format); 536