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