1 /*- 2 * Copyright (c) 2013,2014 Juniper Networks, Inc. 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/queue.h> 31 #include <sys/stat.h> 32 #include <sys/types.h> 33 #include <sys/uuid.h> 34 #include <errno.h> 35 #include <err.h> 36 #include <fcntl.h> 37 #include <getopt.h> 38 #include <libutil.h> 39 #include <limits.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <sysexits.h> 44 #include <unistd.h> 45 46 #include "image.h" 47 #include "format.h" 48 #include "mkimg.h" 49 #include "scheme.h" 50 51 #define LONGOPT_FORMATS 0x01000001 52 #define LONGOPT_SCHEMES 0x01000002 53 #define LONGOPT_VERSION 0x01000003 54 55 static struct option longopts[] = { 56 { "formats", no_argument, NULL, LONGOPT_FORMATS }, 57 { "schemes", no_argument, NULL, LONGOPT_SCHEMES }, 58 { "version", no_argument, NULL, LONGOPT_VERSION }, 59 { NULL, 0, NULL, 0 } 60 }; 61 62 static uint64_t capacity; 63 64 struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist); 65 u_int nparts = 0; 66 67 u_int unit_testing; 68 u_int verbose; 69 70 u_int ncyls = 0; 71 u_int nheads = 1; 72 u_int nsecs = 1; 73 u_int secsz = 512; 74 u_int blksz = 0; 75 76 static void 77 print_formats(int usage) 78 { 79 struct mkimg_format *f; 80 const char *sep; 81 82 if (usage) { 83 fprintf(stderr, " formats:\n"); 84 f = NULL; 85 while ((f = format_iterate(f)) != NULL) { 86 fprintf(stderr, "\t%s\t- %s\n", f->name, 87 f->description); 88 } 89 } else { 90 sep = ""; 91 f = NULL; 92 while ((f = format_iterate(f)) != NULL) { 93 printf("%s%s", sep, f->name); 94 sep = " "; 95 } 96 putchar('\n'); 97 } 98 } 99 100 static void 101 print_schemes(int usage) 102 { 103 struct mkimg_scheme *s; 104 const char *sep; 105 106 if (usage) { 107 fprintf(stderr, " schemes:\n"); 108 s = NULL; 109 while ((s = scheme_iterate(s)) != NULL) { 110 fprintf(stderr, "\t%s\t- %s\n", s->name, 111 s->description); 112 } 113 } else { 114 sep = ""; 115 s = NULL; 116 while ((s = scheme_iterate(s)) != NULL) { 117 printf("%s%s", sep, s->name); 118 sep = " "; 119 } 120 putchar('\n'); 121 } 122 } 123 124 static void 125 print_version(void) 126 { 127 u_int width; 128 129 #ifdef __LP64__ 130 width = 64; 131 #else 132 width = 32; 133 #endif 134 printf("mkimg %u (%u-bit)\n", MKIMG_VERSION, width); 135 } 136 137 static void 138 usage(const char *why) 139 { 140 141 warnx("error: %s", why); 142 fputc('\n', stderr); 143 fprintf(stderr, "usage: %s <options>\n", getprogname()); 144 145 fprintf(stderr, " options:\n"); 146 fprintf(stderr, "\t--formats\t- list image formats\n"); 147 fprintf(stderr, "\t--schemes\t- list partition schemes\n"); 148 fprintf(stderr, "\t--version\t- show version information\n"); 149 fputc('\n', stderr); 150 fprintf(stderr, "\t-b <file>\t- file containing boot code\n"); 151 fprintf(stderr, "\t-c <num>\t- capacity (in bytes) of the disk\n"); 152 fprintf(stderr, "\t-f <format>\n"); 153 fprintf(stderr, "\t-o <file>\t- file to write image into\n"); 154 fprintf(stderr, "\t-p <partition>\n"); 155 fprintf(stderr, "\t-s <scheme>\n"); 156 fprintf(stderr, "\t-v\t\t- increase verbosity\n"); 157 fprintf(stderr, "\t-y\t\t- [developers] enable unit test\n"); 158 fprintf(stderr, "\t-H <num>\t- number of heads to simulate\n"); 159 fprintf(stderr, "\t-P <num>\t- physical sector size\n"); 160 fprintf(stderr, "\t-S <num>\t- logical sector size\n"); 161 fprintf(stderr, "\t-T <num>\t- number of tracks to simulate\n"); 162 fputc('\n', stderr); 163 print_formats(1); 164 fputc('\n', stderr); 165 print_schemes(1); 166 fputc('\n', stderr); 167 fprintf(stderr, " partition specification:\n"); 168 fprintf(stderr, "\t<t>[/<l>]::<size>\t- empty partition of given " 169 "size\n"); 170 fprintf(stderr, "\t<t>[/<l>]:=<file>\t- partition content and size " 171 "are determined\n\t\t\t\t by the named file\n"); 172 fprintf(stderr, "\t<t>[/<l>]:-<cmd>\t- partition content and size " 173 "are taken from\n\t\t\t\t the output of the command to run\n"); 174 fprintf(stderr, "\t-\t\t\t- unused partition entry\n"); 175 fprintf(stderr, "\t where:\n"); 176 fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n"); 177 fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition " 178 "label\n"); 179 180 exit(EX_USAGE); 181 } 182 183 static int 184 parse_uint32(uint32_t *valp, uint32_t min, uint32_t max, const char *arg) 185 { 186 uint64_t val; 187 188 if (expand_number(arg, &val) == -1) 189 return (errno); 190 if (val > UINT_MAX || val < (uint64_t)min || val > (uint64_t)max) 191 return (EINVAL); 192 *valp = (uint32_t)val; 193 return (0); 194 } 195 196 static int 197 parse_uint64(uint64_t *valp, uint64_t min, uint64_t max, const char *arg) 198 { 199 uint64_t val; 200 201 if (expand_number(arg, &val) == -1) 202 return (errno); 203 if (val < min || val > max) 204 return (EINVAL); 205 *valp = val; 206 return (0); 207 } 208 209 static int 210 pwr_of_two(u_int nr) 211 { 212 213 return (((nr & (nr - 1)) == 0) ? 1 : 0); 214 } 215 216 /* 217 * A partition specification has the following format: 218 * <type> ':' <kind> <contents> 219 * where: 220 * type the partition type alias 221 * kind the interpretation of the contents specification 222 * ':' contents holds the size of an empty partition 223 * '=' contents holds the name of a file to read 224 * '-' contents holds a command to run; the output of 225 * which is the contents of the partition. 226 * contents the specification of a partition's contents 227 * 228 * A specification that is a single dash indicates an unused partition 229 * entry. 230 */ 231 static int 232 parse_part(const char *spec) 233 { 234 struct part *part; 235 char *sep; 236 size_t len; 237 int error; 238 239 if (strcmp(spec, "-") == 0) { 240 nparts++; 241 return (0); 242 } 243 244 part = calloc(1, sizeof(struct part)); 245 if (part == NULL) 246 return (ENOMEM); 247 248 sep = strchr(spec, ':'); 249 if (sep == NULL) { 250 error = EINVAL; 251 goto errout; 252 } 253 len = sep - spec + 1; 254 if (len < 2) { 255 error = EINVAL; 256 goto errout; 257 } 258 part->alias = malloc(len); 259 if (part->alias == NULL) { 260 error = ENOMEM; 261 goto errout; 262 } 263 strlcpy(part->alias, spec, len); 264 spec = sep + 1; 265 266 switch (*spec) { 267 case ':': 268 part->kind = PART_KIND_SIZE; 269 break; 270 case '=': 271 part->kind = PART_KIND_FILE; 272 break; 273 case '-': 274 part->kind = PART_KIND_PIPE; 275 break; 276 default: 277 error = EINVAL; 278 goto errout; 279 } 280 spec++; 281 282 part->contents = strdup(spec); 283 if (part->contents == NULL) { 284 error = ENOMEM; 285 goto errout; 286 } 287 288 spec = part->alias; 289 sep = strchr(spec, '/'); 290 if (sep != NULL) { 291 *sep++ = '\0'; 292 if (strlen(part->alias) == 0 || strlen(sep) == 0) { 293 error = EINVAL; 294 goto errout; 295 } 296 part->label = strdup(sep); 297 if (part->label == NULL) { 298 error = ENOMEM; 299 goto errout; 300 } 301 } 302 303 part->index = nparts; 304 STAILQ_INSERT_TAIL(&partlist, part, link); 305 nparts++; 306 return (0); 307 308 errout: 309 if (part->alias != NULL) 310 free(part->alias); 311 free(part); 312 return (error); 313 } 314 315 #if defined(SPARSE_WRITE) 316 ssize_t 317 sparse_write(int fd, const void *ptr, size_t sz) 318 { 319 const char *buf, *p; 320 off_t ofs; 321 size_t len; 322 ssize_t wr, wrsz; 323 324 buf = ptr; 325 wrsz = 0; 326 p = memchr(buf, 0, sz); 327 while (sz > 0) { 328 len = (p != NULL) ? (size_t)(p - buf) : sz; 329 if (len > 0) { 330 len = (len + secsz - 1) & ~(secsz - 1); 331 if (len > sz) 332 len = sz; 333 wr = write(fd, buf, len); 334 if (wr < 0) 335 return (-1); 336 } else { 337 while (len < sz && *p++ == '\0') 338 len++; 339 if (len < sz) 340 len &= ~(secsz - 1); 341 if (len == 0) 342 continue; 343 ofs = lseek(fd, len, SEEK_CUR); 344 if (ofs < 0) 345 return (-1); 346 wr = len; 347 } 348 buf += wr; 349 sz -= wr; 350 wrsz += wr; 351 p = memchr(buf, 0, sz); 352 } 353 return (wrsz); 354 } 355 #endif /* SPARSE_WRITE */ 356 357 void 358 mkimg_chs(lba_t lba, u_int maxcyl, u_int *cylp, u_int *hdp, u_int *secp) 359 { 360 u_int hd, sec; 361 362 *cylp = *hdp = *secp = ~0U; 363 if (nsecs == 1 || nheads == 1) 364 return; 365 366 sec = lba % nsecs + 1; 367 lba /= nsecs; 368 hd = lba % nheads; 369 lba /= nheads; 370 if (lba > maxcyl) 371 return; 372 373 *cylp = lba; 374 *hdp = hd; 375 *secp = sec; 376 } 377 378 void 379 mkimg_uuid(struct uuid *uuid) 380 { 381 static uint8_t gen[sizeof(struct uuid)]; 382 u_int i; 383 384 if (!unit_testing) { 385 uuidgen(uuid, 1); 386 return; 387 } 388 389 for (i = 0; i < sizeof(gen); i++) 390 gen[i]++; 391 memcpy(uuid, gen, sizeof(uuid_t)); 392 } 393 394 static int 395 capacity_resize(lba_t end) 396 { 397 lba_t capsz; 398 399 capsz = (capacity + secsz - 1) / secsz; 400 if (end >= capsz) 401 return (0); 402 return (image_set_size(capsz)); 403 } 404 405 static void 406 mkimg(void) 407 { 408 FILE *fp; 409 struct part *part; 410 lba_t block; 411 off_t bytesize; 412 int error, fd; 413 414 /* First check partition information */ 415 STAILQ_FOREACH(part, &partlist, link) { 416 error = scheme_check_part(part); 417 if (error) 418 errc(EX_DATAERR, error, "partition %d", part->index+1); 419 } 420 421 block = scheme_metadata(SCHEME_META_IMG_START, 0); 422 STAILQ_FOREACH(part, &partlist, link) { 423 block = scheme_metadata(SCHEME_META_PART_BEFORE, block); 424 if (verbose) 425 fprintf(stderr, "partition %d: starting block %llu " 426 "... ", part->index + 1, (long long)block); 427 part->block = block; 428 switch (part->kind) { 429 case PART_KIND_SIZE: 430 if (expand_number(part->contents, &bytesize) == -1) 431 error = errno; 432 break; 433 case PART_KIND_FILE: 434 fd = open(part->contents, O_RDONLY, 0); 435 if (fd != -1) { 436 error = image_copyin(block, fd, &bytesize); 437 close(fd); 438 } else 439 error = errno; 440 break; 441 case PART_KIND_PIPE: 442 fp = popen(part->contents, "r"); 443 if (fp != NULL) { 444 fd = fileno(fp); 445 error = image_copyin(block, fd, &bytesize); 446 pclose(fp); 447 } else 448 error = errno; 449 break; 450 } 451 if (error) 452 errc(EX_IOERR, error, "partition %d", part->index + 1); 453 part->size = (bytesize + secsz - 1) / secsz; 454 if (verbose) { 455 bytesize = part->size * secsz; 456 fprintf(stderr, "size %llu bytes (%llu blocks)\n", 457 (long long)bytesize, (long long)part->size); 458 } 459 block = scheme_metadata(SCHEME_META_PART_AFTER, 460 part->block + part->size); 461 } 462 463 block = scheme_metadata(SCHEME_META_IMG_END, block); 464 error = image_set_size(block); 465 if (!error) { 466 error = capacity_resize(block); 467 block = image_get_size(); 468 } 469 if (!error) { 470 error = format_resize(block); 471 block = image_get_size(); 472 } 473 if (error) 474 errc(EX_IOERR, error, "image sizing"); 475 ncyls = block / (nsecs * nheads); 476 error = scheme_write(block); 477 if (error) 478 errc(EX_IOERR, error, "writing metadata"); 479 } 480 481 int 482 main(int argc, char *argv[]) 483 { 484 int bcfd, outfd; 485 int c, error; 486 487 bcfd = -1; 488 outfd = 1; /* Write to stdout by default */ 489 while ((c = getopt_long(argc, argv, "b:c:f:o:p:s:vyH:P:S:T:", 490 longopts, NULL)) != -1) { 491 switch (c) { 492 case 'b': /* BOOT CODE */ 493 if (bcfd != -1) 494 usage("multiple bootcode given"); 495 bcfd = open(optarg, O_RDONLY, 0); 496 if (bcfd == -1) 497 err(EX_UNAVAILABLE, "%s", optarg); 498 break; 499 case 'c': /* CAPACITY */ 500 error = parse_uint64(&capacity, 1, OFF_MAX, optarg); 501 if (error) 502 errc(EX_DATAERR, error, "capacity in bytes"); 503 break; 504 case 'f': /* OUTPUT FORMAT */ 505 if (format_selected() != NULL) 506 usage("multiple formats given"); 507 error = format_select(optarg); 508 if (error) 509 errc(EX_DATAERR, error, "format"); 510 break; 511 case 'o': /* OUTPUT FILE */ 512 if (outfd != 1) 513 usage("multiple output files given"); 514 outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 515 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); 516 if (outfd == -1) 517 err(EX_CANTCREAT, "%s", optarg); 518 break; 519 case 'p': /* PARTITION */ 520 error = parse_part(optarg); 521 if (error) 522 errc(EX_DATAERR, error, "partition"); 523 break; 524 case 's': /* SCHEME */ 525 if (scheme_selected() != NULL) 526 usage("multiple schemes given"); 527 error = scheme_select(optarg); 528 if (error) 529 errc(EX_DATAERR, error, "scheme"); 530 break; 531 case 'y': 532 unit_testing++; 533 break; 534 case 'v': 535 verbose++; 536 break; 537 case 'H': /* GEOMETRY: HEADS */ 538 error = parse_uint32(&nheads, 1, 255, optarg); 539 if (error) 540 errc(EX_DATAERR, error, "number of heads"); 541 break; 542 case 'P': /* GEOMETRY: PHYSICAL SECTOR SIZE */ 543 error = parse_uint32(&blksz, 512, INT_MAX+1U, optarg); 544 if (error == 0 && !pwr_of_two(blksz)) 545 error = EINVAL; 546 if (error) 547 errc(EX_DATAERR, error, "physical sector size"); 548 break; 549 case 'S': /* GEOMETRY: LOGICAL SECTOR SIZE */ 550 error = parse_uint32(&secsz, 512, INT_MAX+1U, optarg); 551 if (error == 0 && !pwr_of_two(secsz)) 552 error = EINVAL; 553 if (error) 554 errc(EX_DATAERR, error, "logical sector size"); 555 break; 556 case 'T': /* GEOMETRY: TRACK SIZE */ 557 error = parse_uint32(&nsecs, 1, 63, optarg); 558 if (error) 559 errc(EX_DATAERR, error, "track size"); 560 break; 561 case LONGOPT_FORMATS: 562 print_formats(0); 563 exit(EX_OK); 564 /*NOTREACHED*/ 565 case LONGOPT_SCHEMES: 566 print_schemes(0); 567 exit(EX_OK); 568 /*NOTREACHED*/ 569 case LONGOPT_VERSION: 570 print_version(); 571 exit(EX_OK); 572 /*NOTREACHED*/ 573 default: 574 usage("unknown option"); 575 } 576 } 577 578 if (argc > optind) 579 usage("trailing arguments"); 580 if (scheme_selected() == NULL && nparts > 0) 581 usage("no scheme"); 582 if (nparts == 0 && capacity == 0) 583 usage("no partitions"); 584 585 if (secsz > blksz) { 586 if (blksz != 0) 587 errx(EX_DATAERR, "the physical block size cannot " 588 "be smaller than the sector size"); 589 blksz = secsz; 590 } 591 592 if (secsz > scheme_max_secsz()) 593 errx(EX_DATAERR, "maximum sector size supported is %u; " 594 "size specified is %u", scheme_max_secsz(), secsz); 595 596 if (nparts > scheme_max_parts()) 597 errx(EX_DATAERR, "%d partitions supported; %d given", 598 scheme_max_parts(), nparts); 599 600 if (format_selected() == NULL) 601 format_select("raw"); 602 603 if (bcfd != -1) { 604 error = scheme_bootcode(bcfd); 605 close(bcfd); 606 if (error) 607 errc(EX_DATAERR, error, "boot code"); 608 } 609 610 if (verbose) { 611 fprintf(stderr, "Logical sector size: %u\n", secsz); 612 fprintf(stderr, "Physical block size: %u\n", blksz); 613 fprintf(stderr, "Sectors per track: %u\n", nsecs); 614 fprintf(stderr, "Number of heads: %u\n", nheads); 615 fputc('\n', stderr); 616 if (scheme_selected()) 617 fprintf(stderr, "Partitioning scheme: %s\n", 618 scheme_selected()->name); 619 fprintf(stderr, "Output file format: %s\n", 620 format_selected()->name); 621 fputc('\n', stderr); 622 } 623 624 error = image_init(); 625 if (error) 626 errc(EX_OSERR, error, "cannot initialize"); 627 628 mkimg(); 629 630 if (verbose) { 631 fputc('\n', stderr); 632 fprintf(stderr, "Number of cylinders: %u\n", ncyls); 633 } 634 635 error = format_write(outfd); 636 if (error) 637 errc(EX_IOERR, error, "writing image"); 638 639 return (0); 640 } 641