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