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