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