120dbae1marcel/*-
27f983a3marcel * Copyright (c) 2013,2014 Juniper Networks, Inc.
320dbae1marcel * All rights reserved.
420dbae1marcel *
520dbae1marcel * Redistribution and use in source and binary forms, with or without
620dbae1marcel * modification, are permitted provided that the following conditions
720dbae1marcel * are met:
820dbae1marcel * 1. Redistributions of source code must retain the above copyright
920dbae1marcel *    notice, this list of conditions and the following disclaimer.
1020dbae1marcel * 2. Redistributions in binary form must reproduce the above copyright
1120dbae1marcel *    notice, this list of conditions and the following disclaimer in the
1220dbae1marcel *    documentation and/or other materials provided with the distribution.
1320dbae1marcel *
1420dbae1marcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1520dbae1marcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1620dbae1marcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1720dbae1marcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1820dbae1marcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1920dbae1marcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2020dbae1marcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2120dbae1marcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2220dbae1marcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2320dbae1marcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2420dbae1marcel * SUCH DAMAGE.
2520dbae1marcel */
2620dbae1marcel
2720dbae1marcel#include <sys/cdefs.h>
2820dbae1marcel__FBSDID("$FreeBSD$");
2920dbae1marcel
30bf54eeamanu#include <sys/param.h>
3120dbae1marcel#include <sys/stat.h>
3220dbae1marcel#include <errno.h>
3320dbae1marcel#include <err.h>
3420dbae1marcel#include <fcntl.h>
35bf39d18marcel#include <getopt.h>
3620dbae1marcel#include <libutil.h>
370a26345marcel#include <limits.h>
3819e7651benno#include <stdbool.h>
395e84416marcel#include <stdint.h>
4020dbae1marcel#include <stdio.h>
4120dbae1marcel#include <stdlib.h>
4220dbae1marcel#include <string.h>
4320dbae1marcel#include <sysexits.h>
4420dbae1marcel#include <unistd.h>
4520dbae1marcel
4624cdcc8marcel#include "image.h"
47fb17722marcel#include "format.h"
48cf8b12emarcel#include "mkimg.h"
4920dbae1marcel#include "scheme.h"
5020dbae1marcel
51bf54eeamanu#define	LONGOPT_FORMATS		0x01000001
52bf54eeamanu#define	LONGOPT_SCHEMES		0x01000002
53bf54eeamanu#define	LONGOPT_VERSION		0x01000003
54bf54eeamanu#define	LONGOPT_CAPACITY	0x01000004
55bf39d18marcel
56bf39d18marcelstatic struct option longopts[] = {
57bf39d18marcel	{ "formats", no_argument, NULL, LONGOPT_FORMATS },
58bf39d18marcel	{ "schemes", no_argument, NULL, LONGOPT_SCHEMES },
59bf39d18marcel	{ "version", no_argument, NULL, LONGOPT_VERSION },
60bf54eeamanu	{ "capacity", required_argument, NULL, LONGOPT_CAPACITY },
61bf39d18marcel	{ NULL, 0, NULL, 0 }
62bf39d18marcel};
63bf39d18marcel
64bf54eeamanustatic uint64_t min_capacity = 0;
65bf54eeamanustatic uint64_t max_capacity = 0;
661603ce9marcel
67f258aa5marcelstruct partlisthead partlist = TAILQ_HEAD_INITIALIZER(partlist);
68cf8b12emarcelu_int nparts = 0;
6920dbae1marcel
70787496fmarcelu_int unit_testing;
716084642marcelu_int verbose;
726084642marcel
730a26345marcelu_int ncyls = 0;
740a26345marcelu_int nheads = 1;
750a26345marcelu_int nsecs = 1;
763cd8745marcelu_int secsz = 512;
776084642marcelu_int blksz = 0;
785f1361aimpuint32_t active_partition = 0;
793cd8745marcel
8020dbae1marcelstatic void
81bf39d18marcelprint_formats(int usage)
8220dbae1marcel{
83a70a057marcel	struct mkimg_format *f;
84bf39d18marcel	const char *sep;
85bf39d18marcel
86bf39d18marcel	if (usage) {
872bf89afmarcel		fprintf(stderr, "    formats:\n");
88a70a057marcel		f = NULL;
89a70a057marcel		while ((f = format_iterate(f)) != NULL) {
90bf39d18marcel			fprintf(stderr, "\t%s\t-  %s\n", f->name,
91bf39d18marcel			    f->description);
92bf39d18marcel		}
93bf39d18marcel	} else {
94bf39d18marcel		sep = "";
95a70a057marcel		f = NULL;
96a70a057marcel		while ((f = format_iterate(f)) != NULL) {
97bf39d18marcel			printf("%s%s", sep, f->name);
98bf39d18marcel			sep = " ";
99bf39d18marcel		}
100bf39d18marcel		putchar('\n');
101bf39d18marcel	}
102bf39d18marcel}
103bf39d18marcel
104bf39d18marcelstatic void
105bf39d18marcelprint_schemes(int usage)
106bf39d18marcel{
107a70a057marcel	struct mkimg_scheme *s;
108bf39d18marcel	const char *sep;
109bf39d18marcel
110bf39d18marcel	if (usage) {
1112bf89afmarcel		fprintf(stderr, "    schemes:\n");
112a70a057marcel		s = NULL;
113a70a057marcel		while ((s = scheme_iterate(s)) != NULL) {
114bf39d18marcel			fprintf(stderr, "\t%s\t-  %s\n", s->name,
115bf39d18marcel			    s->description);
116bf39d18marcel		}
117bf39d18marcel	} else {
118bf39d18marcel		sep = "";
119a70a057marcel		s = NULL;
120a70a057marcel		while ((s = scheme_iterate(s)) != NULL) {
121bf39d18marcel			printf("%s%s", sep, s->name);
122bf39d18marcel			sep = " ";
123bf39d18marcel		}
124bf39d18marcel		putchar('\n');
125bf39d18marcel	}
126bf39d18marcel}
127bf39d18marcel
128bf39d18marcelstatic void
129bf39d18marcelprint_version(void)
130bf39d18marcel{
131bf39d18marcel	u_int width;
132bf39d18marcel
133bf39d18marcel#ifdef __LP64__
134bf39d18marcel	width = 64;
135bf39d18marcel#else
136bf39d18marcel	width = 32;
137bf39d18marcel#endif
138bf39d18marcel	printf("mkimg %u (%u-bit)\n", MKIMG_VERSION, width);
139bf39d18marcel}
140bf39d18marcel
141bf39d18marcelstatic void
142bf39d18marcelusage(const char *why)
143bf39d18marcel{
1447f983a3marcel
14520dbae1marcel	warnx("error: %s", why);
1462bf89afmarcel	fputc('\n', stderr);
1472bf89afmarcel	fprintf(stderr, "usage: %s <options>\n", getprogname());
14848a229fmarcel
14920dbae1marcel	fprintf(stderr, "    options:\n");
1502bf89afmarcel	fprintf(stderr, "\t--formats\t-  list image formats\n");
1512bf89afmarcel	fprintf(stderr, "\t--schemes\t-  list partition schemes\n");
1522bf89afmarcel	fprintf(stderr, "\t--version\t-  show version information\n");
1532bf89afmarcel	fputc('\n', stderr);
1545f1361aimp	fprintf(stderr, "\t-a <num>\t-  mark num'th partion as active\n");
15548a229fmarcel	fprintf(stderr, "\t-b <file>\t-  file containing boot code\n");
156bf54eeamanu	fprintf(stderr, "\t-c <num>\t-  minimum capacity (in bytes) of the disk\n");
157bf54eeamanu	fprintf(stderr, "\t-C <num>\t-  maximum capacity (in bytes) of the disk\n");
1583ca7c0dmarcel	fprintf(stderr, "\t-f <format>\n");
15948a229fmarcel	fprintf(stderr, "\t-o <file>\t-  file to write image into\n");
16020dbae1marcel	fprintf(stderr, "\t-p <partition>\n");
16120dbae1marcel	fprintf(stderr, "\t-s <scheme>\n");
16272dff6bmarcel	fprintf(stderr, "\t-v\t\t-  increase verbosity\n");
16372dff6bmarcel	fprintf(stderr, "\t-y\t\t-  [developers] enable unit test\n");
1640a26345marcel	fprintf(stderr, "\t-H <num>\t-  number of heads to simulate\n");
1650a26345marcel	fprintf(stderr, "\t-P <num>\t-  physical sector size\n");
1660a26345marcel	fprintf(stderr, "\t-S <num>\t-  logical sector size\n");
1670a26345marcel	fprintf(stderr, "\t-T <num>\t-  number of tracks to simulate\n");
1682bf89afmarcel	fputc('\n', stderr);
169bf39d18marcel	print_formats(1);
1702bf89afmarcel	fputc('\n', stderr);
171bf39d18marcel	print_schemes(1);
1722bf89afmarcel	fputc('\n', stderr);
1732bf89afmarcel	fprintf(stderr, "    partition specification:\n");
17419e7651benno	fprintf(stderr, "\t<t>[/<l>]::<size>[:[+]<offset>]\t-  "
17519e7651benno	    "empty partition of given size and\n\t\t\t\t\t"
17619e7651benno	    "   optional relative or absolute offset\n");
17719e7651benno	fprintf(stderr, "\t<t>[/<l>]:=<file>\t\t-  partition content and size "
17819e7651benno	    "are\n\t\t\t\t\t   determined by the named file\n");
17919e7651benno	fprintf(stderr, "\t<t>[/<l>]:-<cmd>\t\t-  partition content and size "
18019e7651benno	    "are taken\n\t\t\t\t\t   from the output of the command to run\n");
18119e7651benno	fprintf(stderr, "\t-\t\t\t\t-  unused partition entry\n");
18248a229fmarcel	fprintf(stderr, "\t    where:\n");
1838ff772amarcel	fprintf(stderr, "\t\t<t>\t-  scheme neutral partition type\n");
1848ff772amarcel	fprintf(stderr, "\t\t<l>\t-  optional scheme-dependent partition "
1858ff772amarcel	    "label\n");
18648a229fmarcel
18720dbae1marcel	exit(EX_USAGE);
18820dbae1marcel}
18920dbae1marcel
1900a26345marcelstatic int
1911603ce9marcelparse_uint32(uint32_t *valp, uint32_t min, uint32_t max, const char *arg)
1920a26345marcel{
1930a26345marcel	uint64_t val;
1940a26345marcel
1950a26345marcel	if (expand_number(arg, &val) == -1)
1960a26345marcel		return (errno);
1970a26345marcel	if (val > UINT_MAX || val < (uint64_t)min || val > (uint64_t)max)
1980a26345marcel		return (EINVAL);
1991603ce9marcel	*valp = (uint32_t)val;
2001603ce9marcel	return (0);
2011603ce9marcel}
2021603ce9marcel
2031603ce9marcelstatic int
2041603ce9marcelparse_uint64(uint64_t *valp, uint64_t min, uint64_t max, const char *arg)
2051603ce9marcel{
2061603ce9marcel	uint64_t val;
2071603ce9marcel
2081603ce9marcel	if (expand_number(arg, &val) == -1)
2091603ce9marcel		return (errno);
2101603ce9marcel	if (val < min || val > max)
2111603ce9marcel		return (EINVAL);
2121603ce9marcel	*valp = val;
2130a26345marcel	return (0);
2140a26345marcel}
2150a26345marcel
2160a26345marcelstatic int
2170a26345marcelpwr_of_two(u_int nr)
2180a26345marcel{
2190a26345marcel
2200a26345marcel	return (((nr & (nr - 1)) == 0) ? 1 : 0);
2210a26345marcel}
2220a26345marcel
22320dbae1marcel/*
22420dbae1marcel * A partition specification has the following format:
22520dbae1marcel *	<type> ':' <kind> <contents>
22620dbae1marcel * where:
22720dbae1marcel *	type	  the partition type alias
22820dbae1marcel *	kind	  the interpretation of the contents specification
22920dbae1marcel *		  ':'   contents holds the size of an empty partition
23020dbae1marcel *		  '='   contents holds the name of a file to read
23176165e7marcel *		  '-'   contents holds a command to run; the output of
23220dbae1marcel *			which is the contents of the partition.
23320dbae1marcel *	contents  the specification of a partition's contents
23491c4578marcel *
23591c4578marcel * A specification that is a single dash indicates an unused partition
23691c4578marcel * entry.
23720dbae1marcel */
23820dbae1marcelstatic int
23920dbae1marcelparse_part(const char *spec)
24020dbae1marcel{
24120dbae1marcel	struct part *part;
24220dbae1marcel	char *sep;
24320dbae1marcel	size_t len;
24420dbae1marcel	int error;
24520dbae1marcel
24691c4578marcel	if (strcmp(spec, "-") == 0) {
24791c4578marcel		nparts++;
24891c4578marcel		return (0);
24991c4578marcel	}
25091c4578marcel
25120dbae1marcel	part = calloc(1, sizeof(struct part));
25220dbae1marcel	if (part == NULL)
25320dbae1marcel		return (ENOMEM);
25420dbae1marcel
25520dbae1marcel	sep = strchr(spec, ':');
25620dbae1marcel	if (sep == NULL) {
25720dbae1marcel		error = EINVAL;
25820dbae1marcel		goto errout;
25920dbae1marcel	}
26020dbae1marcel	len = sep - spec + 1;
26120dbae1marcel	if (len < 2) {
26220dbae1marcel		error = EINVAL;
26320dbae1marcel		goto errout;
26420dbae1marcel	}
2655f39eb9marcel	part->alias = malloc(len);
2665f39eb9marcel	if (part->alias == NULL) {
26720dbae1marcel		error = ENOMEM;
26820dbae1marcel		goto errout;
26920dbae1marcel	}
2705f39eb9marcel	strlcpy(part->alias, spec, len);
27120dbae1marcel	spec = sep + 1;
27220dbae1marcel
27320dbae1marcel	switch (*spec) {
27420dbae1marcel	case ':':
27520dbae1marcel		part->kind = PART_KIND_SIZE;
27620dbae1marcel		break;
27720dbae1marcel	case '=':
27820dbae1marcel		part->kind = PART_KIND_FILE;
27920dbae1marcel		break;
28076165e7marcel	case '-':
28120dbae1marcel		part->kind = PART_KIND_PIPE;
28220dbae1marcel		break;
28320dbae1marcel	default:
28420dbae1marcel		error = EINVAL;
28520dbae1marcel		goto errout;
28620dbae1marcel	}
28720dbae1marcel	spec++;
28820dbae1marcel
28920dbae1marcel	part->contents = strdup(spec);
29020dbae1marcel	if (part->contents == NULL) {
29120dbae1marcel		error = ENOMEM;
29220dbae1marcel		goto errout;
29320dbae1marcel	}
29420dbae1marcel
295ee34aedmarcel	spec = part->alias;
296ee34aedmarcel	sep = strchr(spec, '/');
297ee34aedmarcel	if (sep != NULL) {
298ee34aedmarcel		*sep++ = '\0';
299ee34aedmarcel		if (strlen(part->alias) == 0 || strlen(sep) == 0) {
300ee34aedmarcel			error = EINVAL;
301ee34aedmarcel			goto errout;
302ee34aedmarcel		}
303ee34aedmarcel		part->label = strdup(sep);
304ee34aedmarcel		if (part->label == NULL) {
305ee34aedmarcel			error = ENOMEM;
306ee34aedmarcel			goto errout;
307ee34aedmarcel		}
308ee34aedmarcel	}
309ee34aedmarcel
31020dbae1marcel	part->index = nparts;
311f258aa5marcel	TAILQ_INSERT_TAIL(&partlist, part, link);
31220dbae1marcel	nparts++;
31320dbae1marcel	return (0);
31420dbae1marcel
31520dbae1marcel errout:
3165f39eb9marcel	if (part->alias != NULL)
3175f39eb9marcel		free(part->alias);
31820dbae1marcel	free(part);
31920dbae1marcel	return (error);
32020dbae1marcel}
32120dbae1marcel
3224cf1c78marcel#if defined(SPARSE_WRITE)
3238e16ce1marcelssize_t
324bea02c4marcelsparse_write(int fd, const void *ptr, size_t sz)
3254cf1c78marcel{
326bea02c4marcel	const char *buf, *p;
3274cf1c78marcel	off_t ofs;
3284cf1c78marcel	size_t len;
3294cf1c78marcel	ssize_t wr, wrsz;
3304cf1c78marcel
331bea02c4marcel	buf = ptr;
3324cf1c78marcel	wrsz = 0;
3334cf1c78marcel	p = memchr(buf, 0, sz);
3344cf1c78marcel	while (sz > 0) {
335f017f60marcel		len = (p != NULL) ? (size_t)(p - buf) : sz;
3364cf1c78marcel		if (len > 0) {
3374cf1c78marcel			len = (len + secsz - 1) & ~(secsz - 1);
3384cf1c78marcel			if (len > sz)
3394cf1c78marcel				len = sz;
3404cf1c78marcel			wr = write(fd, buf, len);
3414cf1c78marcel			if (wr < 0)
3424cf1c78marcel				return (-1);
3434cf1c78marcel		} else {
3444cf1c78marcel			while (len < sz && *p++ == '\0')
3454cf1c78marcel				len++;
3464cf1c78marcel			if (len < sz)
3474cf1c78marcel				len &= ~(secsz - 1);
3484cf1c78marcel			if (len == 0)
3494cf1c78marcel				continue;
3504cf1c78marcel			ofs = lseek(fd, len, SEEK_CUR);
3514cf1c78marcel			if (ofs < 0)
3524cf1c78marcel				return (-1);
3534cf1c78marcel			wr = len;
3544cf1c78marcel		}
3554cf1c78marcel		buf += wr;
3564cf1c78marcel		sz -= wr;
3574cf1c78marcel		wrsz += wr;
3584cf1c78marcel		p = memchr(buf, 0, sz);
3594cf1c78marcel	}
3604cf1c78marcel	return (wrsz);
3614cf1c78marcel}
3624cf1c78marcel#endif /* SPARSE_WRITE */
3634cf1c78marcel
364787496fmarcelvoid
365ad936aamarcelmkimg_chs(lba_t lba, u_int maxcyl, u_int *cylp, u_int *hdp, u_int *secp)
366ad936aamarcel{
367ad936aamarcel	u_int hd, sec;
368ad936aamarcel
369ad936aamarcel	*cylp = *hdp = *secp = ~0U;
370ad936aamarcel	if (nsecs == 1 || nheads == 1)
371ad936aamarcel		return;
372ad936aamarcel
373ad936aamarcel	sec = lba % nsecs + 1;
374ad936aamarcel	lba /= nsecs;
375ad936aamarcel	hd = lba % nheads;
376ad936aamarcel	lba /= nheads;
377ad936aamarcel	if (lba > maxcyl)
378ad936aamarcel		return;
379ad936aamarcel
380ad936aamarcel	*cylp = lba;
381ad936aamarcel	*hdp = hd;
382ad936aamarcel	*secp = sec;
383ad936aamarcel}
384ad936aamarcel
3851603ce9marcelstatic int
3861603ce9marcelcapacity_resize(lba_t end)
3871603ce9marcel{
388bf54eeamanu	lba_t min_capsz, max_capsz;
3891603ce9marcel
390bf54eeamanu	min_capsz = (min_capacity + secsz - 1) / secsz;
391bf54eeamanu	max_capsz = (max_capacity + secsz - 1) / secsz;
392bf54eeamanu
393bf54eeamanu	if (max_capsz != 0 && end > max_capsz)
394bf54eeamanu		return (ENOSPC);
395bf54eeamanu	if (end >= min_capsz)
3961603ce9marcel		return (0);
397bf54eeamanu
398bf54eeamanu	return (image_set_size(min_capsz));
3991603ce9marcel}
4001603ce9marcel
40120dbae1marcelstatic void
40219e7651bennomkimg_validate(void)
40319e7651benno{
40419e7651benno	struct part *part, *part2;
40519e7651benno	lba_t start, end, start2, end2;
40619e7651benno	int i, j;
40719e7651benno
40819e7651benno	i = 0;
40919e7651benno
41019e7651benno	TAILQ_FOREACH(part, &partlist, link) {
41119e7651benno		start = part->block;
41219e7651benno		end = part->block + part->size;
41319e7651benno		j = i + 1;
41419e7651benno		part2 = TAILQ_NEXT(part, link);
41519e7651benno		if (part2 == NULL)
41619e7651benno			break;
41719e7651benno
41819e7651benno		TAILQ_FOREACH_FROM(part2, &partlist, link) {
41919e7651benno			start2 = part2->block;
42019e7651benno			end2 = part2->block + part2->size;
42119e7651benno
42219e7651benno			if ((start >= start2 && start < end2) ||
42319e7651benno			    (end > start2 && end <= end2)) {
42419e7651benno				errx(1, "partition %d overlaps partition %d",
42519e7651benno				    i, j);
42619e7651benno			}
42719e7651benno
42819e7651benno			j++;
42919e7651benno		}
43019e7651benno
43119e7651benno		i++;
43219e7651benno	}
43319e7651benno}
43419e7651benno
43519e7651bennostatic void
4368e16ce1marcelmkimg(void)
43720dbae1marcel{
43820dbae1marcel	FILE *fp;
43920dbae1marcel	struct part *part;
44019e7651benno	lba_t block, blkoffset;
44119e7651benno	off_t bytesize, byteoffset;
44219e7651benno	char *size, *offset;
44319e7651benno	bool abs_offset;
44420dbae1marcel	int error, fd;
44520dbae1marcel
4460dddaf6marcel	/* First check partition information */
447f258aa5marcel	TAILQ_FOREACH(part, &partlist, link) {
4480dddaf6marcel		error = scheme_check_part(part);
4490dddaf6marcel		if (error)
4500dddaf6marcel			errc(EX_DATAERR, error, "partition %d", part->index+1);
4510dddaf6marcel	}
4520dddaf6marcel
453bfd31b9marcel	block = scheme_metadata(SCHEME_META_IMG_START, 0);
45419e7651benno	abs_offset = false;
455f258aa5marcel	TAILQ_FOREACH(part, &partlist, link) {
45619e7651benno		byteoffset = blkoffset = 0;
45719e7651benno		abs_offset = false;
45819e7651benno
45919e7651benno		/* Look for an offset. Set size too if we can. */
46020dbae1marcel		switch (part->kind) {
46120dbae1marcel		case PART_KIND_SIZE:
46219e7651benno			offset = part->contents;
46319e7651benno			size = strsep(&offset, ":");
46419e7651benno			if (expand_number(size, &bytesize) == -1)
46520dbae1marcel				error = errno;
46619e7651benno			if (offset != NULL) {
46710246e8benno				if (*offset != '+')
46819e7651benno					abs_offset = true;
46910246e8benno				else
47019e7651benno					offset++;
47119e7651benno				if (expand_number(offset, &byteoffset) == -1)
47219e7651benno					error = errno;
47319e7651benno			}
47420dbae1marcel			break;
47519e7651benno		}
47619e7651benno
47719e7651benno		/* Work out exactly where the partition starts. */
47819e7651benno		blkoffset = (byteoffset + secsz - 1) / secsz;
47919e7651benno		if (abs_offset) {
48019e7651benno			part->block = scheme_metadata(SCHEME_META_PART_ABSOLUTE,
48119e7651benno			    blkoffset);
48219e7651benno		} else {
48319e7651benno			block = scheme_metadata(SCHEME_META_PART_BEFORE,
48419e7651benno			    block + blkoffset);
48519e7651benno			part->block = block;
48619e7651benno		}
48719e7651benno
48819e7651benno		if (verbose)
48919e7651benno			fprintf(stderr, "partition %d: starting block %llu "
49019e7651benno			    "... ", part->index + 1, (long long)part->block);
49119e7651benno
49219e7651benno		/* Pull in partition contents, set size if we haven't yet. */
49319e7651benno		switch (part->kind) {
49420dbae1marcel		case PART_KIND_FILE:
49520dbae1marcel			fd = open(part->contents, O_RDONLY, 0);
49620dbae1marcel			if (fd != -1) {
497ec5097cmarcel				error = image_copyin(block, fd, &bytesize);
49820dbae1marcel				close(fd);
49920dbae1marcel			} else
50020dbae1marcel				error = errno;
50120dbae1marcel			break;
50220dbae1marcel		case PART_KIND_PIPE:
50320dbae1marcel			fp = popen(part->contents, "r");
50420dbae1marcel			if (fp != NULL) {
5058743dd6marcel				fd = fileno(fp);
506ec5097cmarcel				error = image_copyin(block, fd, &bytesize);
50720dbae1marcel				pclose(fp);
50820dbae1marcel			} else
50920dbae1marcel				error = errno;
51020dbae1marcel			break;
51120dbae1marcel		}
5125f39eb9marcel		if (error)
513bfd31b9marcel			errc(EX_IOERR, error, "partition %d", part->index + 1);
5143cd8745marcel		part->size = (bytesize + secsz - 1) / secsz;
515bfd31b9marcel		if (verbose) {
516bfd31b9marcel			bytesize = part->size * secsz;
517533cda2marcel			fprintf(stderr, "size %llu bytes (%llu blocks)\n",
518bfd31b9marcel			     (long long)bytesize, (long long)part->size);
51919e7651benno			if (abs_offset) {
52019e7651benno				fprintf(stderr,
52119e7651benno				    "    location %llu bytes (%llu blocks)\n",
52219e7651benno				    (long long)byteoffset,
52319e7651benno				    (long long)blkoffset);
52419e7651benno			} else if (blkoffset > 0) {
52519e7651benno				fprintf(stderr,
52619e7651benno				    "    offset %llu bytes (%llu blocks)\n",
52719e7651benno				    (long long)byteoffset,
52819e7651benno				    (long long)blkoffset);
52919e7651benno			}
53019e7651benno		}
53119e7651benno		if (!abs_offset) {
53219e7651benno			block = scheme_metadata(SCHEME_META_PART_AFTER,
53319e7651benno			    part->block + part->size);
534bfd31b9marcel		}
53520dbae1marcel	}
53620dbae1marcel
53719e7651benno	mkimg_validate();
53819e7651benno
539bfd31b9marcel	block = scheme_metadata(SCHEME_META_IMG_END, block);
540fb17722marcel	error = image_set_size(block);
54189337d7marcel	if (!error) {
5421603ce9marcel		error = capacity_resize(block);
54389337d7marcel		block = image_get_size();
54489337d7marcel	}
54589337d7marcel	if (!error) {
546fb17722marcel		error = format_resize(block);
54789337d7marcel		block = image_get_size();
54889337d7marcel	}
549fb17722marcel	if (error)
550fb17722marcel		errc(EX_IOERR, error, "image sizing");
551fb17722marcel	ncyls = block / (nsecs * nheads);
55206c3519marcel	error = scheme_write(block);
553fb17722marcel	if (error)
554fb17722marcel		errc(EX_IOERR, error, "writing metadata");
55520dbae1marcel}
55620dbae1marcel
55720dbae1marcelint
55820dbae1marcelmain(int argc, char *argv[])
55920dbae1marcel{
5608e16ce1marcel	int bcfd, outfd;
56120dbae1marcel	int c, error;
56220dbae1marcel
5638e16ce1marcel	bcfd = -1;
5648e16ce1marcel	outfd = 1;	/* Write to stdout by default */
565bf54eeamanu	while ((c = getopt_long(argc, argv, "a:b:c:C:f:o:p:s:vyH:P:S:T:",
566bf39d18marcel	    longopts, NULL)) != -1) {
56720dbae1marcel		switch (c) {
5685f1361aimp		case 'a':	/* ACTIVE PARTITION, if supported */
5695f1361aimp			error = parse_uint32(&active_partition, 1, 100, optarg);
5705f1361aimp			if (error)
5715f1361aimp				errc(EX_DATAERR, error, "Partition ordinal");
5725f1361aimp			break;
57320dbae1marcel		case 'b':	/* BOOT CODE */
574d449c3fmarcel			if (bcfd != -1)
57520dbae1marcel				usage("multiple bootcode given");
57620dbae1marcel			bcfd = open(optarg, O_RDONLY, 0);
57720dbae1marcel			if (bcfd == -1)
57820dbae1marcel				err(EX_UNAVAILABLE, "%s", optarg);
57920dbae1marcel			break;
580bf54eeamanu		case 'c':	/* MINIMUM CAPACITY */
581bf54eeamanu			error = parse_uint64(&min_capacity, 1, INT64_MAX, optarg);
5821603ce9marcel			if (error)
583bf54eeamanu				errc(EX_DATAERR, error, "minimum capacity in bytes");
584bf54eeamanu			break;
585bf54eeamanu		case 'C':	/* MAXIMUM CAPACITY */
586bf54eeamanu			error = parse_uint64(&max_capacity, 1, INT64_MAX, optarg);
587bf54eeamanu			if (error)
588bf54eeamanu				errc(EX_DATAERR, error, "maximum capacity in bytes");
5891603ce9marcel			break;
5903ca7c0dmarcel		case 'f':	/* OUTPUT FORMAT */
5913ca7c0dmarcel			if (format_selected() != NULL)
5923ca7c0dmarcel				usage("multiple formats given");
5933ca7c0dmarcel			error = format_select(optarg);
5943ca7c0dmarcel			if (error)
5953ca7c0dmarcel				errc(EX_DATAERR, error, "format");
5963ca7c0dmarcel			break;
59720dbae1marcel		case 'o':	/* OUTPUT FILE */
5988e16ce1marcel			if (outfd != 1)
59920dbae1marcel				usage("multiple output files given");
60020dbae1marcel			outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC,
60120dbae1marcel			    S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
60220dbae1marcel			if (outfd == -1)
60320dbae1marcel				err(EX_CANTCREAT, "%s", optarg);
60420dbae1marcel			break;
60520dbae1marcel		case 'p':	/* PARTITION */
60620dbae1marcel			error = parse_part(optarg);
60720dbae1marcel			if (error)
60820dbae1marcel				errc(EX_DATAERR, error, "partition");
60920dbae1marcel			break;
61020dbae1marcel		case 's':	/* SCHEME */
6117f983a3marcel			if (scheme_selected() != NULL)
61220dbae1marcel				usage("multiple schemes given");
61320dbae1marcel			error = scheme_select(optarg);
61420dbae1marcel			if (error)
61520dbae1marcel				errc(EX_DATAERR, error, "scheme");
61620dbae1marcel			break;
617787496fmarcel		case 'y':
618787496fmarcel			unit_testing++;
619787496fmarcel			break;
6206084642marcel		case 'v':
6216084642marcel			verbose++;
6226084642marcel			break;
6230a26345marcel		case 'H':	/* GEOMETRY: HEADS */
6241603ce9marcel			error = parse_uint32(&nheads, 1, 255, optarg);
6250a26345marcel			if (error)
6260a26345marcel				errc(EX_DATAERR, error, "number of heads");
6270a26345marcel			break;
6280a26345marcel		case 'P':	/* GEOMETRY: PHYSICAL SECTOR SIZE */
6291603ce9marcel			error = parse_uint32(&blksz, 512, INT_MAX+1U, optarg);
6300a26345marcel			if (error == 0 && !pwr_of_two(blksz))
6310a26345marcel				error = EINVAL;
6320a26345marcel			if (error)
6330a26345marcel				errc(EX_DATAERR, error, "physical sector size");
6340a26345marcel			break;
6350a26345marcel		case 'S':	/* GEOMETRY: LOGICAL SECTOR SIZE */
6361603ce9marcel			error = parse_uint32(&secsz, 512, INT_MAX+1U, optarg);
6376084642marcel			if (error == 0 && !pwr_of_two(secsz))
6380a26345marcel				error = EINVAL;
6390a26345marcel			if (error)
6400a26345marcel				errc(EX_DATAERR, error, "logical sector size");
6410a26345marcel			break;
6420a26345marcel		case 'T':	/* GEOMETRY: TRACK SIZE */
6431603ce9marcel			error = parse_uint32(&nsecs, 1, 63, optarg);
6440a26345marcel			if (error)
6450a26345marcel				errc(EX_DATAERR, error, "track size");
6460a26345marcel			break;
647bf39d18marcel		case LONGOPT_FORMATS:
648bf39d18marcel			print_formats(0);
649bf39d18marcel			exit(EX_OK);
650bf39d18marcel			/*NOTREACHED*/
651bf39d18marcel		case LONGOPT_SCHEMES:
652bf39d18marcel			print_schemes(0);
653bf39d18marcel			exit(EX_OK);
654bf39d18marcel			/*NOTREACHED*/
655bf39d18marcel		case LONGOPT_VERSION:
656bf39d18marcel			print_version();
657bf39d18marcel			exit(EX_OK);
658bf39d18marcel			/*NOTREACHED*/
659bf54eeamanu		case LONGOPT_CAPACITY:
660bf54eeamanu			error = parse_uint64(&min_capacity, 1, INT64_MAX, optarg);
661bf54eeamanu			if (error)
662bf54eeamanu				errc(EX_DATAERR, error, "capacity in bytes");
663bf54eeamanu			max_capacity = min_capacity;
664bf54eeamanu			break;
66520dbae1marcel		default:
66620dbae1marcel			usage("unknown option");
66720dbae1marcel		}
66820dbae1marcel	}
6696084642marcel
67020dbae1marcel	if (argc > optind)
67120dbae1marcel		usage("trailing arguments");
67288793a4marcel	if (scheme_selected() == NULL && nparts > 0)
67320dbae1marcel		usage("no scheme");
674bf54eeamanu	if (nparts == 0 && min_capacity == 0)
67520dbae1marcel		usage("no partitions");
676bf54eeamanu	if (max_capacity != 0 && min_capacity > max_capacity)
677bf54eeamanu		usage("minimum capacity cannot be larger than the maximum one");
67820dbae1marcel
6796084642marcel	if (secsz > blksz) {
6806084642marcel		if (blksz != 0)
6816084642marcel			errx(EX_DATAERR, "the physical block size cannot "
6826084642marcel			    "be smaller than the sector size");
6836084642marcel		blksz = secsz;
6846084642marcel	}
6856084642marcel
6866084642marcel	if (secsz > scheme_max_secsz())
6876084642marcel		errx(EX_DATAERR, "maximum sector size supported is %u; "
6886084642marcel		    "size specified is %u", scheme_max_secsz(), secsz);
6896084642marcel
6906084642marcel	if (nparts > scheme_max_parts())
6916084642marcel		errx(EX_DATAERR, "%d partitions supported; %d given",
6926084642marcel		    scheme_max_parts(), nparts);
6936084642marcel
6943ca7c0dmarcel	if (format_selected() == NULL)
6953ca7c0dmarcel		format_select("raw");
6963ca7c0dmarcel
6978e16ce1marcel	if (bcfd != -1) {
6988e16ce1marcel		error = scheme_bootcode(bcfd);
6998e16ce1marcel		close(bcfd);
7008e16ce1marcel		if (error)
7018e16ce1marcel			errc(EX_DATAERR, error, "boot code");
7028e16ce1marcel	}
70320dbae1marcel
7046084642marcel	if (verbose) {
705533cda2marcel		fprintf(stderr, "Logical sector size: %u\n", secsz);
706533cda2marcel		fprintf(stderr, "Physical block size: %u\n", blksz);
707533cda2marcel		fprintf(stderr, "Sectors per track:   %u\n", nsecs);
708533cda2marcel		fprintf(stderr, "Number of heads:     %u\n", nheads);
7093ca7c0dmarcel		fputc('\n', stderr);
71088793a4marcel		if (scheme_selected())
71188793a4marcel			fprintf(stderr, "Partitioning scheme: %s\n",
71288793a4marcel			    scheme_selected()->name);
7133ca7c0dmarcel		fprintf(stderr, "Output file format:  %s\n",
7143ca7c0dmarcel		    format_selected()->name);
7153ca7c0dmarcel		fputc('\n', stderr);
7166084642marcel	}
7176084642marcel
7188e16ce1marcel	error = image_init();
7198e16ce1marcel	if (error)
7208e16ce1marcel		errc(EX_OSERR, error, "cannot initialize");
7218e16ce1marcel
7228e16ce1marcel	mkimg();
72320dbae1marcel
7243ca7c0dmarcel	if (verbose) {
7253ca7c0dmarcel		fputc('\n', stderr);
726533cda2marcel		fprintf(stderr, "Number of cylinders: %u\n", ncyls);
7273ca7c0dmarcel	}
728bfd31b9marcel
729bd79c28marcel	error = format_write(outfd);
7308e16ce1marcel	if (error)
7318e16ce1marcel		errc(EX_IOERR, error, "writing image");
73220dbae1marcel
73320dbae1marcel	return (0);
73420dbae1marcel}
735