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