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 0x636f6e6563746978ULL 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 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 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 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 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 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 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 0x6378737061727365ULL 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 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 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