Deleted Added
full compact
mkimg.c (263537) mkimg.c (263653)
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}