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 /* 44 * General notes: 45 * o File is in network byte order. 46 * o The timestamp is seconds since 1/1/2000 12:00:00 AM UTC 47 * 48 * This file is divided in 3 parts: 49 * 1. Common definitions 50 * 2. Dynamic VHD support 51 * 3. Fixed VHD support 52 */ 53 54 /* 55 * PART 1: Common definitions 56 */ 57 58 #define VHD_SECTOR_SIZE 512 59 #define VHD_BLOCK_SIZE (4096 * VHD_SECTOR_SIZE) /* 2MB blocks */ 60 61 struct vhd_footer { 62 uint64_t cookie; 63 #define VHD_FOOTER_COOKIE 0x636f6e6563746978 64 uint32_t features; 65 #define VHD_FEATURES_TEMPORARY 0x01 66 #define VHD_FEATURES_RESERVED 0x02 67 uint32_t version; 68 #define VHD_VERSION 0x00010000 69 uint64_t data_offset; 70 uint32_t timestamp; 71 uint32_t creator_tool; 72 #define VHD_CREATOR_TOOL 0x2a696d67 /* FreeBSD mkimg */ 73 uint32_t creator_version; 74 #define VHD_CREATOR_VERSION 0x00010000 75 uint32_t creator_os; 76 #define VHD_CREATOR_OS 0x46425344 77 uint64_t original_size; 78 uint64_t current_size; 79 uint16_t cylinders; 80 uint8_t heads; 81 uint8_t sectors; 82 uint32_t disk_type; 83 #define VHD_DISK_TYPE_FIXED 2 84 #define VHD_DISK_TYPE_DYNAMIC 3 85 #define VHD_DISK_TYPE_DIFF 4 86 uint32_t checksum; 87 uuid_t id; 88 uint8_t saved_state; 89 uint8_t _reserved[427]; 90 }; 91 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE, 92 "Wrong size for footer"); 93 94 static uint32_t 95 vhd_checksum(void *buf, size_t sz) 96 { 97 uint8_t *p = buf; 98 uint32_t sum; 99 size_t ofs; 100 101 sum = 0; 102 for (ofs = 0; ofs < sz; ofs++) 103 sum += p[ofs]; 104 return (~sum); 105 } 106 107 static void 108 vhd_geometry(struct vhd_footer *footer, uint64_t image_size) 109 { 110 lba_t imgsz; 111 long cth; 112 113 /* Respect command line options if possible. */ 114 if (nheads > 1 && nheads < 256 && 115 nsecs > 1 && nsecs < 256 && 116 ncyls < 65536) { 117 be16enc(&footer->cylinders, ncyls); 118 footer->heads = nheads; 119 footer->sectors = nsecs; 120 return; 121 } 122 123 imgsz = image_size / VHD_SECTOR_SIZE; 124 if (imgsz > 65536 * 16 * 255) 125 imgsz = 65536 * 16 * 255; 126 if (imgsz >= 65535 * 16 * 63) { 127 be16enc(&footer->cylinders, imgsz / (16 * 255)); 128 footer->heads = 16; 129 footer->sectors = 255; 130 return; 131 } 132 footer->sectors = 17; 133 cth = imgsz / 17; 134 footer->heads = (cth + 1023) / 1024; 135 if (footer->heads < 4) 136 footer->heads = 4; 137 if (cth >= (footer->heads * 1024) || footer->heads > 16) { 138 footer->heads = 16; 139 footer->sectors = 31; 140 cth = imgsz / 31; 141 } 142 if (cth >= (footer->heads * 1024)) { 143 footer->heads = 16; 144 footer->sectors = 63; 145 cth = imgsz / 63; 146 } 147 be16enc(&footer->cylinders, cth / footer->heads); 148 } 149 150 static uint32_t 151 vhd_timestamp(void) 152 { 153 time_t t; 154 155 if (!unit_testing) { 156 t = time(NULL); 157 return (t - 0x386d4380); 158 } 159 160 return (0x01234567); 161 } 162 163 static void 164 vhd_uuid_enc(void *buf, const uuid_t *uuid) 165 { 166 uint8_t *p = buf; 167 int i; 168 169 be32enc(p, uuid->time_low); 170 be16enc(p + 4, uuid->time_mid); 171 be16enc(p + 6, uuid->time_hi_and_version); 172 p[8] = uuid->clock_seq_hi_and_reserved; 173 p[9] = uuid->clock_seq_low; 174 for (i = 0; i < _UUID_NODE_LEN; i++) 175 p[10 + i] = uuid->node[i]; 176 } 177 178 static void 179 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size, 180 uint32_t disk_type, uint64_t data_offset) 181 { 182 uuid_t id; 183 184 memset(footer, 0, sizeof(*footer)); 185 be64enc(&footer->cookie, VHD_FOOTER_COOKIE); 186 be32enc(&footer->features, VHD_FEATURES_RESERVED); 187 be32enc(&footer->version, VHD_VERSION); 188 be64enc(&footer->data_offset, data_offset); 189 be32enc(&footer->timestamp, vhd_timestamp()); 190 be32enc(&footer->creator_tool, VHD_CREATOR_TOOL); 191 be32enc(&footer->creator_version, VHD_CREATOR_VERSION); 192 be32enc(&footer->creator_os, VHD_CREATOR_OS); 193 be64enc(&footer->original_size, image_size); 194 be64enc(&footer->current_size, image_size); 195 vhd_geometry(footer, image_size); 196 be32enc(&footer->disk_type, disk_type); 197 mkimg_uuid(&id); 198 vhd_uuid_enc(&footer->id, &id); 199 be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer))); 200 } 201 202 /* 203 * We round the image size to 2MB for both the dynamic and 204 * fixed VHD formats. For dynamic VHD, this is needed to 205 * have the image size be a multiple of the grain size. For 206 * fixed VHD this is not really needed, but makes sure that 207 * it's easy to convert from fixed VHD to dynamic VHD. 208 */ 209 static int 210 vhd_resize(lba_t imgsz) 211 { 212 uint64_t imagesz; 213 214 imagesz = imgsz * secsz; 215 imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); 216 return (image_set_size(imagesz / secsz)); 217 } 218 219 /* 220 * PART 2: Dynamic VHD support 221 * 222 * Notes: 223 * o File layout: 224 * copy of disk footer 225 * dynamic disk header 226 * block allocation table (BAT) 227 * data blocks 228 * disk footer 229 */ 230 231 struct vhd_dyn_header { 232 uint64_t cookie; 233 #define VHD_HEADER_COOKIE 0x6378737061727365 234 uint64_t data_offset; 235 uint64_t table_offset; 236 uint32_t version; 237 uint32_t max_entries; 238 uint32_t block_size; 239 uint32_t checksum; 240 uuid_t parent_id; 241 uint32_t parent_timestamp; 242 char _reserved1[4]; 243 uint16_t parent_name[256]; /* UTF-16 */ 244 struct { 245 uint32_t code; 246 uint32_t data_space; 247 uint32_t data_length; 248 uint32_t _reserved; 249 uint64_t data_offset; 250 } parent_locator[8]; 251 char _reserved2[256]; 252 }; 253 _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2, 254 "Wrong size for header"); 255 256 static int 257 vhd_dyn_write(int fd) 258 { 259 struct vhd_footer footer; 260 struct vhd_dyn_header header; 261 uint64_t imgsz; 262 lba_t blk, blkcnt, nblks; 263 uint32_t *bat; 264 void *bitmap; 265 size_t batsz; 266 uint32_t sector; 267 int bat_entries, error, entry; 268 269 imgsz = image_get_size() * secsz; 270 bat_entries = imgsz / VHD_BLOCK_SIZE; 271 272 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer)); 273 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 274 return (errno); 275 276 memset(&header, 0, sizeof(header)); 277 be64enc(&header.cookie, VHD_HEADER_COOKIE); 278 be64enc(&header.data_offset, ~0ULL); 279 be64enc(&header.table_offset, sizeof(footer) + sizeof(header)); 280 be32enc(&header.version, VHD_VERSION); 281 be32enc(&header.max_entries, bat_entries); 282 be32enc(&header.block_size, VHD_BLOCK_SIZE); 283 be32enc(&header.checksum, vhd_checksum(&header, sizeof(header))); 284 if (sparse_write(fd, &header, sizeof(header)) < 0) 285 return (errno); 286 287 batsz = bat_entries * sizeof(uint32_t); 288 batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1); 289 bat = malloc(batsz); 290 if (bat == NULL) 291 return (errno); 292 memset(bat, 0xff, batsz); 293 blkcnt = VHD_BLOCK_SIZE / secsz; 294 sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE; 295 for (entry = 0; entry < bat_entries; entry++) { 296 blk = entry * blkcnt; 297 if (image_data(blk, blkcnt)) { 298 be32enc(&bat[entry], sector); 299 sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1; 300 } 301 } 302 if (sparse_write(fd, bat, batsz) < 0) { 303 free(bat); 304 return (errno); 305 } 306 free(bat); 307 308 bitmap = malloc(VHD_SECTOR_SIZE); 309 if (bitmap == NULL) 310 return (errno); 311 memset(bitmap, 0xff, VHD_SECTOR_SIZE); 312 313 blk = 0; 314 blkcnt = VHD_BLOCK_SIZE / secsz; 315 error = 0; 316 nblks = image_get_size(); 317 while (blk < nblks) { 318 if (!image_data(blk, blkcnt)) { 319 blk += blkcnt; 320 continue; 321 } 322 if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) { 323 error = errno; 324 break; 325 } 326 error = image_copyout_region(fd, blk, blkcnt); 327 if (error) 328 break; 329 blk += blkcnt; 330 } 331 free(bitmap); 332 if (blk != nblks) 333 return (error); 334 335 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 336 return (errno); 337 338 return (0); 339 } 340 341 static struct mkimg_format vhd_dyn_format = { 342 .name = "vhd", 343 .description = "Virtual Hard Disk", 344 .resize = vhd_resize, 345 .write = vhd_dyn_write, 346 }; 347 348 FORMAT_DEFINE(vhd_dyn_format); 349 350 /* 351 * PART 2: Fixed VHD 352 */ 353 354 static int 355 vhd_fix_write(int fd) 356 { 357 struct vhd_footer footer; 358 uint64_t imgsz; 359 int error; 360 361 error = image_copyout(fd); 362 if (!error) { 363 imgsz = image_get_size() * secsz; 364 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL); 365 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 366 error = errno; 367 } 368 return (error); 369 } 370 371 static struct mkimg_format vhd_fix_format = { 372 .name = "vhdf", 373 .description = "Fixed Virtual Hard Disk", 374 .resize = vhd_resize, 375 .write = vhd_fix_write, 376 }; 377 378 FORMAT_DEFINE(vhd_fix_format); 379