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 <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <sysexits.h> 42#include <unistd.h> 43 44#include "mkimg.h" 45#include "scheme.h" 46 47#define BUFFER_SIZE (1024*1024) 48 49struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist); 50u_int nparts = 0; 51
| 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 <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <sysexits.h> 42#include <unistd.h> 43 44#include "mkimg.h" 45#include "scheme.h" 46 47#define BUFFER_SIZE (1024*1024) 48 49struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist); 50u_int nparts = 0; 51
|
| 52u_int secsz = 512; 53
|
52static int bcfd = -1; 53static int outfd = 0; 54static int tmpfd = -1; 55 56static char tmpfname[] = "/tmp/mkimg-XXXXXX"; 57 58static void 59cleanup(void) 60{ 61 62 if (tmpfd != -1) 63 close(tmpfd); 64 unlink(tmpfname); 65} 66 67static void 68usage(const char *why) 69{ 70 struct mkimg_scheme *s, **iter; 71 72 warnx("error: %s", why); 73 fprintf(stderr, "\nusage: %s <options>\n", getprogname()); 74 75 fprintf(stderr, " options:\n"); 76 fprintf(stderr, "\t-b <file>\t- file containing boot code\n"); 77 fprintf(stderr, "\t-h <num>\t- number of heads to simulate\n"); 78 fprintf(stderr, "\t-o <file>\t- file to write image into\n"); 79 fprintf(stderr, "\t-p <partition>\n"); 80 fprintf(stderr, "\t-s <scheme>\n"); 81 fprintf(stderr, "\t-t <num>\t- number of tracks to simulate\n"); 82 fprintf(stderr, "\t-z\t\t- write a sparse file\n"); 83 84 fprintf(stderr, " schemes:\n"); 85 SET_FOREACH(iter, schemes) { 86 s = *iter; 87 fprintf(stderr, "\t%s\t- %s\n", s->name, s->description); 88 } 89 90 fprintf(stderr, " partition specification:\n"); 91 fprintf(stderr, "\t<t>[/<l>]::<size>\t- empty partition of given " 92 "size\n"); 93 fprintf(stderr, "\t<t>[/<l>]:=<file>\t- partition content and size " 94 "are determined\n\t\t\t\t by the named file\n"); 95 fprintf(stderr, "\t<t>[/<l>]:!<cmd>\t- partition content and size " 96 "are taken from\n\t\t\t\t the output of the command to run\n"); 97 fprintf(stderr, "\t where:\n"); 98 fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n"); 99 fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition " 100 "label\n"); 101 102 exit(EX_USAGE); 103} 104 105/* 106 * A partition specification has the following format: 107 * <type> ':' <kind> <contents> 108 * where: 109 * type the partition type alias 110 * kind the interpretation of the contents specification 111 * ':' contents holds the size of an empty partition 112 * '=' contents holds the name of a file to read 113 * '!' contents holds a command to run; the output of 114 * which is the contents of the partition. 115 * contents the specification of a partition's contents 116 */ 117static int 118parse_part(const char *spec) 119{ 120 struct part *part; 121 char *sep; 122 size_t len; 123 int error; 124 125 part = calloc(1, sizeof(struct part)); 126 if (part == NULL) 127 return (ENOMEM); 128 129 sep = strchr(spec, ':'); 130 if (sep == NULL) { 131 error = EINVAL; 132 goto errout; 133 } 134 len = sep - spec + 1; 135 if (len < 2) { 136 error = EINVAL; 137 goto errout; 138 } 139 part->alias = malloc(len); 140 if (part->alias == NULL) { 141 error = ENOMEM; 142 goto errout; 143 } 144 strlcpy(part->alias, spec, len); 145 spec = sep + 1; 146 147 switch (*spec) { 148 case ':': 149 part->kind = PART_KIND_SIZE; 150 break; 151 case '=': 152 part->kind = PART_KIND_FILE; 153 break; 154 case '!': 155 part->kind = PART_KIND_PIPE; 156 break; 157 default: 158 error = EINVAL; 159 goto errout; 160 } 161 spec++; 162 163 part->contents = strdup(spec); 164 if (part->contents == NULL) { 165 error = ENOMEM; 166 goto errout; 167 } 168 169 spec = part->alias; 170 sep = strchr(spec, '/'); 171 if (sep != NULL) { 172 *sep++ = '\0'; 173 if (strlen(part->alias) == 0 || strlen(sep) == 0) { 174 error = EINVAL; 175 goto errout; 176 } 177 part->label = strdup(sep); 178 if (part->label == NULL) { 179 error = ENOMEM; 180 goto errout; 181 } 182 } 183 184 part->index = nparts; 185 STAILQ_INSERT_TAIL(&partlist, part, link); 186 nparts++; 187 return (0); 188 189 errout: 190 if (part->alias != NULL) 191 free(part->alias); 192 free(part); 193 return (error); 194} 195 196static int 197fdcopy(int src, int dst, uint64_t *count) 198{ 199 void *buffer; 200 ssize_t rdsz, wrsz; 201 202 if (count != 0) 203 *count = 0; 204 205 buffer = malloc(BUFFER_SIZE); 206 if (buffer == NULL) 207 return (errno); 208 while (1) { 209 rdsz = read(src, buffer, BUFFER_SIZE); 210 if (rdsz <= 0) { 211 free(buffer); 212 return ((rdsz < 0) ? errno : 0); 213 } 214 if (count != NULL) 215 *count += rdsz; 216 wrsz = write(dst, buffer, rdsz); 217 if (wrsz < 0) 218 break; 219 } 220 free(buffer); 221 return (errno); 222} 223
| 54static int bcfd = -1; 55static int outfd = 0; 56static int tmpfd = -1; 57 58static char tmpfname[] = "/tmp/mkimg-XXXXXX"; 59 60static void 61cleanup(void) 62{ 63 64 if (tmpfd != -1) 65 close(tmpfd); 66 unlink(tmpfname); 67} 68 69static void 70usage(const char *why) 71{ 72 struct mkimg_scheme *s, **iter; 73 74 warnx("error: %s", why); 75 fprintf(stderr, "\nusage: %s <options>\n", getprogname()); 76 77 fprintf(stderr, " options:\n"); 78 fprintf(stderr, "\t-b <file>\t- file containing boot code\n"); 79 fprintf(stderr, "\t-h <num>\t- number of heads to simulate\n"); 80 fprintf(stderr, "\t-o <file>\t- file to write image into\n"); 81 fprintf(stderr, "\t-p <partition>\n"); 82 fprintf(stderr, "\t-s <scheme>\n"); 83 fprintf(stderr, "\t-t <num>\t- number of tracks to simulate\n"); 84 fprintf(stderr, "\t-z\t\t- write a sparse file\n"); 85 86 fprintf(stderr, " schemes:\n"); 87 SET_FOREACH(iter, schemes) { 88 s = *iter; 89 fprintf(stderr, "\t%s\t- %s\n", s->name, s->description); 90 } 91 92 fprintf(stderr, " partition specification:\n"); 93 fprintf(stderr, "\t<t>[/<l>]::<size>\t- empty partition of given " 94 "size\n"); 95 fprintf(stderr, "\t<t>[/<l>]:=<file>\t- partition content and size " 96 "are determined\n\t\t\t\t by the named file\n"); 97 fprintf(stderr, "\t<t>[/<l>]:!<cmd>\t- partition content and size " 98 "are taken from\n\t\t\t\t the output of the command to run\n"); 99 fprintf(stderr, "\t where:\n"); 100 fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n"); 101 fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition " 102 "label\n"); 103 104 exit(EX_USAGE); 105} 106 107/* 108 * A partition specification has the following format: 109 * <type> ':' <kind> <contents> 110 * where: 111 * type the partition type alias 112 * kind the interpretation of the contents specification 113 * ':' contents holds the size of an empty partition 114 * '=' contents holds the name of a file to read 115 * '!' contents holds a command to run; the output of 116 * which is the contents of the partition. 117 * contents the specification of a partition's contents 118 */ 119static int 120parse_part(const char *spec) 121{ 122 struct part *part; 123 char *sep; 124 size_t len; 125 int error; 126 127 part = calloc(1, sizeof(struct part)); 128 if (part == NULL) 129 return (ENOMEM); 130 131 sep = strchr(spec, ':'); 132 if (sep == NULL) { 133 error = EINVAL; 134 goto errout; 135 } 136 len = sep - spec + 1; 137 if (len < 2) { 138 error = EINVAL; 139 goto errout; 140 } 141 part->alias = malloc(len); 142 if (part->alias == NULL) { 143 error = ENOMEM; 144 goto errout; 145 } 146 strlcpy(part->alias, spec, len); 147 spec = sep + 1; 148 149 switch (*spec) { 150 case ':': 151 part->kind = PART_KIND_SIZE; 152 break; 153 case '=': 154 part->kind = PART_KIND_FILE; 155 break; 156 case '!': 157 part->kind = PART_KIND_PIPE; 158 break; 159 default: 160 error = EINVAL; 161 goto errout; 162 } 163 spec++; 164 165 part->contents = strdup(spec); 166 if (part->contents == NULL) { 167 error = ENOMEM; 168 goto errout; 169 } 170 171 spec = part->alias; 172 sep = strchr(spec, '/'); 173 if (sep != NULL) { 174 *sep++ = '\0'; 175 if (strlen(part->alias) == 0 || strlen(sep) == 0) { 176 error = EINVAL; 177 goto errout; 178 } 179 part->label = strdup(sep); 180 if (part->label == NULL) { 181 error = ENOMEM; 182 goto errout; 183 } 184 } 185 186 part->index = nparts; 187 STAILQ_INSERT_TAIL(&partlist, part, link); 188 nparts++; 189 return (0); 190 191 errout: 192 if (part->alias != NULL) 193 free(part->alias); 194 free(part); 195 return (error); 196} 197 198static int 199fdcopy(int src, int dst, uint64_t *count) 200{ 201 void *buffer; 202 ssize_t rdsz, wrsz; 203 204 if (count != 0) 205 *count = 0; 206 207 buffer = malloc(BUFFER_SIZE); 208 if (buffer == NULL) 209 return (errno); 210 while (1) { 211 rdsz = read(src, buffer, BUFFER_SIZE); 212 if (rdsz <= 0) { 213 free(buffer); 214 return ((rdsz < 0) ? errno : 0); 215 } 216 if (count != NULL) 217 *count += rdsz; 218 wrsz = write(dst, buffer, rdsz); 219 if (wrsz < 0) 220 break; 221 } 222 free(buffer); 223 return (errno); 224} 225
|
| 226int 227mkimg_seek(int fd, lba_t blk) 228{ 229 off_t off; 230 231 off = blk * secsz; 232 if (lseek(fd, off, SEEK_SET) != off) 233 return (errno); 234 return (0); 235} 236
|
224static void 225mkimg(int bfd) 226{ 227 FILE *fp; 228 struct part *part;
| 237static void 238mkimg(int bfd) 239{ 240 FILE *fp; 241 struct part *part;
|
229 off_t offset; 230 uint64_t size;
| 242 lba_t block; 243 off_t bytesize;
|
231 int error, fd; 232 233 if (nparts > scheme_max_parts()) 234 errc(EX_DATAERR, ENOSPC, "only %d partitions are supported", 235 scheme_max_parts()); 236 237 error = scheme_bootcode(bfd); 238 if (error) 239 errc(EX_DATAERR, error, "boot code"); 240 241 /* First check partition information */ 242 STAILQ_FOREACH(part, &partlist, link) { 243 error = scheme_check_part(part); 244 if (error) 245 errc(EX_DATAERR, error, "partition %d", part->index+1); 246 } 247
| 244 int error, fd; 245 246 if (nparts > scheme_max_parts()) 247 errc(EX_DATAERR, ENOSPC, "only %d partitions are supported", 248 scheme_max_parts()); 249 250 error = scheme_bootcode(bfd); 251 if (error) 252 errc(EX_DATAERR, error, "boot code"); 253 254 /* First check partition information */ 255 STAILQ_FOREACH(part, &partlist, link) { 256 error = scheme_check_part(part); 257 if (error) 258 errc(EX_DATAERR, error, "partition %d", part->index+1); 259 } 260
|
248 offset = scheme_first_offset(nparts);
| 261 block = scheme_first_block();
|
249 STAILQ_FOREACH(part, &partlist, link) {
| 262 STAILQ_FOREACH(part, &partlist, link) {
|
250 part->offset = offset; 251 lseek(tmpfd, offset, SEEK_SET); 252 /* XXX check error */ 253 254 error = 0;
| 263 part->block = block; 264 error = mkimg_seek(tmpfd, block);
|
255 switch (part->kind) { 256 case PART_KIND_SIZE:
| 265 switch (part->kind) { 266 case PART_KIND_SIZE:
|
257 if (expand_number(part->contents, &size) == -1)
| 267 if (expand_number(part->contents, &bytesize) == -1)
|
258 error = errno; 259 break; 260 case PART_KIND_FILE: 261 fd = open(part->contents, O_RDONLY, 0); 262 if (fd != -1) {
| 268 error = errno; 269 break; 270 case PART_KIND_FILE: 271 fd = open(part->contents, O_RDONLY, 0); 272 if (fd != -1) {
|
263 error = fdcopy(fd, tmpfd, &size);
| 273 error = fdcopy(fd, tmpfd, &bytesize);
|
264 close(fd); 265 } else 266 error = errno; 267 break; 268 case PART_KIND_PIPE: 269 fp = popen(part->contents, "r"); 270 if (fp != NULL) {
| 274 close(fd); 275 } else 276 error = errno; 277 break; 278 case PART_KIND_PIPE: 279 fp = popen(part->contents, "r"); 280 if (fp != NULL) {
|
271 error = fdcopy(fileno(fp), tmpfd, &size);
| 281 error = fdcopy(fileno(fp), tmpfd, &bytesize);
|
272 pclose(fp); 273 } else 274 error = errno; 275 break; 276 } 277 if (error) 278 errc(EX_IOERR, error, "partition %d", part->index+1);
| 282 pclose(fp); 283 } else 284 error = errno; 285 break; 286 } 287 if (error) 288 errc(EX_IOERR, error, "partition %d", part->index+1);
|
279 size = scheme_round(size); 280 part->size = size; 281 offset = scheme_next_offset(offset, size);
| 289 part->size = (bytesize + secsz - 1) / secsz; 290 block = scheme_next_block(part->block, part->size);
|
282 } 283
| 291 } 292
|
284 error = (scheme_write(tmpfd, offset));
| 293 error = (scheme_write(tmpfd, block));
|
285} 286 287int 288main(int argc, char *argv[]) 289{ 290 int c, error; 291 292 while ((c = getopt(argc, argv, "b:h:o:p:s:t:z")) != -1) { 293 switch (c) { 294 case 'b': /* BOOT CODE */ 295 if (bcfd != -1) 296 usage("multiple bootcode given"); 297 bcfd = open(optarg, O_RDONLY, 0); 298 if (bcfd == -1) 299 err(EX_UNAVAILABLE, "%s", optarg); 300 break; 301 case 'h': /* GEOMETRY: HEADS */ 302 break; 303 case 'o': /* OUTPUT FILE */ 304 if (outfd != 0) 305 usage("multiple output files given"); 306 outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 307 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); 308 if (outfd == -1) 309 err(EX_CANTCREAT, "%s", optarg); 310 break; 311 case 'p': /* PARTITION */ 312 error = parse_part(optarg); 313 if (error) 314 errc(EX_DATAERR, error, "partition"); 315 break; 316 case 's': /* SCHEME */ 317 if (scheme_selected() != NULL) 318 usage("multiple schemes given"); 319 error = scheme_select(optarg); 320 if (error) 321 errc(EX_DATAERR, error, "scheme"); 322 break; 323 case 't': /* GEOMETRY: TRACK SIZE */ 324 break; 325 case 'z': /* SPARSE OUTPUT */ 326 break; 327 default: 328 usage("unknown option"); 329 } 330 } 331 if (argc > optind) 332 usage("trailing arguments"); 333 if (scheme_selected() == NULL) 334 usage("no scheme"); 335 if (nparts == 0) 336 usage("no partitions"); 337 338 if (outfd == 0) { 339 if (atexit(cleanup) == -1) 340 err(EX_OSERR, "cannot register cleanup function"); 341 outfd = 1; 342 tmpfd = mkstemp(tmpfname); 343 if (tmpfd == -1) 344 err(EX_OSERR, "cannot create temporary file"); 345 } else 346 tmpfd = outfd; 347 348 mkimg(bcfd); 349 350 if (tmpfd != outfd) { 351 if (lseek(tmpfd, 0, SEEK_SET) == 0) 352 error = fdcopy(tmpfd, outfd, NULL); 353 else 354 error = errno; 355 /* XXX check error */ 356 } 357 358 return (0); 359}
| 294} 295 296int 297main(int argc, char *argv[]) 298{ 299 int c, error; 300 301 while ((c = getopt(argc, argv, "b:h:o:p:s:t:z")) != -1) { 302 switch (c) { 303 case 'b': /* BOOT CODE */ 304 if (bcfd != -1) 305 usage("multiple bootcode given"); 306 bcfd = open(optarg, O_RDONLY, 0); 307 if (bcfd == -1) 308 err(EX_UNAVAILABLE, "%s", optarg); 309 break; 310 case 'h': /* GEOMETRY: HEADS */ 311 break; 312 case 'o': /* OUTPUT FILE */ 313 if (outfd != 0) 314 usage("multiple output files given"); 315 outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 316 S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); 317 if (outfd == -1) 318 err(EX_CANTCREAT, "%s", optarg); 319 break; 320 case 'p': /* PARTITION */ 321 error = parse_part(optarg); 322 if (error) 323 errc(EX_DATAERR, error, "partition"); 324 break; 325 case 's': /* SCHEME */ 326 if (scheme_selected() != NULL) 327 usage("multiple schemes given"); 328 error = scheme_select(optarg); 329 if (error) 330 errc(EX_DATAERR, error, "scheme"); 331 break; 332 case 't': /* GEOMETRY: TRACK SIZE */ 333 break; 334 case 'z': /* SPARSE OUTPUT */ 335 break; 336 default: 337 usage("unknown option"); 338 } 339 } 340 if (argc > optind) 341 usage("trailing arguments"); 342 if (scheme_selected() == NULL) 343 usage("no scheme"); 344 if (nparts == 0) 345 usage("no partitions"); 346 347 if (outfd == 0) { 348 if (atexit(cleanup) == -1) 349 err(EX_OSERR, "cannot register cleanup function"); 350 outfd = 1; 351 tmpfd = mkstemp(tmpfname); 352 if (tmpfd == -1) 353 err(EX_OSERR, "cannot create temporary file"); 354 } else 355 tmpfd = outfd; 356 357 mkimg(bcfd); 358 359 if (tmpfd != outfd) { 360 if (lseek(tmpfd, 0, SEEK_SET) == 0) 361 error = fdcopy(tmpfd, outfd, NULL); 362 else 363 error = errno; 364 /* XXX check error */ 365 } 366 367 return (0); 368}
|