1 /*-
2 * Copyright (c) 2014 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/types.h>
31 #include <sys/endian.h>
32 #include <sys/errno.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <uuid.h>
38
39 #include "image.h"
40 #include "format.h"
41 #include "mkimg.h"
42
43 #ifndef __has_extension
44 #define __has_extension(x) 0
45 #endif
46
47 /*
48 * General notes:
49 * o File is in network byte order.
50 * o The timestamp is seconds since 1/1/2000 12:00:00 AM UTC
51 *
52 * This file is divided in 3 parts:
53 * 1. Common definitions
54 * 2. Dynamic VHD support
55 * 3. Fixed VHD support
56 */
57
58 /*
59 * PART 1: Common definitions
60 */
61
62 #define VHD_SECTOR_SIZE 512
63 #define VHD_BLOCK_SIZE (4096 * VHD_SECTOR_SIZE) /* 2MB blocks */
64
65 struct vhd_footer {
66 uint64_t cookie;
67 #define VHD_FOOTER_COOKIE 0x636f6e6563746978
68 uint32_t features;
69 #define VHD_FEATURES_TEMPORARY 0x01
70 #define VHD_FEATURES_RESERVED 0x02
71 uint32_t version;
72 #define VHD_VERSION 0x00010000
73 uint64_t data_offset;
74 uint32_t timestamp;
75 uint32_t creator_tool;
76 #define VHD_CREATOR_TOOL 0x2a696d67 /* FreeBSD mkimg */
77 uint32_t creator_version;
78 #define VHD_CREATOR_VERSION 0x00010000
79 uint32_t creator_os;
80 #define VHD_CREATOR_OS 0x46425344
81 uint64_t original_size;
82 uint64_t current_size;
83 uint16_t cylinders;
84 uint8_t heads;
85 uint8_t sectors;
86 uint32_t disk_type;
87 #define VHD_DISK_TYPE_FIXED 2
88 #define VHD_DISK_TYPE_DYNAMIC 3
89 #define VHD_DISK_TYPE_DIFF 4
90 uint32_t checksum;
91 uuid_t id;
92 uint8_t saved_state;
93 uint8_t _reserved[427];
94 };
95 #if __has_extension(c_static_assert)
96 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE,
97 "Wrong size for footer");
98 #endif
99
100 static uint32_t
vhd_checksum(void * buf,size_t sz)101 vhd_checksum(void *buf, size_t sz)
102 {
103 uint8_t *p = buf;
104 uint32_t sum;
105 size_t ofs;
106
107 sum = 0;
108 for (ofs = 0; ofs < sz; ofs++)
109 sum += p[ofs];
110 return (~sum);
111 }
112
113 static void
vhd_geometry(struct vhd_footer * footer,uint64_t image_size)114 vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
115 {
116 lba_t imgsz;
117 long cth;
118
119 /* Respect command line options if possible. */
120 if (nheads > 1 && nheads < 256 &&
121 nsecs > 1 && nsecs < 256 &&
122 ncyls < 65536) {
123 be16enc(&footer->cylinders, ncyls);
124 footer->heads = nheads;
125 footer->sectors = nsecs;
126 return;
127 }
128
129 imgsz = image_size / VHD_SECTOR_SIZE;
130 if (imgsz > 65536 * 16 * 255)
131 imgsz = 65536 * 16 * 255;
132 if (imgsz >= 65535 * 16 * 63) {
133 be16enc(&footer->cylinders, imgsz / (16 * 255));
134 footer->heads = 16;
135 footer->sectors = 255;
136 return;
137 }
138 footer->sectors = 17;
139 cth = imgsz / 17;
140 footer->heads = (cth + 1023) / 1024;
141 if (footer->heads < 4)
142 footer->heads = 4;
143 if (cth >= (footer->heads * 1024) || footer->heads > 16) {
144 footer->heads = 16;
145 footer->sectors = 31;
146 cth = imgsz / 31;
147 }
148 if (cth >= (footer->heads * 1024)) {
149 footer->heads = 16;
150 footer->sectors = 63;
151 cth = imgsz / 63;
152 }
153 be16enc(&footer->cylinders, cth / footer->heads);
154 }
155
156 static uint32_t
vhd_timestamp(void)157 vhd_timestamp(void)
158 {
159 time_t t;
160
161 if (!unit_testing) {
162 t = time(NULL);
163 return (t - 0x386d4380);
164 }
165
166 return (0x01234567);
167 }
168
169 static void
vhd_uuid_enc(void * buf,const uuid_t * uuid)170 vhd_uuid_enc(void *buf, const uuid_t *uuid)
171 {
172 uint8_t *p = buf;
173 int i;
174
175 be32enc(p, uuid->time_low);
176 be16enc(p + 4, uuid->time_mid);
177 be16enc(p + 6, uuid->time_hi_and_version);
178 p[8] = uuid->clock_seq_hi_and_reserved;
179 p[9] = uuid->clock_seq_low;
180 for (i = 0; i < _UUID_NODE_LEN; i++)
181 p[10 + i] = uuid->node[i];
182 }
183
184 static void
vhd_make_footer(struct vhd_footer * footer,uint64_t image_size,uint32_t disk_type,uint64_t data_offset)185 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size,
186 uint32_t disk_type, uint64_t data_offset)
187 {
188 uuid_t id;
189
190 memset(footer, 0, sizeof(*footer));
191 be64enc(&footer->cookie, VHD_FOOTER_COOKIE);
192 be32enc(&footer->features, VHD_FEATURES_RESERVED);
193 be32enc(&footer->version, VHD_VERSION);
194 be64enc(&footer->data_offset, data_offset);
195 be32enc(&footer->timestamp, vhd_timestamp());
196 be32enc(&footer->creator_tool, VHD_CREATOR_TOOL);
197 be32enc(&footer->creator_version, VHD_CREATOR_VERSION);
198 be32enc(&footer->creator_os, VHD_CREATOR_OS);
199 be64enc(&footer->original_size, image_size);
200 be64enc(&footer->current_size, image_size);
201 vhd_geometry(footer, image_size);
202 be32enc(&footer->disk_type, disk_type);
203 mkimg_uuid(&id);
204 vhd_uuid_enc(&footer->id, &id);
205 be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer)));
206 }
207
208 /*
209 * We round the image size to 2MB for both the dynamic and
210 * fixed VHD formats. For dynamic VHD, this is needed to
211 * have the image size be a multiple of the grain size. For
212 * fixed VHD this is not really needed, but makes sure that
213 * it's easy to convert from fixed VHD to dynamic VHD.
214 */
215 static int
vhd_resize(lba_t imgsz)216 vhd_resize(lba_t imgsz)
217 {
218 uint64_t imagesz;
219
220 imagesz = imgsz * secsz;
221 imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
222 return (image_set_size(imagesz / secsz));
223 }
224
225 /*
226 * PART 2: Dynamic VHD support
227 *
228 * Notes:
229 * o File layout:
230 * copy of disk footer
231 * dynamic disk header
232 * block allocation table (BAT)
233 * data blocks
234 * disk footer
235 */
236
237 struct vhd_dyn_header {
238 uint64_t cookie;
239 #define VHD_HEADER_COOKIE 0x6378737061727365
240 uint64_t data_offset;
241 uint64_t table_offset;
242 uint32_t version;
243 uint32_t max_entries;
244 uint32_t block_size;
245 uint32_t checksum;
246 uuid_t parent_id;
247 uint32_t parent_timestamp;
248 char _reserved1[4];
249 uint16_t parent_name[256]; /* UTF-16 */
250 struct {
251 uint32_t code;
252 uint32_t data_space;
253 uint32_t data_length;
254 uint32_t _reserved;
255 uint64_t data_offset;
256 } parent_locator[8];
257 char _reserved2[256];
258 };
259 #if __has_extension(c_static_assert)
260 _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
261 "Wrong size for header");
262 #endif
263
264 static int
vhd_dyn_write(int fd)265 vhd_dyn_write(int fd)
266 {
267 struct vhd_footer footer;
268 struct vhd_dyn_header header;
269 uint64_t imgsz;
270 lba_t blk, blkcnt, nblks;
271 uint32_t *bat;
272 void *bitmap;
273 size_t batsz;
274 uint32_t sector;
275 int bat_entries, error, entry;
276
277 imgsz = image_get_size() * secsz;
278 bat_entries = imgsz / VHD_BLOCK_SIZE;
279
280 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer));
281 if (sparse_write(fd, &footer, sizeof(footer)) < 0)
282 return (errno);
283
284 memset(&header, 0, sizeof(header));
285 be64enc(&header.cookie, VHD_HEADER_COOKIE);
286 be64enc(&header.data_offset, ~0ULL);
287 be64enc(&header.table_offset, sizeof(footer) + sizeof(header));
288 be32enc(&header.version, VHD_VERSION);
289 be32enc(&header.max_entries, bat_entries);
290 be32enc(&header.block_size, VHD_BLOCK_SIZE);
291 be32enc(&header.checksum, vhd_checksum(&header, sizeof(header)));
292 if (sparse_write(fd, &header, sizeof(header)) < 0)
293 return (errno);
294
295 batsz = bat_entries * sizeof(uint32_t);
296 batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1);
297 bat = malloc(batsz);
298 if (bat == NULL)
299 return (errno);
300 memset(bat, 0xff, batsz);
301 blkcnt = VHD_BLOCK_SIZE / secsz;
302 sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE;
303 for (entry = 0; entry < bat_entries; entry++) {
304 blk = entry * blkcnt;
305 if (image_data(blk, blkcnt)) {
306 be32enc(&bat[entry], sector);
307 sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1;
308 }
309 }
310 if (sparse_write(fd, bat, batsz) < 0) {
311 free(bat);
312 return (errno);
313 }
314 free(bat);
315
316 bitmap = malloc(VHD_SECTOR_SIZE);
317 if (bitmap == NULL)
318 return (errno);
319 memset(bitmap, 0xff, VHD_SECTOR_SIZE);
320
321 blk = 0;
322 blkcnt = VHD_BLOCK_SIZE / secsz;
323 error = 0;
324 nblks = image_get_size();
325 while (blk < nblks) {
326 if (!image_data(blk, blkcnt)) {
327 blk += blkcnt;
328 continue;
329 }
330 if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) {
331 error = errno;
332 break;
333 }
334 error = image_copyout_region(fd, blk, blkcnt);
335 if (error)
336 break;
337 blk += blkcnt;
338 }
339 free(bitmap);
340 if (blk != nblks)
341 return (error);
342
343 if (sparse_write(fd, &footer, sizeof(footer)) < 0)
344 return (errno);
345
346 return (0);
347 }
348
349 static struct mkimg_format vhd_dyn_format = {
350 .name = "vhd",
351 .description = "Virtual Hard Disk",
352 .resize = vhd_resize,
353 .write = vhd_dyn_write,
354 };
355
356 FORMAT_DEFINE(vhd_dyn_format);
357
358 /*
359 * PART 2: Fixed VHD
360 */
361
362 static int
vhd_fix_write(int fd)363 vhd_fix_write(int fd)
364 {
365 struct vhd_footer footer;
366 uint64_t imgsz;
367 int error;
368
369 error = image_copyout(fd);
370 if (!error) {
371 imgsz = image_get_size() * secsz;
372 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL);
373 if (sparse_write(fd, &footer, sizeof(footer)) < 0)
374 error = errno;
375 }
376 return (error);
377 }
378
379 static struct mkimg_format vhd_fix_format = {
380 .name = "vhdf",
381 .description = "Fixed Virtual Hard Disk",
382 .resize = vhd_resize,
383 .write = vhd_fix_write,
384 };
385
386 FORMAT_DEFINE(vhd_fix_format);
387