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 <errno.h> 35 #include <err.h> 36 #include <fcntl.h> 37 #include <libutil.h> 38 #include <limits.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sysexits.h> 43 #include <unistd.h> 44 45 #include "mkimg.h" 46 #include "scheme.h" 47 48 #if !defined(SPARSE_WRITE) 49 #define sparse_write write 50 #endif 51 52 #define BUFFER_SIZE (1024*1024) 53 54 struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist); 55 u_int nparts = 0; 56 57 u_int verbose; 58 59 u_int ncyls = 0; 60 u_int nheads = 1; 61 u_int nsecs = 1; 62 u_int secsz = 512; 63 u_int blksz = 0; 64 65 static int bcfd = -1; 66 static int outfd = 0; 67 static int tmpfd = -1; 68 69 static char tmpfname[] = "/tmp/mkimg-XXXXXX"; 70 71 static void 72 cleanup(void) 73 { 74 75 if (tmpfd != -1) 76 close(tmpfd); 77 unlink(tmpfname); 78 } 79 80 static void 81 usage(const char *why) 82 { 83 struct mkimg_scheme *s, **iter; 84 85 warnx("error: %s", why); 86 fprintf(stderr, "\nusage: %s <options>\n", getprogname()); 87 88 fprintf(stderr, " options:\n"); 89 fprintf(stderr, "\t-b <file>\t- file containing boot code\n"); 90 fprintf(stderr, "\t-o <file>\t- file to write image into\n"); 91 fprintf(stderr, "\t-p <partition>\n"); 92 fprintf(stderr, "\t-s <scheme>\n"); 93 fprintf(stderr, "\t-H <num>\t- number of heads to simulate\n"); 94 fprintf(stderr, "\t-P <num>\t- physical sector size\n"); 95 fprintf(stderr, "\t-S <num>\t- logical sector size\n"); 96 fprintf(stderr, "\t-T <num>\t- number of tracks to simulate\n"); 97 98 fprintf(stderr, " schemes:\n"); 99 SET_FOREACH(iter, schemes) { 100 s = *iter; 101 fprintf(stderr, "\t%s\t- %s\n", s->name, s->description); 102 } 103 104 fprintf(stderr, " partition specification:\n"); 105 fprintf(stderr, "\t<t>[/<l>]::<size>\t- empty partition of given " 106 "size\n"); 107 fprintf(stderr, "\t<t>[/<l>]:=<file>\t- partition content and size " 108 "are determined\n\t\t\t\t by the named file\n"); 109 fprintf(stderr, "\t<t>[/<l>]:-<cmd>\t- partition content and size " 110 "are taken from\n\t\t\t\t the output of the command to run\n"); 111 fprintf(stderr, "\t where:\n"); 112 fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n"); 113 fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition " 114 "label\n"); 115 116 exit(EX_USAGE); 117 } 118 119 static int 120 parse_number(u_int *valp, u_int min, u_int max, const char *arg) 121 { 122 uint64_t val; 123 124 if (expand_number(arg, &val) == -1) 125 return (errno); 126 if (val > UINT_MAX || val < (uint64_t)min || val > (uint64_t)max) 127 return (EINVAL); 128 *valp = (u_int)val; 129 return (0); 130 } 131 132 static int 133 pwr_of_two(u_int nr) 134 { 135 136 return (((nr & (nr - 1)) == 0) ? 1 : 0); 137 } 138 139 /* 140 * A partition specification has the following format: 141 * <type> ':' <kind> <contents> 142 * where: 143 * type the partition type alias 144 * kind the interpretation of the contents specification 145 * ':' contents holds the size of an empty partition 146 * '=' contents holds the name of a file to read 147 * '-' contents holds a command to run; the output of 148 * which is the contents of the partition. 149 * contents the specification of a partition's contents 150 */ 151 static int 152 parse_part(const char *spec) 153 { 154 struct part *part; 155 char *sep; 156 size_t len; 157 int error; 158 159 part = calloc(1, sizeof(struct part)); 160 if (part == NULL) 161 return (ENOMEM); 162 163 sep = strchr(spec, ':'); 164 if (sep == NULL) { 165 error = EINVAL; 166 goto errout; 167 } 168 len = sep - spec + 1; 169 if (len < 2) { 170 error = EINVAL; 171 goto errout; 172 } 173 part->alias = malloc(len); 174 if (part->alias == NULL) { 175 error = ENOMEM; 176 goto errout; 177 } 178 strlcpy(part->alias, spec, len); 179 spec = sep + 1; 180 181 switch (*spec) { 182 case ':': 183 part->kind = PART_KIND_SIZE; 184 break; 185 case '=': 186 part->kind = PART_KIND_FILE; 187 break; 188 case '-': 189 part->kind = PART_KIND_PIPE; 190 break; 191 default: 192 error = EINVAL; 193 goto errout; 194 } 195 spec++; 196 197 part->contents = strdup(spec); 198 if (part->contents == NULL) { 199 error = ENOMEM; 200 goto errout; 201 } 202 203 spec = part->alias; 204 sep = strchr(spec, '/'); 205 if (sep != NULL) { 206 *sep++ = '\0'; 207 if (strlen(part->alias) == 0 || strlen(sep) == 0) { 208 error = EINVAL; 209 goto errout; 210 } 211 part->label = strdup(sep); 212 if (part->label == NULL) { 213 error = ENOMEM; 214 goto errout; 215 } 216 } 217 218 part->index = nparts; 219 STAILQ_INSERT_TAIL(&partlist, part, link); 220 nparts++; 221 return (0); 222 223 errout: 224 if (part->alias != NULL) 225 free(part->alias); 226 free(part); 227 return (error); 228 } 229 230 #if defined(SPARSE_WRITE) 231 static ssize_t 232 sparse_write(int fd, const char *buf, size_t sz) 233 { 234 const char *p; 235 off_t ofs; 236 size_t len; 237 ssize_t wr, wrsz; 238 239 wrsz = 0; 240 p = memchr(buf, 0, sz); 241 while (sz > 0) { 242 len = (p != NULL) ? (size_t)(p - buf) : sz; 243 if (len > 0) { 244 len = (len + secsz - 1) & ~(secsz - 1); 245 if (len > sz) 246 len = sz; 247 wr = write(fd, buf, len); 248 if (wr < 0) 249 return (-1); 250 } else { 251 while (len < sz && *p++ == '\0') 252 len++; 253 if (len < sz) 254 len &= ~(secsz - 1); 255 if (len == 0) 256 continue; 257 ofs = lseek(fd, len, SEEK_CUR); 258 if (ofs < 0) 259 return (-1); 260 wr = len; 261 } 262 buf += wr; 263 sz -= wr; 264 wrsz += wr; 265 p = memchr(buf, 0, sz); 266 } 267 return (wrsz); 268 } 269 #endif /* SPARSE_WRITE */ 270 271 static int 272 fdcopy(int src, int dst, uint64_t *count) 273 { 274 char *buffer; 275 off_t ofs; 276 ssize_t rdsz, wrsz; 277 278 /* A return value of -1 means that we can't write a sparse file. */ 279 ofs = lseek(dst, 0L, SEEK_CUR); 280 281 if (count != NULL) 282 *count = 0; 283 284 buffer = malloc(BUFFER_SIZE); 285 if (buffer == NULL) 286 return (errno); 287 while (1) { 288 rdsz = read(src, buffer, BUFFER_SIZE); 289 if (rdsz <= 0) { 290 free(buffer); 291 return ((rdsz < 0) ? errno : 0); 292 } 293 if (count != NULL) 294 *count += rdsz; 295 wrsz = (ofs == -1) ? 296 write(dst, buffer, rdsz) : 297 sparse_write(dst, buffer, rdsz); 298 if (wrsz < 0) 299 break; 300 } 301 free(buffer); 302 return (errno); 303 } 304 305 int 306 mkimg_seek(int fd, lba_t blk) 307 { 308 off_t off; 309 310 off = blk * secsz; 311 if (lseek(fd, off, SEEK_SET) != off) 312 return (errno); 313 return (0); 314 } 315 316 static void 317 mkimg(int bfd) 318 { 319 FILE *fp; 320 struct part *part; 321 lba_t block; 322 off_t bytesize; 323 int error, fd; 324 325 error = scheme_bootcode(bfd); 326 if (error) 327 errc(EX_DATAERR, error, "boot code"); 328 329 /* First check partition information */ 330 STAILQ_FOREACH(part, &partlist, link) { 331 error = scheme_check_part(part); 332 if (error) 333 errc(EX_DATAERR, error, "partition %d", part->index+1); 334 } 335 336 block = scheme_metadata(SCHEME_META_IMG_START, 0); 337 STAILQ_FOREACH(part, &partlist, link) { 338 block = scheme_metadata(SCHEME_META_PART_BEFORE, block); 339 if (verbose) 340 fprintf(stderr, "partition %d: starting block %llu " 341 "... ", part->index + 1, (long long)block); 342 part->block = block; 343 error = mkimg_seek(tmpfd, block); 344 switch (part->kind) { 345 case PART_KIND_SIZE: 346 if (expand_number(part->contents, &bytesize) == -1) 347 error = errno; 348 break; 349 case PART_KIND_FILE: 350 fd = open(part->contents, O_RDONLY, 0); 351 if (fd != -1) { 352 error = fdcopy(fd, tmpfd, &bytesize); 353 close(fd); 354 } else 355 error = errno; 356 break; 357 case PART_KIND_PIPE: 358 fp = popen(part->contents, "r"); 359 if (fp != NULL) { 360 error = fdcopy(fileno(fp), tmpfd, &bytesize); 361 pclose(fp); 362 } else 363 error = errno; 364 break; 365 } 366 if (error) 367 errc(EX_IOERR, error, "partition %d", part->index + 1); 368 part->size = (bytesize + secsz - 1) / secsz; 369 if (verbose) { 370 bytesize = part->size * secsz; 371 fprintf(stderr, "size %llu bytes (%llu blocks)\n", 372 (long long)bytesize, (long long)part->size); 373 } 374 block = scheme_metadata(SCHEME_META_PART_AFTER, 375 part->block + part->size); 376 } 377 378 block = scheme_metadata(SCHEME_META_IMG_END, block); 379 error = (scheme_write(tmpfd, block)); 380 } 381 382 int 383 main(int argc, char *argv[]) 384 { 385 int c, error; 386 387 while ((c = getopt(argc, argv, "b:o:p:s:vH:P:S:T:")) != -1) { 388 switch (c) { 389 case 'b': /* BOOT CODE */ 390 if (bcfd != -1) 391 usage("multiple bootcode given"); 392 bcfd = open(optarg, O_RDONLY, 0); 393 if (bcfd == -1) 394 err(EX_UNAVAILABLE, "%s", optarg); 395 break; 396 case 'o': /* OUTPUT FILE */ 397 if (outfd != 0) 398 usage("multiple output files given"); 399 outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 400 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); 401 if (outfd == -1) 402 err(EX_CANTCREAT, "%s", optarg); 403 break; 404 case 'p': /* PARTITION */ 405 error = parse_part(optarg); 406 if (error) 407 errc(EX_DATAERR, error, "partition"); 408 break; 409 case 's': /* SCHEME */ 410 if (scheme_selected() != NULL) 411 usage("multiple schemes given"); 412 error = scheme_select(optarg); 413 if (error) 414 errc(EX_DATAERR, error, "scheme"); 415 break; 416 case 'v': 417 verbose++; 418 break; 419 case 'H': /* GEOMETRY: HEADS */ 420 error = parse_number(&nheads, 1, 255, optarg); 421 if (error) 422 errc(EX_DATAERR, error, "number of heads"); 423 break; 424 case 'P': /* GEOMETRY: PHYSICAL SECTOR SIZE */ 425 error = parse_number(&blksz, 512, INT_MAX+1U, optarg); 426 if (error == 0 && !pwr_of_two(blksz)) 427 error = EINVAL; 428 if (error) 429 errc(EX_DATAERR, error, "physical sector size"); 430 break; 431 case 'S': /* GEOMETRY: LOGICAL SECTOR SIZE */ 432 error = parse_number(&secsz, 512, INT_MAX+1U, optarg); 433 if (error == 0 && !pwr_of_two(secsz)) 434 error = EINVAL; 435 if (error) 436 errc(EX_DATAERR, error, "logical sector size"); 437 break; 438 case 'T': /* GEOMETRY: TRACK SIZE */ 439 error = parse_number(&nsecs, 1, 63, optarg); 440 if (error) 441 errc(EX_DATAERR, error, "track size"); 442 break; 443 default: 444 usage("unknown option"); 445 } 446 } 447 448 if (argc > optind) 449 usage("trailing arguments"); 450 if (scheme_selected() == NULL) 451 usage("no scheme"); 452 if (nparts == 0) 453 usage("no partitions"); 454 455 if (secsz > blksz) { 456 if (blksz != 0) 457 errx(EX_DATAERR, "the physical block size cannot " 458 "be smaller than the sector size"); 459 blksz = secsz; 460 } 461 462 if (secsz > scheme_max_secsz()) 463 errx(EX_DATAERR, "maximum sector size supported is %u; " 464 "size specified is %u", scheme_max_secsz(), secsz); 465 466 if (nparts > scheme_max_parts()) 467 errx(EX_DATAERR, "%d partitions supported; %d given", 468 scheme_max_parts(), nparts); 469 470 if (outfd == 0) { 471 if (atexit(cleanup) == -1) 472 err(EX_OSERR, "cannot register cleanup function"); 473 outfd = 1; 474 tmpfd = mkstemp(tmpfname); 475 if (tmpfd == -1) 476 err(EX_OSERR, "cannot create temporary file"); 477 } else 478 tmpfd = outfd; 479 480 if (verbose) { 481 fprintf(stderr, "Logical sector size: %u\n", secsz); 482 fprintf(stderr, "Physical block size: %u\n", blksz); 483 fprintf(stderr, "Sectors per track: %u\n", nsecs); 484 fprintf(stderr, "Number of heads: %u\n", nheads); 485 } 486 487 mkimg(bcfd); 488 489 if (verbose) 490 fprintf(stderr, "Number of cylinders: %u\n", ncyls); 491 492 if (tmpfd != outfd) { 493 error = mkimg_seek(tmpfd, 0); 494 if (error == 0) 495 error = fdcopy(tmpfd, outfd, NULL); 496 if (error) 497 errc(EX_IOERR, error, "writing to stdout"); 498 } 499 500 return (0); 501 } 502