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 uint32_t 163 vhd_timestamp(void) 164 { 165 time_t t; 166 167 if (!unit_testing) { 168 t = time(NULL); 169 return (t - 0x386d4380); 170 } 171 172 return (0x01234567); 173 } 174 175 static void 176 vhd_uuid_enc(void *buf, const uuid_t *uuid) 177 { 178 uint8_t *p = buf; 179 int i; 180 181 be32enc(p, uuid->time_low); 182 be16enc(p + 4, uuid->time_mid); 183 be16enc(p + 6, uuid->time_hi_and_version); 184 p[8] = uuid->clock_seq_hi_and_reserved; 185 p[9] = uuid->clock_seq_low; 186 for (i = 0; i < _UUID_NODE_LEN; i++) 187 p[10 + i] = uuid->node[i]; 188 } 189 190 static void 191 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size, 192 uint32_t disk_type, uint64_t data_offset) 193 { 194 uuid_t id; 195 196 memset(footer, 0, sizeof(*footer)); 197 be64enc(&footer->cookie, VHD_FOOTER_COOKIE); 198 be32enc(&footer->features, VHD_FEATURES_RESERVED); 199 be32enc(&footer->version, VHD_VERSION); 200 be64enc(&footer->data_offset, data_offset); 201 be32enc(&footer->timestamp, vhd_timestamp()); 202 be32enc(&footer->creator_tool, VHD_CREATOR_TOOL); 203 be32enc(&footer->creator_version, VHD_CREATOR_VERSION); 204 be32enc(&footer->creator_os, VHD_CREATOR_OS); 205 be64enc(&footer->original_size, image_size); 206 be64enc(&footer->current_size, image_size); 207 vhd_geometry(image_size, &footer->geometry); 208 be16enc(&footer->geometry.cylinders, footer->geometry.cylinders); 209 be32enc(&footer->disk_type, disk_type); 210 mkimg_uuid(&id); 211 vhd_uuid_enc(&footer->id, &id); 212 be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer))); 213 } 214 215 /* 216 * PART 2: Dynamic VHD support 217 * 218 * Notes: 219 * o File layout: 220 * copy of disk footer 221 * dynamic disk header 222 * block allocation table (BAT) 223 * data blocks 224 * disk footer 225 */ 226 227 struct vhd_dyn_header { 228 uint64_t cookie; 229 #define VHD_HEADER_COOKIE 0x6378737061727365ULL 230 uint64_t data_offset; 231 uint64_t table_offset; 232 uint32_t version; 233 uint32_t max_entries; 234 uint32_t block_size; 235 uint32_t checksum; 236 uuid_t parent_id; 237 uint32_t parent_timestamp; 238 char _reserved1[4]; 239 uint16_t parent_name[256]; /* UTF-16 */ 240 struct { 241 uint32_t code; 242 uint32_t data_space; 243 uint32_t data_length; 244 uint32_t _reserved; 245 uint64_t data_offset; 246 } parent_locator[8]; 247 char _reserved2[256]; 248 }; 249 #if __has_extension(c_static_assert) 250 _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2, 251 "Wrong size for header"); 252 #endif 253 254 static int 255 vhd_dyn_resize(lba_t imgsz) 256 { 257 uint64_t imagesz; 258 259 imagesz = imgsz * secsz; 260 imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); 261 return (image_set_size(imagesz / secsz)); 262 } 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_dyn_resize, 353 .write = vhd_dyn_write, 354 }; 355 356 FORMAT_DEFINE(vhd_dyn_format); 357 358 /* 359 * PART 3: Fixed VHD 360 */ 361 362 static int 363 vhd_fix_resize(lba_t imgsz) 364 { 365 struct vhd_geom geom; 366 int64_t imagesz; 367 368 /* 369 * Round the image size to the pre-determined geometry that 370 * matches the image size. This circular dependency implies 371 * that we need to loop to handle boundary conditions. 372 */ 373 imgsz *= secsz; 374 imagesz = imgsz; 375 while (1) { 376 vhd_geometry(imagesz, &geom); 377 imagesz = (int64_t)geom.cylinders * geom.heads * 378 geom.sectors * VHD_SECTOR_SIZE; 379 if (imagesz >= imgsz) 380 break; 381 imagesz += geom.heads * geom.sectors * VHD_SECTOR_SIZE; 382 } 383 /* 384 * Azure demands that images are a whole number of megabytes. 385 */ 386 imagesz = (imagesz + 0xfffffULL) & ~0xfffffULL; 387 return (image_set_size(imagesz / secsz)); 388 } 389 390 static int 391 vhd_fix_write(int fd) 392 { 393 struct vhd_footer footer; 394 uint64_t imgsz; 395 int error; 396 397 error = image_copyout(fd); 398 if (!error) { 399 imgsz = image_get_size() * secsz; 400 vhd_make_footer(&footer, imgsz, VHD_DISK_TYPE_FIXED, ~0ULL); 401 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 402 error = errno; 403 } 404 return (error); 405 } 406 407 static struct mkimg_format vhd_fix_format = { 408 .name = "vhdf", 409 .description = "Fixed Virtual Hard Disk", 410 .resize = vhd_fix_resize, 411 .write = vhd_fix_write, 412 }; 413 414 FORMAT_DEFINE(vhd_fix_format); 415