svn commit: r253923 - user/marcel/mkimg

Marcel Moolenaar marcel at FreeBSD.org
Sun Aug 4 02:37:07 UTC 2013


Author: marcel
Date: Sun Aug  4 02:37:05 2013
New Revision: 253923
URL: http://svnweb.freebsd.org/changeset/base/253923

Log:
  Safe WIP: mkimg is a user-space utility for creating disk images.
  In its current form and shape, it creates images with the correct
  partition contents and overall image layout. What it doesn't do
  yet is actually scribble the metadata, such as partition tables
  and boot code. This is where I'd like to leverage code from other
  places, such as geom_part, and if possible/feasible.
  Also, the utility should be a little bit smarter about files and
  pipes so that we create temporary files only when needed (e.g.
  when we don't know the size of a partition's contents in advance
  while wrting the image to stdout).

Added:
  user/marcel/mkimg/
  user/marcel/mkimg/Makefile
  user/marcel/mkimg/mkimg.8
  user/marcel/mkimg/mkimg.c
  user/marcel/mkimg/scheme.c
  user/marcel/mkimg/scheme.h

Added: user/marcel/mkimg/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/marcel/mkimg/Makefile	Sun Aug  4 02:37:05 2013	(r253923)
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG=	mkimg
+SRCS=	mkimg.c scheme.c
+MAN=	mkimg.8
+
+DPADD=	${LIBUTIL}
+LDADD=	-lutil
+
+.include <bsd.prog.mk>

Added: user/marcel/mkimg/mkimg.8
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/marcel/mkimg/mkimg.8	Sun Aug  4 02:37:05 2013	(r253923)
@@ -0,0 +1,51 @@
+.\" Copyright (c) 2013 Juniper Networks, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 26, 2013
+.Dt MKIMG 1
+.Os
+.Sh NAME
+.Nm mkimg
+.Nd "utility to make a disk image"
+.Sh SYNOPSIS
+.Nm
+.Op Fl b Ar bootcode
+.Op Fl o Ar outfile
+.Op Fl p Ar partition
+.Op Fl s Ar scheme
+.Op Fl z
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a disk image from the raw partition contents and using the
+partitioning scheme specified.
+.Sh SEE ALSO
+.Xr makefs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 10.0

Added: user/marcel/mkimg/mkimg.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/marcel/mkimg/mkimg.c	Sun Aug  4 02:37:05 2013	(r253923)
@@ -0,0 +1,320 @@
+/*-
+ * Copyright (c) 2013 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "scheme.h"
+
+#define	BUFFER_SIZE	(1024*1024)
+
+struct part {
+	STAILQ_ENTRY(part) link;
+	char	*type;		/* Partition type. */
+	char	*contents;	/* Contents/size specification. */
+	u_int	kind;		/* Content kind. */
+#define	PART_UNDEF	0
+#define	PART_KIND_FILE	1
+#define	PART_KIND_PIPE	2
+#define	PART_KIND_SIZE	3
+	u_int	index;		/* Partition index (0-based). */
+	off_t	offset;		/* Byte-offset of partition in image. */
+	off_t	size;		/* Size in bytes of partition. */
+};
+
+static STAILQ_HEAD(, part) parts = STAILQ_HEAD_INITIALIZER(parts);
+static u_int nparts = 0;
+
+static int bcfd = 0;
+static int outfd = 0;
+static int tmpfd = -1;
+
+static char tmpfname[] = "/tmp/mkimg-XXXXXX";
+
+static void
+cleanup(void)
+{
+
+	if (tmpfd != -1)
+		close(tmpfd);
+	unlink(tmpfname);
+}
+
+static void
+usage(const char *why)
+{
+	warnx("error: %s", why);
+	fprintf(stderr, "usage: %s <options>\n", getprogname());
+	fprintf(stderr, "    options:\n");
+	fprintf(stderr, "\t-b <bootcode>\n");
+	fprintf(stderr, "\t-o <file>\n");
+	fprintf(stderr, "\t-p <partition>\n");
+	fprintf(stderr, "\t-s <scheme>\n");
+	fprintf(stderr, "\t-z\n");
+	exit(EX_USAGE);
+}
+
+/*
+ * A partition specification has the following format:
+ *	<type> ':' <kind> <contents>
+ * where:
+ *	type	  the partition type alias
+ *	kind	  the interpretation of the contents specification
+ *		  ':'   contents holds the size of an empty partition
+ *		  '='   contents holds the name of a file to read
+ *		  '!'   contents holds a command to run; the output of
+ *			which is the contents of the partition.
+ *	contents  the specification of a partition's contents
+ */
+static int
+parse_part(const char *spec)
+{
+	struct part *part;
+	char *sep;
+	size_t len;
+	int error;
+
+	part = calloc(1, sizeof(struct part));
+	if (part == NULL)
+		return (ENOMEM);
+
+	sep = strchr(spec, ':');
+	if (sep == NULL) {
+		error = EINVAL;
+		goto errout;
+	}
+	len = sep - spec + 1;
+	if (len < 2) {
+		error = EINVAL;
+		goto errout;
+	}
+	part->type = malloc(len);
+	if (part->type == NULL) {
+		error = ENOMEM;
+		goto errout;
+	}
+	strlcpy(part->type, spec, len);
+	spec = sep + 1;
+
+	switch (*spec) {
+	case ':':
+		part->kind = PART_KIND_SIZE;
+		break;
+	case '=':
+		part->kind = PART_KIND_FILE;
+		break;
+	case '!':
+		part->kind = PART_KIND_PIPE;
+		break;
+	default:
+		error = EINVAL;
+		goto errout;
+	}
+	spec++;
+
+	part->contents = strdup(spec);
+	if (part->contents == NULL) {
+		error = ENOMEM;
+		goto errout;
+	}
+
+	part->index = nparts;
+	STAILQ_INSERT_TAIL(&parts, part, link);
+	nparts++;
+	return (0);
+
+ errout:
+	if (part->type != NULL)
+		free(part->type);
+	free(part);
+	return (error);
+}
+
+static int
+fdcopy(int src, int dst, uint64_t *count)
+{
+	void *buffer;
+	ssize_t rdsz, wrsz;
+
+	if (count != 0)
+		*count = 0;
+
+	buffer = malloc(BUFFER_SIZE);
+	if (buffer == NULL)
+		return (errno);
+	while (1) {
+		rdsz = read(src, buffer, BUFFER_SIZE);
+		if (rdsz <= 0) {
+			free(buffer);
+			return ((rdsz < 0) ? errno : 0);
+		}
+		if (count != NULL)
+			*count += rdsz;
+		wrsz = write(dst, buffer, rdsz);
+		if (wrsz < 0)
+			break;
+	}
+	free(buffer);
+	return (errno);
+}
+
+static void
+mkimg(void)
+{
+	FILE *fp;
+	struct part *part;
+	off_t offset;
+	uint64_t size;
+	int error, fd;
+
+	if (nparts > scheme_max_parts())
+		errc(EX_DATAERR, ENOSPC, "only %d partitions are supported",
+		    scheme_max_parts());
+
+	offset = scheme_first_offset(nparts);
+	STAILQ_FOREACH(part, &parts, link) {
+		part->offset = offset;
+		lseek(tmpfd, offset, SEEK_SET);
+		/* XXX check error */
+
+		error = 0;
+		switch (part->kind) {
+		case PART_KIND_SIZE:
+			if (expand_number(part->contents, &size) == -1)
+				error = errno;
+			break;
+		case PART_KIND_FILE:
+			fd = open(part->contents, O_RDONLY, 0);
+			if (fd != -1) {
+				error = fdcopy(fd, tmpfd, &size);
+				close(fd);
+			} else
+				error = errno;
+			break;
+		case PART_KIND_PIPE:
+			fp = popen(part->contents, "r");
+			if (fp != NULL) {
+				error = fdcopy(fileno(fp), tmpfd, &size);
+				pclose(fp);
+			} else
+				error = errno;
+			break;
+		}
+		part->size = size;
+		scheme_add_part(part->index, part->type, part->offset,
+		    part->size);
+		offset = scheme_next_offset(offset, size);
+	}
+
+	scheme_write(tmpfd, offset);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c, error;
+
+	while ((c = getopt(argc, argv, "b:h:o:p:s:t:z")) != -1) {
+		switch (c) {
+		case 'b':	/* BOOT CODE */
+			if (bcfd != 0)
+				usage("multiple bootcode given");
+			bcfd = open(optarg, O_RDONLY, 0);
+			if (bcfd == -1)
+				err(EX_UNAVAILABLE, "%s", optarg);
+			break;
+		case 'h':	/* GEOMETRY: HEADS */
+			break;
+		case 'o':	/* OUTPUT FILE */
+			if (outfd != 0)
+				usage("multiple output files given");
+			outfd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC,
+			    S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+			if (outfd == -1)
+				err(EX_CANTCREAT, "%s", optarg);
+			break;
+		case 'p':	/* PARTITION */
+			error = parse_part(optarg);
+			if (error)
+				errc(EX_DATAERR, error, "partition");
+			break;
+		case 's':	/* SCHEME */
+			if (scheme_selected() != SCHEME_UNDEF)
+				usage("multiple schemes given");
+			error = scheme_select(optarg);
+			if (error)
+				errc(EX_DATAERR, error, "scheme");
+			break;
+		case 't':	/* GEOMETRY: TRACK SIZE */
+			break;
+		case 'z':	/* SPARSE OUTPUT */
+			break;
+		default:
+			usage("unknown option");
+		}
+	}
+	if (argc > optind)
+		usage("trailing arguments");
+	if (scheme_selected() == SCHEME_UNDEF)
+		usage("no scheme");
+	if (nparts == 0)
+		usage("no partitions");
+
+	if (outfd == 0) {
+		if (atexit(cleanup) == -1)
+			err(EX_OSERR, "cannot register cleanup function");
+		outfd = 1;
+		tmpfd = mkstemp(tmpfname);
+		if (tmpfd == -1)
+			err(EX_OSERR, "cannot create temporary file");
+	} else
+		tmpfd = outfd;
+
+	mkimg();
+
+	if (tmpfd != outfd) {
+		if (lseek(tmpfd, 0, SEEK_SET) == 0)
+			error = fdcopy(tmpfd, outfd, NULL);
+		else
+			error = errno;
+		/* XXX check error */
+	}
+
+	return (0);
+}

Added: user/marcel/mkimg/scheme.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/marcel/mkimg/scheme.c	Sun Aug  4 02:37:05 2013	(r253923)
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 2013 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/diskmbr.h>
+#include <sys/vtoc.h>
+#include <err.h>
+#include <errno.h>
+#include <stdint.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "scheme.h"
+
+#define	MAX(a, b)	((a < b) ? b : a)
+
+static struct scheme {
+	const char *lexeme;
+	u_int token;
+} schemes[] = {
+	{ .lexeme = "apm", .token = SCHEME_APM },
+	{ .lexeme = "bsd", .token = SCHEME_BSD },
+	{ .lexeme = "ebr", .token = SCHEME_EBR },
+	{ .lexeme = "gpt", .token = SCHEME_GPT },
+	{ .lexeme = "mbr", .token = SCHEME_MBR },
+	{ .lexeme = "pc98", .token = SCHEME_PC98 },
+	{ .lexeme = "vtoc8", .token = SCHEME_VTOC8 },
+	{ .lexeme = NULL, .token = SCHEME_UNDEF }
+};
+
+static u_int scheme = SCHEME_UNDEF;
+static u_int secsz = 512;
+static u_int nparts = 0;
+
+int
+scheme_select(const char *spec)
+{
+	struct scheme *s;
+
+	s = schemes;
+	while (s->lexeme != NULL) {
+		if (strcasecmp(spec, s->lexeme) == 0) {
+			scheme = s->token;
+			return (0);
+		}
+		s++;
+	}
+	return (EINVAL);
+}
+
+u_int
+scheme_selected(void)
+{
+
+	return (scheme);
+}
+
+int
+scheme_add_part(u_int idx, const char *type, off_t offset, off_t size)
+{
+
+	warnx("part: index=%u, type=`%s', offset=%ju, size=%ju", idx,
+	    type, (uintmax_t)offset, (uintmax_t)size);
+	switch (scheme) {
+	case SCHEME_APM:
+		break;
+	case SCHEME_BSD:
+		break;
+	case SCHEME_EBR:
+		break;
+	case SCHEME_GPT:
+		break;
+	case SCHEME_MBR:
+		break;
+	case SCHEME_PC98:
+		break;
+	case SCHEME_VTOC8:
+		break;
+	}
+	return (0);
+}
+
+u_int
+scheme_max_parts(void)
+{
+	u_int parts;
+
+	switch (scheme) {
+	case SCHEME_APM:
+		parts = 4096;
+		break;
+	case SCHEME_BSD:
+		parts = 20;
+		break;
+	case SCHEME_EBR:
+		parts = 4096;
+		break;
+	case SCHEME_GPT:
+		parts = 4096;
+		break;
+	case SCHEME_MBR:
+		parts = 4;
+		break;
+	case SCHEME_PC98:
+		parts = 16;
+		break;
+	case SCHEME_VTOC8:
+		parts = VTOC8_NPARTS;
+		break;
+	default:
+		parts = 0;
+		break;
+	}
+	return (parts);
+}
+
+off_t
+scheme_first_offset(u_int parts)
+{
+	off_t off;
+
+	nparts = parts;		/* Save nparts for later. */
+	switch (scheme) {
+	case SCHEME_APM:
+		off = parts + 1;
+		break;
+	case SCHEME_BSD:
+		off = 16;
+		break;
+	case SCHEME_EBR:
+		off = 1;
+		break;
+	case SCHEME_GPT:
+		off = 2 + (MAX(128, parts) + 3) / 4;
+		break;
+	case SCHEME_MBR:
+		off = 1;
+		break;
+	case SCHEME_PC98:
+		off = 16;
+		break;
+	case SCHEME_VTOC8:
+		off = 1;
+		break;
+	default:
+		off = 0;
+		break;
+	}
+	off *= secsz;
+	return (off);
+}
+
+off_t
+scheme_next_offset(off_t off, uint64_t sz)
+{
+
+	sz = (sz + secsz - 1) & ~(secsz - 1);
+	if (scheme == SCHEME_EBR)
+		sz += secsz;
+	return (off + sz);
+}
+
+void
+scheme_write(int fd, off_t off)
+{
+	off_t lim;
+
+	switch (scheme) {
+	case SCHEME_GPT:
+		lim = off + secsz * (1 + (MAX(128, nparts) + 3) / 4);
+		break;
+	case SCHEME_EBR:
+		off -= secsz;
+		/* FALLTHROUGH */
+	default:
+		lim = off;
+		break;
+	}
+	ftruncate(fd, lim);
+}

Added: user/marcel/mkimg/scheme.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/marcel/mkimg/scheme.h	Sun Aug  4 02:37:05 2013	(r253923)
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2013 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MKIMG_SCHEME_H_
+#define	_MKIMG_SCHEME_H_
+
+#define	SCHEME_UNDEF	0
+#define	SCHEME_APM	1
+#define	SCHEME_BSD	2
+#define	SCHEME_EBR	3
+#define	SCHEME_GPT	4
+#define	SCHEME_MBR	5
+#define	SCHEME_PC98	6
+#define	SCHEME_VTOC8	7
+
+int	scheme_select(const char *);
+u_int	scheme_selected(void);
+
+int scheme_add_part(u_int, const char *, off_t, off_t);
+u_int scheme_max_parts(void);
+off_t scheme_first_offset(u_int);
+off_t scheme_next_offset(off_t, uint64_t);
+void scheme_write(int, off_t);
+
+#endif /* _MKIMG_SCHEME_H_ */


More information about the svn-src-user mailing list