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