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