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