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