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