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