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