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