1 /*- 2 * Copyright (c) 2014, 2015 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_geom { 66 uint16_t cylinders; 67 uint8_t heads; 68 uint8_t sectors; 69 }; 70 71 struct vhd_footer { 72 uint64_t cookie; 73 #define VHD_FOOTER_COOKIE 0x636f6e6563746978ULL 74 uint32_t features; 75 #define VHD_FEATURES_TEMPORARY 0x01 76 #define VHD_FEATURES_RESERVED 0x02 77 uint32_t version; 78 #define VHD_VERSION 0x00010000 79 uint64_t data_offset; 80 uint32_t timestamp; 81 uint32_t creator_tool; 82 #define VHD_CREATOR_TOOL 0x2a696d67 /* FreeBSD mkimg */ 83 uint32_t creator_version; 84 #define VHD_CREATOR_VERSION 0x00020000 85 uint32_t creator_os; 86 #define VHD_CREATOR_OS 0x5769326b /* Wi2k */ 87 uint64_t original_size; 88 uint64_t current_size; 89 struct vhd_geom geometry; 90 uint32_t disk_type; 91 #define VHD_DISK_TYPE_FIXED 2 92 #define VHD_DISK_TYPE_DYNAMIC 3 93 #define VHD_DISK_TYPE_DIFF 4 94 uint32_t checksum; 95 uuid_t id; 96 uint8_t saved_state; 97 uint8_t _reserved[427]; 98 }; 99 #if __has_extension(c_static_assert) 100 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE, 101 "Wrong size for footer"); 102 #endif 103 104 static uint32_t 105 vhd_checksum(void *buf, size_t sz) 106 { 107 uint8_t *p = buf; 108 uint32_t sum; 109 size_t ofs; 110 111 sum = 0; 112 for (ofs = 0; ofs < sz; ofs++) 113 sum += p[ofs]; 114 return (~sum); 115 } 116 117 static void 118 vhd_geometry(uint64_t image_size, struct vhd_geom *geom) 119 { 120 lba_t imgsz; 121 long cth; 122 123 imgsz = image_size / VHD_SECTOR_SIZE; 124 125 /* Respect command line options if possible. */ 126 if (nheads > 1 && nheads < 256 && 127 nsecs > 1 && nsecs < 256 && 128 ncyls < 65536) { 129 geom->cylinders = (ncyls != 0) ? ncyls : 130 imgsz / (nheads * nsecs); 131 geom->heads = nheads; 132 geom->sectors = nsecs; 133 return; 134 } 135 136 if (imgsz > 65536 * 16 * 255) 137 imgsz = 65536 * 16 * 255; 138 if (imgsz >= 65535 * 16 * 63) { 139 geom->cylinders = imgsz / (16 * 255); 140 geom->heads = 16; 141 geom->sectors = 255; 142 return; 143 } 144 geom->sectors = 17; 145 cth = imgsz / 17; 146 geom->heads = (cth + 1023) / 1024; 147 if (geom->heads < 4) 148 geom->heads = 4; 149 if (cth >= (geom->heads * 1024) || geom->heads > 16) { 150 geom->heads = 16; 151 geom->sectors = 31; 152 cth = imgsz / 31; 153 } 154 if (cth >= (geom->heads * 1024)) { 155 geom->heads = 16; 156 geom->sectors = 63; 157 cth = imgsz / 63; 158 } 159 geom->cylinders = cth / geom->heads; 160 } 161 162 static uint64_t 163 vhd_resize(uint64_t origsz) 164 { 165 struct vhd_geom geom; 166 uint64_t newsz; 167 168 /* 169 * Round the image size to the pre-determined geometry that 170 * matches the image size. This circular dependency implies 171 * that we need to loop to handle boundary conditions. 172 * The first time, newsz equals origsz and the geometry will 173 * typically yield a new size that's smaller. We keep adding 174 * cylinder's worth of sectors to the new size until its 175 * larger or equal or origsz. But during those iterations, 176 * the geometry can change, so we need to account for that. 177 */ 178 newsz = origsz; 179 while (1) { 180 vhd_geometry(newsz, &geom); 181 newsz = (int64_t)geom.cylinders * geom.heads * 182 geom.sectors * VHD_SECTOR_SIZE; 183 if (newsz >= origsz) 184 break; 185 newsz += geom.heads * geom.sectors * VHD_SECTOR_SIZE; 186 } 187 return (newsz); 188 } 189 190 static uint32_t 191 vhd_timestamp(void) 192 { 193 time_t t; 194 195 if (!unit_testing) { 196 t = time(NULL); 197 return (t - 0x386d4380); 198 } 199 200 return (0x01234567); 201 } 202 203 static void 204 vhd_uuid_enc(void *buf, const uuid_t *uuid) 205 { 206 uint8_t *p = buf; 207 int i; 208 209 be32enc(p, uuid->time_low); 210 be16enc(p + 4, uuid->time_mid); 211 be16enc(p + 6, uuid->time_hi_and_version); 212 p[8] = uuid->clock_seq_hi_and_reserved; 213 p[9] = uuid->clock_seq_low; 214 for (i = 0; i < _UUID_NODE_LEN; i++) 215 p[10 + i] = uuid->node[i]; 216 } 217 218 static void 219 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size, 220 uint32_t disk_type, uint64_t data_offset) 221 { 222 uuid_t id; 223 224 memset(footer, 0, sizeof(*footer)); 225 be64enc(&footer->cookie, VHD_FOOTER_COOKIE); 226 be32enc(&footer->features, VHD_FEATURES_RESERVED); 227 be32enc(&footer->version, VHD_VERSION); 228 be64enc(&footer->data_offset, data_offset); 229 be32enc(&footer->timestamp, vhd_timestamp()); 230 be32enc(&footer->creator_tool, VHD_CREATOR_TOOL); 231 be32enc(&footer->creator_version, VHD_CREATOR_VERSION); 232 be32enc(&footer->creator_os, VHD_CREATOR_OS); 233 be64enc(&footer->original_size, image_size); 234 be64enc(&footer->current_size, image_size); 235 vhd_geometry(image_size, &footer->geometry); 236 be16enc(&footer->geometry.cylinders, footer->geometry.cylinders); 237 be32enc(&footer->disk_type, disk_type); 238 mkimg_uuid(&id); 239 vhd_uuid_enc(&footer->id, &id); 240 be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer))); 241 } 242 243 /* 244 * PART 2: Dynamic VHD support 245 * 246 * Notes: 247 * o File layout: 248 * copy of disk footer 249 * dynamic disk header 250 * block allocation table (BAT) 251 * data blocks 252 * disk footer 253 */ 254 255 struct vhd_dyn_header { 256 uint64_t cookie; 257 #define VHD_HEADER_COOKIE 0x6378737061727365ULL 258 uint64_t data_offset; 259 uint64_t table_offset; 260 uint32_t version; 261 uint32_t max_entries; 262 uint32_t block_size; 263 uint32_t checksum; 264 uuid_t parent_id; 265 uint32_t parent_timestamp; 266 char _reserved1[4]; 267 uint16_t parent_name[256]; /* UTF-16 */ 268 struct { 269 uint32_t code; 270 uint32_t data_space; 271 uint32_t data_length; 272 uint32_t _reserved; 273 uint64_t data_offset; 274 } parent_locator[8]; 275 char _reserved2[256]; 276 }; 277 #if __has_extension(c_static_assert) 278 _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2, 279 "Wrong size for header"); 280 #endif 281 282 static int 283 vhd_dyn_resize(lba_t imgsz) 284 { 285 uint64_t imagesz; 286 287 imagesz = vhd_resize(imgsz * secsz); 288 return (image_set_size(imagesz / secsz)); 289 } 290 291 static int 292 vhd_dyn_write(int fd) 293 { 294 struct vhd_footer footer; 295 struct vhd_dyn_header header; 296 uint64_t imgsz, rawsz; 297 lba_t blk, blkcnt, nblks; 298 uint32_t *bat; 299 void *bitmap; 300 size_t batsz; 301 uint32_t sector; 302 int bat_entries, error, entry; 303 304 rawsz = image_get_size() * secsz; 305 imgsz = (rawsz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); 306 307 vhd_make_footer(&footer, rawsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer)); 308 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 309 return (errno); 310 311 bat_entries = imgsz / VHD_BLOCK_SIZE; 312 memset(&header, 0, sizeof(header)); 313 be64enc(&header.cookie, VHD_HEADER_COOKIE); 314 be64enc(&header.data_offset, ~0ULL); 315 be64enc(&header.table_offset, sizeof(footer) + sizeof(header)); 316 be32enc(&header.version, VHD_VERSION); 317 be32enc(&header.max_entries, bat_entries); 318 be32enc(&header.block_size, VHD_BLOCK_SIZE); 319 be32enc(&header.checksum, vhd_checksum(&header, sizeof(header))); 320 if (sparse_write(fd, &header, sizeof(header)) < 0) 321 return (errno); 322 323 batsz = bat_entries * sizeof(uint32_t); 324 batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1); 325 bat = malloc(batsz); 326 if (bat == NULL) 327 return (errno); 328 memset(bat, 0xff, batsz); 329 blkcnt = VHD_BLOCK_SIZE / secsz; 330 sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE; 331 for (entry = 0; entry < bat_entries; entry++) { 332 blk = entry * blkcnt; 333 if (image_data(blk, blkcnt)) { 334 be32enc(&bat[entry], sector); 335 sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1; 336 } 337 } 338 if (sparse_write(fd, bat, batsz) < 0) { 339 free(bat); 340 return (errno); 341 } 342 free(bat); 343 344 bitmap = malloc(VHD_SECTOR_SIZE); 345 if (bitmap == NULL) 346 return (errno); 347 memset(bitmap, 0xff, VHD_SECTOR_SIZE); 348 349 blk = 0; 350 blkcnt = VHD_BLOCK_SIZE / secsz; 351 error = 0; 352 nblks = rawsz / secsz; 353 while (blk < nblks) { 354 if (!image_data(blk, blkcnt)) { 355 blk += blkcnt; 356 continue; 357 } 358 if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) { 359 error = errno; 360 break; 361 } 362 /* Handle partial last block */ 363 if (blk + blkcnt > nblks) 364 blkcnt = nblks - blk; 365 error = image_copyout_region(fd, blk, blkcnt); 366 if (error) 367 break; 368 blk += blkcnt; 369 } 370 free(bitmap); 371 if (error) 372 return (error); 373 error = image_copyout_zeroes(fd, imgsz - rawsz); 374 if (error) 375 return (error); 376 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 377 return (errno); 378 379 return (0); 380 } 381 382 static struct mkimg_format vhd_dyn_format = { 383 .name = "vhd", 384 .description = "Virtual Hard Disk", 385 .resize = vhd_dyn_resize, 386 .write = vhd_dyn_write, 387 }; 388 389 FORMAT_DEFINE(vhd_dyn_format); 390 391 /* 392 * PART 3: Fixed VHD 393 */ 394 395 static int 396 vhd_fix_resize(lba_t imgsz) 397 { 398 uint64_t imagesz; 399 400 imagesz = vhd_resize(imgsz * secsz); 401 /* 402 * Azure demands that images are a whole number of megabytes. 403 */ 404 imagesz = (imagesz + 0xfffffULL) & ~0xfffffULL; 405 return (image_set_size(imagesz / secsz)); 406 } 407 408 static int 409 vhd_fix_write(int fd) 410 { 411 struct vhd_footer footer; 412 uint64_t imagesz; 413 int error; 414 415 error = image_copyout(fd); 416 if (error) 417 return (error); 418 419 imagesz = image_get_size() * secsz; 420 vhd_make_footer(&footer, imagesz, VHD_DISK_TYPE_FIXED, ~0ULL); 421 error = (sparse_write(fd, &footer, sizeof(footer)) < 0) ? errno : 0; 422 return (error); 423 } 424 425 static struct mkimg_format vhd_fix_format = { 426 .name = "vhdf", 427 .description = "Fixed Virtual Hard Disk", 428 .resize = vhd_fix_resize, 429 .write = vhd_fix_write, 430 }; 431 432 FORMAT_DEFINE(vhd_fix_format); 433