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/errno.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 35 #include "endian.h" 36 #include "image.h" 37 #include "format.h" 38 #include "mkimg.h" 39 40 #ifndef __has_extension 41 #define __has_extension(x) 0 42 #endif 43 44 /* 45 * General notes: 46 * o File is in network byte order. 47 * o The timestamp is seconds since 1/1/2000 12:00:00 AM UTC 48 * 49 * This file is divided in 3 parts: 50 * 1. Common definitions 51 * 2. Dynamic VHD support 52 * 3. Fixed VHD support 53 */ 54 55 /* 56 * PART 1: Common definitions 57 */ 58 59 #define VHD_SECTOR_SIZE 512 60 #define VHD_BLOCK_SIZE (4096 * VHD_SECTOR_SIZE) /* 2MB blocks */ 61 62 struct vhd_geom { 63 uint16_t cylinders; 64 uint8_t heads; 65 uint8_t sectors; 66 }; 67 68 struct vhd_footer { 69 uint64_t cookie; 70 #define VHD_FOOTER_COOKIE 0x636f6e6563746978ULL 71 uint32_t features; 72 #define VHD_FEATURES_TEMPORARY 0x01 73 #define VHD_FEATURES_RESERVED 0x02 74 uint32_t version; 75 #define VHD_VERSION 0x00010000 76 uint64_t data_offset; 77 uint32_t timestamp; 78 uint32_t creator_tool; 79 #define VHD_CREATOR_TOOL 0x2a696d67 /* FreeBSD mkimg */ 80 uint32_t creator_version; 81 #define VHD_CREATOR_VERSION 0x00020000 82 uint32_t creator_os; 83 #define VHD_CREATOR_OS 0x5769326b /* Wi2k */ 84 uint64_t original_size; 85 uint64_t current_size; 86 struct vhd_geom geometry; 87 uint32_t disk_type; 88 #define VHD_DISK_TYPE_FIXED 2 89 #define VHD_DISK_TYPE_DYNAMIC 3 90 #define VHD_DISK_TYPE_DIFF 4 91 uint32_t checksum; 92 mkimg_uuid_t id; 93 uint8_t saved_state; 94 uint8_t _reserved[427]; 95 }; 96 #if __has_extension(c_static_assert) 97 _Static_assert(sizeof(struct vhd_footer) == VHD_SECTOR_SIZE, 98 "Wrong size for footer"); 99 #endif 100 101 static uint32_t 102 vhd_checksum(void *buf, size_t sz) 103 { 104 uint8_t *p = buf; 105 uint32_t sum; 106 size_t ofs; 107 108 sum = 0; 109 for (ofs = 0; ofs < sz; ofs++) 110 sum += p[ofs]; 111 return (~sum); 112 } 113 114 static void 115 vhd_geometry(uint64_t image_size, struct vhd_geom *geom) 116 { 117 lba_t imgsz; 118 long cth; 119 120 imgsz = image_size / VHD_SECTOR_SIZE; 121 122 /* Respect command line options if possible. */ 123 if (nheads > 1 && nheads < 256 && 124 nsecs > 1 && nsecs < 256 && 125 ncyls < 65536) { 126 geom->cylinders = (ncyls != 0) ? ncyls : 127 imgsz / (nheads * nsecs); 128 geom->heads = nheads; 129 geom->sectors = nsecs; 130 return; 131 } 132 133 if (imgsz > 65536 * 16 * 255) 134 imgsz = 65536 * 16 * 255; 135 if (imgsz >= 65535 * 16 * 63) { 136 geom->cylinders = imgsz / (16 * 255); 137 geom->heads = 16; 138 geom->sectors = 255; 139 return; 140 } 141 geom->sectors = 17; 142 cth = imgsz / 17; 143 geom->heads = (cth + 1023) / 1024; 144 if (geom->heads < 4) 145 geom->heads = 4; 146 if (cth >= (geom->heads * 1024) || geom->heads > 16) { 147 geom->heads = 16; 148 geom->sectors = 31; 149 cth = imgsz / 31; 150 } 151 if (cth >= (geom->heads * 1024)) { 152 geom->heads = 16; 153 geom->sectors = 63; 154 cth = imgsz / 63; 155 } 156 geom->cylinders = cth / geom->heads; 157 } 158 159 static uint64_t 160 vhd_resize(uint64_t origsz) 161 { 162 struct vhd_geom geom; 163 uint64_t newsz; 164 165 /* 166 * Round the image size to the pre-determined geometry that 167 * matches the image size. This circular dependency implies 168 * that we need to loop to handle boundary conditions. 169 * The first time, newsz equals origsz and the geometry will 170 * typically yield a new size that's smaller. We keep adding 171 * cylinder's worth of sectors to the new size until its 172 * larger or equal or origsz. But during those iterations, 173 * the geometry can change, so we need to account for that. 174 */ 175 newsz = origsz; 176 while (1) { 177 vhd_geometry(newsz, &geom); 178 newsz = (int64_t)geom.cylinders * geom.heads * 179 geom.sectors * VHD_SECTOR_SIZE; 180 if (newsz >= origsz) 181 break; 182 newsz += geom.heads * geom.sectors * VHD_SECTOR_SIZE; 183 } 184 return (newsz); 185 } 186 187 static uint32_t 188 vhd_timestamp(void) 189 { 190 time_t t; 191 192 if (!unit_testing) { 193 t = time(NULL); 194 return (t - 0x386d4380); 195 } 196 197 return (0x01234567); 198 } 199 200 static void 201 vhd_make_footer(struct vhd_footer *footer, uint64_t image_size, 202 uint32_t disk_type, uint64_t data_offset) 203 { 204 mkimg_uuid_t id; 205 206 memset(footer, 0, sizeof(*footer)); 207 be64enc(&footer->cookie, VHD_FOOTER_COOKIE); 208 be32enc(&footer->features, VHD_FEATURES_RESERVED); 209 be32enc(&footer->version, VHD_VERSION); 210 be64enc(&footer->data_offset, data_offset); 211 be32enc(&footer->timestamp, vhd_timestamp()); 212 be32enc(&footer->creator_tool, VHD_CREATOR_TOOL); 213 be32enc(&footer->creator_version, VHD_CREATOR_VERSION); 214 be32enc(&footer->creator_os, VHD_CREATOR_OS); 215 be64enc(&footer->original_size, image_size); 216 be64enc(&footer->current_size, image_size); 217 vhd_geometry(image_size, &footer->geometry); 218 be16enc(&footer->geometry.cylinders, footer->geometry.cylinders); 219 be32enc(&footer->disk_type, disk_type); 220 mkimg_uuid(&id); 221 mkimg_uuid_enc(&footer->id, &id); 222 be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer))); 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 mkimg_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_resize(lba_t imgsz) 266 { 267 uint64_t imagesz; 268 269 imagesz = vhd_resize(imgsz * secsz); 270 return (image_set_size(imagesz / secsz)); 271 } 272 273 static int 274 vhd_dyn_write(int fd) 275 { 276 struct vhd_footer footer; 277 struct vhd_dyn_header header; 278 uint64_t imgsz, rawsz; 279 lba_t blk, blkcnt, nblks; 280 uint32_t *bat; 281 void *bitmap; 282 size_t batsz; 283 uint32_t sector; 284 int bat_entries, error, entry; 285 286 rawsz = image_get_size() * secsz; 287 imgsz = (rawsz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1); 288 289 vhd_make_footer(&footer, rawsz, VHD_DISK_TYPE_DYNAMIC, sizeof(footer)); 290 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 291 return (errno); 292 293 bat_entries = imgsz / VHD_BLOCK_SIZE; 294 memset(&header, 0, sizeof(header)); 295 be64enc(&header.cookie, VHD_HEADER_COOKIE); 296 be64enc(&header.data_offset, ~0ULL); 297 be64enc(&header.table_offset, sizeof(footer) + sizeof(header)); 298 be32enc(&header.version, VHD_VERSION); 299 be32enc(&header.max_entries, bat_entries); 300 be32enc(&header.block_size, VHD_BLOCK_SIZE); 301 be32enc(&header.checksum, vhd_checksum(&header, sizeof(header))); 302 if (sparse_write(fd, &header, sizeof(header)) < 0) 303 return (errno); 304 305 batsz = bat_entries * sizeof(uint32_t); 306 batsz = (batsz + VHD_SECTOR_SIZE - 1) & ~(VHD_SECTOR_SIZE - 1); 307 bat = malloc(batsz); 308 if (bat == NULL) 309 return (errno); 310 memset(bat, 0xff, batsz); 311 blkcnt = VHD_BLOCK_SIZE / secsz; 312 sector = (sizeof(footer) + sizeof(header) + batsz) / VHD_SECTOR_SIZE; 313 for (entry = 0; entry < bat_entries; entry++) { 314 blk = entry * blkcnt; 315 if (image_data(blk, blkcnt)) { 316 be32enc(&bat[entry], sector); 317 sector += (VHD_BLOCK_SIZE / VHD_SECTOR_SIZE) + 1; 318 } 319 } 320 if (sparse_write(fd, bat, batsz) < 0) { 321 free(bat); 322 return (errno); 323 } 324 free(bat); 325 326 bitmap = malloc(VHD_SECTOR_SIZE); 327 if (bitmap == NULL) 328 return (errno); 329 memset(bitmap, 0xff, VHD_SECTOR_SIZE); 330 331 blk = 0; 332 blkcnt = VHD_BLOCK_SIZE / secsz; 333 error = 0; 334 nblks = rawsz / secsz; 335 while (blk < nblks) { 336 if (!image_data(blk, blkcnt)) { 337 blk += blkcnt; 338 continue; 339 } 340 if (sparse_write(fd, bitmap, VHD_SECTOR_SIZE) < 0) { 341 error = errno; 342 break; 343 } 344 /* Handle partial last block */ 345 if (blk + blkcnt > nblks) 346 blkcnt = nblks - blk; 347 error = image_copyout_region(fd, blk, blkcnt); 348 if (error) 349 break; 350 blk += blkcnt; 351 } 352 free(bitmap); 353 if (error) 354 return (error); 355 error = image_copyout_zeroes(fd, imgsz - rawsz); 356 if (error) 357 return (error); 358 if (sparse_write(fd, &footer, sizeof(footer)) < 0) 359 return (errno); 360 361 return (0); 362 } 363 364 static struct mkimg_format vhd_dyn_format = { 365 .name = "vhd", 366 .description = "Virtual Hard Disk", 367 .resize = vhd_dyn_resize, 368 .write = vhd_dyn_write, 369 }; 370 371 FORMAT_DEFINE(vhd_dyn_format); 372 373 /* 374 * PART 3: Fixed VHD 375 */ 376 377 static int 378 vhd_fix_resize(lba_t imgsz) 379 { 380 uint64_t imagesz; 381 382 imagesz = vhd_resize(imgsz * secsz); 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 imagesz; 395 int error; 396 397 error = image_copyout(fd); 398 if (error) 399 return (error); 400 401 imagesz = image_get_size() * secsz; 402 vhd_make_footer(&footer, imagesz, VHD_DISK_TYPE_FIXED, ~0ULL); 403 error = (sparse_write(fd, &footer, sizeof(footer)) < 0) ? errno : 0; 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