svn commit: r272231 - in user/marcel/mkimg: . tests
Marcel Moolenaar
marcel at FreeBSD.org
Sun Sep 28 00:20:12 UTC 2014
Author: marcel
Date: Sun Sep 28 00:20:08 2014
New Revision: 272231
URL: http://svnweb.freebsd.org/changeset/base/272231
Log:
Sync with ^head/usr.bin/mkimg at 272217
Added:
user/marcel/mkimg/qcow.c
- copied unchanged from r272230, head/usr.bin/mkimg/qcow.c
user/marcel/mkimg/tests/
- copied from r272230, head/usr.bin/mkimg/tests/
Modified:
user/marcel/mkimg/Makefile
user/marcel/mkimg/apm.c
user/marcel/mkimg/bsd.c
user/marcel/mkimg/ebr.c
user/marcel/mkimg/gpt.c
user/marcel/mkimg/image.c
user/marcel/mkimg/image.h
user/marcel/mkimg/mbr.c
user/marcel/mkimg/mkimg.1
user/marcel/mkimg/mkimg.c
user/marcel/mkimg/mkimg.h
user/marcel/mkimg/pc98.c
user/marcel/mkimg/scheme.c
user/marcel/mkimg/scheme.h
user/marcel/mkimg/vhd.c
user/marcel/mkimg/vmdk.c
user/marcel/mkimg/vtoc8.c
Directory Properties:
user/marcel/mkimg/ (props changed)
Modified: user/marcel/mkimg/Makefile
==============================================================================
--- user/marcel/mkimg/Makefile Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/Makefile Sun Sep 28 00:20:08 2014 (r272231)
@@ -1,13 +1,18 @@
# $FreeBSD$
+.include <src.opts.mk>
+
PROG= mkimg
SRCS= format.c image.c mkimg.c scheme.c
MAN= mkimg.1
+MKIMG_VERSION=20140927
+CFLAGS+=-DMKIMG_VERSION=${MKIMG_VERSION}
CFLAGS+=-DSPARSE_WRITE
# List of formats to support
SRCS+= \
+ qcow.c \
raw.c \
vhd.c \
vmdk.c
@@ -29,4 +34,8 @@ LDADD= -lutil
WARNS?= 6
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
.include <bsd.prog.mk>
Modified: user/marcel/mkimg/apm.c
==============================================================================
--- user/marcel/mkimg/apm.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/apm.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -39,6 +39,9 @@ __FBSDID("$FreeBSD$");
#include "mkimg.h"
#include "scheme.h"
+#ifndef APM_ENT_TYPE_APPLE_BOOT
+#define APM_ENT_TYPE_APPLE_BOOT "Apple_Bootstrap"
+#endif
#ifndef APM_ENT_TYPE_FREEBSD_NANDFS
#define APM_ENT_TYPE_FREEBSD_NANDFS "FreeBSD-nandfs"
#endif
@@ -54,13 +57,12 @@ static struct mkimg_alias apm_aliases[]
{ ALIAS_NONE, 0 }
};
-static u_int
-apm_metadata(u_int where)
+static lba_t
+apm_metadata(u_int where, lba_t blk)
{
- u_int secs;
- secs = (where == SCHEME_META_IMG_START) ? nparts + 2 : 0;
- return (secs);
+ blk += (where == SCHEME_META_IMG_START) ? nparts + 2 : 0;
+ return (round_block(blk));
}
static int
Modified: user/marcel/mkimg/bsd.c
==============================================================================
--- user/marcel/mkimg/bsd.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/bsd.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -52,13 +52,17 @@ static struct mkimg_alias bsd_aliases[]
{ ALIAS_NONE, 0 }
};
-static u_int
-bsd_metadata(u_int where)
+static lba_t
+bsd_metadata(u_int where, lba_t blk)
{
- u_int secs;
- secs = BBSIZE / secsz;
- return ((where == SCHEME_META_IMG_START) ? secs : 0);
+ if (where == SCHEME_META_IMG_START)
+ blk += BBSIZE / secsz;
+ else if (where == SCHEME_META_IMG_END)
+ blk = round_cylinder(blk);
+ else
+ blk = round_block(blk);
+ return (blk);
}
static int
@@ -68,7 +72,7 @@ bsd_write(lba_t imgsz, void *bootcode)
struct disklabel *d;
struct partition *dp;
struct part *part;
- int error, n;
+ int bsdparts, error, n;
uint16_t checksum;
buf = malloc(BBSIZE);
@@ -76,16 +80,13 @@ bsd_write(lba_t imgsz, void *bootcode)
return (ENOMEM);
if (bootcode != NULL) {
memcpy(buf, bootcode, BBSIZE);
- memset(buf + secsz, 0, secsz);
+ memset(buf + secsz, 0, sizeof(struct disklabel));
} else
memset(buf, 0, BBSIZE);
- imgsz = (lba_t)ncyls * nheads * nsecs;
- error = image_set_size(imgsz);
- if (error) {
- free(buf);
- return (error);
- }
+ bsdparts = nparts + 1; /* Account for c partition */
+ if (bsdparts < MAXPARTITIONS)
+ bsdparts = MAXPARTITIONS;
d = (void *)(buf + secsz);
le32enc(&d->d_magic, DISKMAGIC);
@@ -97,7 +98,7 @@ bsd_write(lba_t imgsz, void *bootcode)
le32enc(&d->d_secperunit, imgsz);
le16enc(&d->d_rpm, 3600);
le32enc(&d->d_magic2, DISKMAGIC);
- le16enc(&d->d_npartitions, (8 > nparts + 1) ? 8 : nparts + 1);
+ le16enc(&d->d_npartitions, bsdparts);
le32enc(&d->d_bbsize, BBSIZE);
dp = &d->d_partitions[RAW_PART];
@@ -107,12 +108,15 @@ bsd_write(lba_t imgsz, void *bootcode)
dp = &d->d_partitions[n];
le32enc(&dp->p_size, part->size);
le32enc(&dp->p_offset, part->block);
+ le32enc(&dp->p_fsize, 0);
dp->p_fstype = ALIAS_TYPE2INT(part->type);
+ dp->p_frag = 0;
+ le16enc(&dp->p_cpg, 0);
}
- dp = &d->d_partitions[nparts + 1];
+ dp = &d->d_partitions[bsdparts];
checksum = 0;
- for (p = buf; p < (u_char *)dp; p += 2)
+ for (p = (void *)d; p < (u_char *)dp; p += 2)
checksum ^= le16dec(p);
le16enc(&d->d_checksum, checksum);
Modified: user/marcel/mkimg/ebr.c
==============================================================================
--- user/marcel/mkimg/ebr.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/ebr.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -49,13 +49,12 @@ static struct mkimg_alias ebr_aliases[]
{ ALIAS_NONE, 0 }
};
-static u_int
-ebr_metadata(u_int where)
+static lba_t
+ebr_metadata(u_int where, lba_t blk)
{
- u_int secs;
- secs = (where == SCHEME_META_PART_BEFORE) ? nsecs : 0;
- return (secs);
+ blk += (where == SCHEME_META_PART_BEFORE) ? 1 : 0;
+ return (round_track(blk));
}
static void
Modified: user/marcel/mkimg/gpt.c
==============================================================================
--- user/marcel/mkimg/gpt.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/gpt.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -153,17 +153,15 @@ gpt_tblsz(void)
return ((nparts + ents - 1) / ents);
}
-static u_int
-gpt_metadata(u_int where)
+static lba_t
+gpt_metadata(u_int where, lba_t blk)
{
- u_int secs;
- if (where != SCHEME_META_IMG_START && where != SCHEME_META_IMG_END)
- return (0);
-
- secs = gpt_tblsz();
- secs += (where == SCHEME_META_IMG_START) ? 2 : 1;
- return (secs);
+ if (where == SCHEME_META_IMG_START || where == SCHEME_META_IMG_END) {
+ blk += gpt_tblsz();
+ blk += (where == SCHEME_META_IMG_START) ? 2 : 1;
+ }
+ return (round_block(blk));
}
static int
Modified: user/marcel/mkimg/image.c
==============================================================================
--- user/marcel/mkimg/image.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/image.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -94,12 +94,19 @@ image_copyin(lba_t blk, int fd, uint64_t
int
image_copyout(int fd)
{
- off_t ofs;
int error;
error = image_copyout_region(fd, 0, image_size);
- if (error)
- return (error);
+ if (!error)
+ error = image_copyout_done(fd);
+ return (error);
+}
+
+int
+image_copyout_done(int fd)
+{
+ off_t ofs;
+ int error;
ofs = lseek(fd, 0L, SEEK_CUR);
if (ofs == -1)
@@ -148,6 +155,33 @@ image_copyout_region(int fd, lba_t blk,
return (error);
}
+int
+image_data(lba_t blk, lba_t size)
+{
+ char *buffer, *p;
+
+ blk *= secsz;
+ if (lseek(image_fd, blk, SEEK_SET) != blk)
+ return (1);
+
+ size *= secsz;
+ buffer = malloc(size);
+ if (buffer == NULL)
+ return (1);
+
+ if (read(image_fd, buffer, size) != (ssize_t)size) {
+ free(buffer);
+ return (1);
+ }
+
+ p = buffer;
+ while (size > 0 && *p == '\0')
+ size--, p++;
+
+ free(buffer);
+ return ((size == 0) ? 0 : 1);
+}
+
lba_t
image_get_size(void)
{
Modified: user/marcel/mkimg/image.h
==============================================================================
--- user/marcel/mkimg/image.h Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/image.h Sun Sep 28 00:20:08 2014 (r272231)
@@ -33,7 +33,9 @@ typedef int64_t lba_t;
int image_copyin(lba_t blk, int fd, uint64_t *sizep);
int image_copyout(int fd);
+int image_copyout_done(int fd);
int image_copyout_region(int fd, lba_t blk, lba_t size);
+int image_data(lba_t blk, lba_t size);
lba_t image_get_size(void);
int image_init(void);
int image_set_size(lba_t blk);
Modified: user/marcel/mkimg/mbr.c
==============================================================================
--- user/marcel/mkimg/mbr.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/mbr.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -50,13 +50,12 @@ static struct mkimg_alias mbr_aliases[]
{ ALIAS_NONE, 0 } /* Keep last! */
};
-static u_int
-mbr_metadata(u_int where)
+static lba_t
+mbr_metadata(u_int where, lba_t blk)
{
- u_int secs;
- secs = (where == SCHEME_META_IMG_START) ? nsecs : 0;
- return (secs);
+ blk += (where == SCHEME_META_IMG_START) ? 1 : 0;
+ return (round_track(blk));
}
static void
Modified: user/marcel/mkimg/mkimg.1
==============================================================================
--- user/marcel/mkimg/mkimg.1 Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/mkimg.1 Sun Sep 28 00:20:08 2014 (r272231)
@@ -24,12 +24,12 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 2, 2014
+.Dd September 27, 2014
.Dt MKIMG 1
.Os
.Sh NAME
.Nm mkimg
-.Nd "utility to make a disk image"
+.Nd "utility to make disk images"
.Sh SYNOPSIS
.Nm
.Op Fl H Ar heads
@@ -40,9 +40,12 @@
.Op Fl f Ar format
.Op Fl o Ar outfile
.Op Fl v
+.Op Fl y
.Fl s Ar scheme
.Fl p Ar partition
.Op Fl p Ar partition ...
+.Nm
+.Ar --formats | --schemes | --version
.Sh DESCRIPTION
The
.Nm
@@ -111,10 +114,42 @@ option increases the level of output tha
.Nm
utility prints.
.Pp
-For a complete list of supported partitioning schemes or supported output
-format, or for a detailed description of how to specify partitions, run the
+The
+.Op Fl y
+option is used for testing purposes only and is not to be used in production.
+When present, the
+.Nm
+utility will generate predictable values for Universally Unique Identifiers
+(UUIDs) and time stamps so that consecutive runs of the
+.Nm
+utility will create images that are identical.
+.Pp
+A set of long options exist to query about the
+.Nm
+utilty itself.
+Options in this set should be given by themselves because the
+.Nm
+utility exits immediately after providing the requested information.
+The version of the
+.Nm
+utility is printed when the
+.Ar --version
+option is given.
+The list of supported output formats is printed when the
+.Ar --formats
+option is given and the list of supported partitioning schemes is printed
+when the
+.Ar --schemes
+option is given.
+Both the format and scheme lists a space-separated lists for easy handling
+in scripts.
+.Pp
+For a more descriptive list of supported partitioning schemes or supported
+output format, or for a detailed description of how to specify partitions,
+run the
.Nm
utility without any arguments.
+This will print a usage message with all the necessary details.
.Sh ENVIRONMENT
.Bl -tag -width "TMPDIR" -compact
.It Ev TMPDIR
@@ -160,6 +195,25 @@ utility as follows:
.Dl % mkimg -s mbr -b /boot/mbr -p freebsd:-'mkimg -s bsd -b /boot/boot \
-p freebsd-ufs:=root-file-system.ufs -p freebsd-swap::1G' -o mbr-bsd.img
.Pp
+To accomodate the need to have partitions named or numbered in a certain
+way, the
+.Nm
+utility allows for the specification of empty partitions.
+For example, to create an image that is compatible with partition layouts
+found in
+.Pa /etc/disktab ,
+the 'd' partition often needs to be skipped.
+This is accomplished by inserting an unused partition after the first 2
+partition specifications.
+It is worth noting at this time that the BSD scheme will automatically
+skip the 'c' partition by virtue of it referring to the entire disk.
+To create an image that is compatible with the qp120at disk, use the
+.Nm
+utility as follows:
+.Dl % mkimg -s bsd -b /boot/boot -p freebsd-ufs:=root-file-system.ufs \
+-p freebsd-swap::20M -p- -p- -p- -p- -p freebsd-ufs:=usr-file-system.ufs \
+-o bsd.img
+.Pp
For partitioning schemes that feature partition labels, the
.Nm
utility supports assigning labels to the partitions specified.
Modified: user/marcel/mkimg/mkimg.c
==============================================================================
--- user/marcel/mkimg/mkimg.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/mkimg.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <err.h>
#include <fcntl.h>
+#include <getopt.h>
#include <libutil.h>
#include <limits.h>
#include <stdio.h>
@@ -48,6 +49,17 @@ __FBSDID("$FreeBSD$");
#include "mkimg.h"
#include "scheme.h"
+#define LONGOPT_FORMATS 0x01000001
+#define LONGOPT_SCHEMES 0x01000002
+#define LONGOPT_VERSION 0x01000003
+
+static struct option longopts[] = {
+ { "formats", no_argument, NULL, LONGOPT_FORMATS },
+ { "schemes", no_argument, NULL, LONGOPT_SCHEMES },
+ { "version", no_argument, NULL, LONGOPT_VERSION },
+ { NULL, 0, NULL, 0 }
+};
+
struct partlisthead partlist = STAILQ_HEAD_INITIALIZER(partlist);
u_int nparts = 0;
@@ -61,44 +73,103 @@ u_int secsz = 512;
u_int blksz = 0;
static void
-usage(const char *why)
+print_formats(int usage)
{
struct mkimg_format *f, **f_iter;
+ const char *sep;
+
+ if (usage) {
+ fprintf(stderr, " formats:\n");
+ SET_FOREACH(f_iter, formats) {
+ f = *f_iter;
+ fprintf(stderr, "\t%s\t- %s\n", f->name,
+ f->description);
+ }
+ } else {
+ sep = "";
+ SET_FOREACH(f_iter, formats) {
+ f = *f_iter;
+ printf("%s%s", sep, f->name);
+ sep = " ";
+ }
+ putchar('\n');
+ }
+}
+
+static void
+print_schemes(int usage)
+{
struct mkimg_scheme *s, **s_iter;
+ const char *sep;
+
+ if (usage) {
+ fprintf(stderr, " schemes:\n");
+ SET_FOREACH(s_iter, schemes) {
+ s = *s_iter;
+ fprintf(stderr, "\t%s\t- %s\n", s->name,
+ s->description);
+ }
+ } else {
+ sep = "";
+ SET_FOREACH(s_iter, schemes) {
+ s = *s_iter;
+ printf("%s%s", sep, s->name);
+ sep = " ";
+ }
+ putchar('\n');
+ }
+}
+
+static void
+print_version(void)
+{
+ u_int width;
+
+#ifdef __LP64__
+ width = 64;
+#else
+ width = 32;
+#endif
+ printf("mkimg %u (%u-bit)\n", MKIMG_VERSION, width);
+}
+
+static void
+usage(const char *why)
+{
warnx("error: %s", why);
- fprintf(stderr, "\nusage: %s <options>\n", getprogname());
+ fputc('\n', stderr);
+ fprintf(stderr, "usage: %s <options>\n", getprogname());
fprintf(stderr, " options:\n");
+ fprintf(stderr, "\t--formats\t- list image formats\n");
+ fprintf(stderr, "\t--schemes\t- list partition schemes\n");
+ fprintf(stderr, "\t--version\t- show version information\n");
+ fputc('\n', stderr);
fprintf(stderr, "\t-b <file>\t- file containing boot code\n");
fprintf(stderr, "\t-f <format>\n");
fprintf(stderr, "\t-o <file>\t- file to write image into\n");
fprintf(stderr, "\t-p <partition>\n");
fprintf(stderr, "\t-s <scheme>\n");
+ fprintf(stderr, "\t-v\t\t- increase verbosity\n");
+ fprintf(stderr, "\t-y\t\t- [developers] enable unit test\n");
fprintf(stderr, "\t-H <num>\t- number of heads to simulate\n");
fprintf(stderr, "\t-P <num>\t- physical sector size\n");
fprintf(stderr, "\t-S <num>\t- logical sector size\n");
fprintf(stderr, "\t-T <num>\t- number of tracks to simulate\n");
-
- fprintf(stderr, "\n formats:\n");
- SET_FOREACH(f_iter, formats) {
- f = *f_iter;
- fprintf(stderr, "\t%s\t- %s\n", f->name, f->description);
- }
-
- fprintf(stderr, "\n schemes:\n");
- SET_FOREACH(s_iter, schemes) {
- s = *s_iter;
- fprintf(stderr, "\t%s\t- %s\n", s->name, s->description);
- }
-
- fprintf(stderr, "\n partition specification:\n");
+ fputc('\n', stderr);
+ print_formats(1);
+ fputc('\n', stderr);
+ print_schemes(1);
+ fputc('\n', stderr);
+ fprintf(stderr, " partition specification:\n");
fprintf(stderr, "\t<t>[/<l>]::<size>\t- empty partition of given "
"size\n");
fprintf(stderr, "\t<t>[/<l>]:=<file>\t- partition content and size "
"are determined\n\t\t\t\t by the named file\n");
fprintf(stderr, "\t<t>[/<l>]:-<cmd>\t- partition content and size "
"are taken from\n\t\t\t\t the output of the command to run\n");
+ fprintf(stderr, "\t-\t\t\t- unused partition entry\n");
fprintf(stderr, "\t where:\n");
fprintf(stderr, "\t\t<t>\t- scheme neutral partition type\n");
fprintf(stderr, "\t\t<l>\t- optional scheme-dependent partition "
@@ -138,6 +209,9 @@ pwr_of_two(u_int nr)
* '-' contents holds a command to run; the output of
* which is the contents of the partition.
* contents the specification of a partition's contents
+ *
+ * A specification that is a single dash indicates an unused partition
+ * entry.
*/
static int
parse_part(const char *spec)
@@ -147,6 +221,11 @@ parse_part(const char *spec)
size_t len;
int error;
+ if (strcmp(spec, "-") == 0) {
+ nparts++;
+ return (0);
+ }
+
part = calloc(1, sizeof(struct part));
if (part == NULL)
return (ENOMEM);
@@ -355,7 +434,8 @@ main(int argc, char *argv[])
bcfd = -1;
outfd = 1; /* Write to stdout by default */
- while ((c = getopt(argc, argv, "b:f:o:p:s:vyH:P:S:T:")) != -1) {
+ while ((c = getopt_long(argc, argv, "b:f:o:p:s:vyH:P:S:T:",
+ longopts, NULL)) != -1) {
switch (c) {
case 'b': /* BOOT CODE */
if (bcfd != -1)
@@ -421,6 +501,18 @@ main(int argc, char *argv[])
if (error)
errc(EX_DATAERR, error, "track size");
break;
+ case LONGOPT_FORMATS:
+ print_formats(0);
+ exit(EX_OK);
+ /*NOTREACHED*/
+ case LONGOPT_SCHEMES:
+ print_schemes(0);
+ exit(EX_OK);
+ /*NOTREACHED*/
+ case LONGOPT_VERSION:
+ print_version();
+ exit(EX_OK);
+ /*NOTREACHED*/
default:
usage("unknown option");
}
Modified: user/marcel/mkimg/mkimg.h
==============================================================================
--- user/marcel/mkimg/mkimg.h Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/mkimg.h Sun Sep 28 00:20:08 2014 (r272231)
@@ -66,6 +66,21 @@ round_block(lba_t n)
return ((n + b - 1) & ~(b - 1));
}
+static inline lba_t
+round_cylinder(lba_t n)
+{
+ u_int cyl = nsecs * nheads;
+ u_int r = n % cyl;
+ return ((r == 0) ? n : n + cyl - r);
+}
+
+static inline lba_t
+round_track(lba_t n)
+{
+ u_int r = n % nsecs;
+ return ((r == 0) ? n : n + nsecs - r);
+}
+
#if !defined(SPARSE_WRITE)
#define sparse_write write
#else
Modified: user/marcel/mkimg/pc98.c
==============================================================================
--- user/marcel/mkimg/pc98.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/pc98.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -59,13 +59,12 @@ static struct mkimg_alias pc98_aliases[]
{ ALIAS_NONE, 0 }
};
-static u_int
-pc98_metadata(u_int where)
+static lba_t
+pc98_metadata(u_int where, lba_t blk)
{
- u_int secs;
-
- secs = PC98_BOOTCODESZ / secsz;
- return ((where == SCHEME_META_IMG_START) ? secs : 0);
+ if (where == SCHEME_META_IMG_START)
+ blk += PC98_BOOTCODESZ / secsz;
+ return (round_track(blk));
}
static void
Copied: user/marcel/mkimg/qcow.c (from r272230, head/usr.bin/mkimg/qcow.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ user/marcel/mkimg/qcow.c Sun Sep 28 00:20:08 2014 (r272231, copy of r272230, head/usr.bin/mkimg/qcow.c)
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 2014 Marcel Moolenaar
+ * 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/endian.h>
+#include <sys/errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "image.h"
+#include "format.h"
+#include "mkimg.h"
+
+/* Default cluster sizes. */
+#define QCOW1_CLSTR_LOG2SZ 12 /* 4KB */
+#define QCOW2_CLSTR_LOG2SZ 16 /* 64KB */
+
+/* Flag bits in cluster offsets */
+#define QCOW_CLSTR_COMPRESSED (1ULL << 62)
+#define QCOW_CLSTR_COPIED (1ULL << 63)
+
+struct qcow_header {
+ uint32_t magic;
+#define QCOW_MAGIC 0x514649fb
+ uint32_t version;
+#define QCOW_VERSION_1 1
+#define QCOW_VERSION_2 2
+ uint64_t path_offset;
+ uint32_t path_length;
+ uint32_t clstr_log2sz; /* v2 only */
+ uint64_t disk_size;
+ union {
+ struct {
+ uint8_t clstr_log2sz;
+ uint8_t l2_log2sz;
+ uint16_t _pad;
+ uint32_t encryption;
+ uint64_t l1_offset;
+ } v1;
+ struct {
+ uint32_t encryption;
+ uint32_t l1_entries;
+ uint64_t l1_offset;
+ uint64_t refcnt_offset;
+ uint32_t refcnt_entries;
+ uint32_t snapshot_count;
+ uint64_t snapshot_offset;
+ } v2;
+ } u;
+};
+
+static u_int clstr_log2sz;
+
+static uint64_t
+round_clstr(uint64_t ofs)
+{
+ uint64_t clstrsz;
+
+ clstrsz = 1UL << clstr_log2sz;
+ return ((ofs + clstrsz - 1) & ~(clstrsz - 1));
+}
+
+static int
+qcow_resize(lba_t imgsz, u_int version)
+{
+ uint64_t imagesz;
+
+ switch (version) {
+ case QCOW_VERSION_1:
+ clstr_log2sz = QCOW1_CLSTR_LOG2SZ;
+ break;
+ case QCOW_VERSION_2:
+ clstr_log2sz = QCOW2_CLSTR_LOG2SZ;
+ break;
+ default:
+ return (EDOOFUS);
+ }
+
+ imagesz = round_clstr(imgsz * secsz);
+
+ if (verbose)
+ fprintf(stderr, "QCOW: image size = %ju, cluster size = %u\n",
+ (uintmax_t)imagesz, (u_int)(1U << clstr_log2sz));
+
+ return (image_set_size(imagesz / secsz));
+}
+
+static int
+qcow1_resize(lba_t imgsz)
+{
+
+ return (qcow_resize(imgsz, QCOW_VERSION_1));
+}
+
+static int
+qcow2_resize(lba_t imgsz)
+{
+
+ return (qcow_resize(imgsz, QCOW_VERSION_2));
+}
+
+static int
+qcow_write(int fd, u_int version)
+{
+ struct qcow_header *hdr;
+ uint64_t *l1tbl, *l2tbl, *rctbl;
+ uint16_t *rcblk;
+ uint64_t clstr_imgsz, clstr_l2tbls, clstr_l1tblsz;
+ uint64_t clstr_rcblks, clstr_rctblsz;
+ uint64_t n, imagesz, nclstrs, ofs, ofsflags;
+ lba_t blk, blkofs, blk_imgsz;
+ u_int l1clno, l2clno, rcclno;
+ u_int blk_clstrsz;
+ u_int clstrsz, l1idx, l2idx;
+ int error;
+
+ if (clstr_log2sz == 0)
+ return (EDOOFUS);
+
+ clstrsz = 1U << clstr_log2sz;
+ blk_clstrsz = clstrsz / secsz;
+ blk_imgsz = image_get_size();
+ imagesz = blk_imgsz * secsz;
+ clstr_imgsz = imagesz >> clstr_log2sz;
+ clstr_l2tbls = round_clstr(clstr_imgsz * 8) >> clstr_log2sz;
+ clstr_l1tblsz = round_clstr(clstr_l2tbls * 8) >> clstr_log2sz;
+ nclstrs = clstr_imgsz + clstr_l2tbls + clstr_l1tblsz + 1;
+ clstr_rcblks = clstr_rctblsz = 0;
+ do {
+ n = clstr_rcblks + clstr_rctblsz;
+ clstr_rcblks = round_clstr((nclstrs + n) * 2) >> clstr_log2sz;
+ clstr_rctblsz = round_clstr(clstr_rcblks * 8) >> clstr_log2sz;
+ } while (n < (clstr_rcblks + clstr_rctblsz));
+
+ /*
+ * We got all the sizes in clusters. Start the layout.
+ * 0 - header
+ * 1 - L1 table
+ * 2 - RC table (v2 only)
+ * 3 - L2 tables
+ * 4 - RC block (v2 only)
+ * 5 - data
+ */
+
+ l1clno = 1;
+ rcclno = 0;
+ rctbl = l2tbl = l1tbl = NULL;
+ rcblk = NULL;
+
+ hdr = calloc(1, clstrsz);
+ if (hdr == NULL)
+ return (errno);
+
+ be32enc(&hdr->magic, QCOW_MAGIC);
+ be32enc(&hdr->version, version);
+ be64enc(&hdr->disk_size, imagesz);
+ switch (version) {
+ case QCOW_VERSION_1:
+ ofsflags = 0;
+ l2clno = l1clno + clstr_l1tblsz;
+ hdr->u.v1.clstr_log2sz = clstr_log2sz;
+ hdr->u.v1.l2_log2sz = clstr_log2sz - 3;
+ be64enc(&hdr->u.v1.l1_offset, clstrsz * l1clno);
+ break;
+ case QCOW_VERSION_2:
+ ofsflags = QCOW_CLSTR_COPIED;
+ rcclno = l1clno + clstr_l1tblsz;
+ l2clno = rcclno + clstr_rctblsz;
+ be32enc(&hdr->clstr_log2sz, clstr_log2sz);
+ be32enc(&hdr->u.v2.l1_entries, clstr_l2tbls);
+ be64enc(&hdr->u.v2.l1_offset, clstrsz * l1clno);
+ be64enc(&hdr->u.v2.refcnt_offset, clstrsz * rcclno);
+ be32enc(&hdr->u.v2.refcnt_entries, clstr_rcblks);
+ break;
+ default:
+ return (EDOOFUS);
+ }
+
+ if (sparse_write(fd, hdr, clstrsz) < 0) {
+ error = errno;
+ goto out;
+ }
+
+ free(hdr);
+ hdr = NULL;
+
+ ofs = clstrsz * l2clno;
+ nclstrs = 1 + clstr_l1tblsz + clstr_rctblsz;
+
+ l1tbl = calloc(1, clstrsz * clstr_l1tblsz);
+ if (l1tbl == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ for (n = 0; n < clstr_imgsz; n++) {
+ blk = n * blk_clstrsz;
+ if (image_data(blk, blk_clstrsz)) {
+ nclstrs++;
+ l1idx = n >> (clstr_log2sz - 3);
+ if (l1tbl[l1idx] == 0) {
+ be64enc(l1tbl + l1idx, ofs + ofsflags);
+ ofs += clstrsz;
+ nclstrs++;
+ }
+ }
+ }
+
+ if (sparse_write(fd, l1tbl, clstrsz * clstr_l1tblsz) < 0) {
+ error = errno;
+ goto out;
+ }
+
+ clstr_rcblks = 0;
+ do {
+ n = clstr_rcblks;
+ clstr_rcblks = round_clstr((nclstrs + n) * 2) >> clstr_log2sz;
+ } while (n < clstr_rcblks);
+
+ if (rcclno > 0) {
+ rctbl = calloc(1, clstrsz * clstr_rctblsz);
+ if (rctbl == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ for (n = 0; n < clstr_rcblks; n++) {
+ be64enc(rctbl + n, ofs);
+ ofs += clstrsz;
+ nclstrs++;
+ }
+ if (sparse_write(fd, rctbl, clstrsz * clstr_rctblsz) < 0) {
+ error = errno;
+ goto out;
+ }
+ free(rctbl);
+ rctbl = NULL;
+ }
+
+ l2tbl = malloc(clstrsz);
+ if (l2tbl == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ for (l1idx = 0; l1idx < clstr_l2tbls; l1idx++) {
+ if (l1tbl[l1idx] == 0)
+ continue;
+ memset(l2tbl, 0, clstrsz);
+ blkofs = (lba_t)l1idx * blk_clstrsz * (clstrsz >> 3);
+ for (l2idx = 0; l2idx < (clstrsz >> 3); l2idx++) {
+ blk = blkofs + (lba_t)l2idx * blk_clstrsz;
+ if (blk >= blk_imgsz)
+ break;
+ if (image_data(blk, blk_clstrsz)) {
+ be64enc(l2tbl + l2idx, ofs + ofsflags);
+ ofs += clstrsz;
+ }
+ }
+ if (sparse_write(fd, l2tbl, clstrsz) < 0) {
+ error = errno;
+ goto out;
+ }
+ }
+
+ free(l2tbl);
+ l2tbl = NULL;
+ free(l1tbl);
+ l1tbl = NULL;
+
+ if (rcclno > 0) {
+ rcblk = calloc(1, clstrsz * clstr_rcblks);
+ if (rcblk == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ for (n = 0; n < nclstrs; n++)
+ be16enc(rcblk + n, 1);
+ if (sparse_write(fd, rcblk, clstrsz * clstr_rcblks) < 0) {
+ error = errno;
+ goto out;
+ }
+ free(rcblk);
+ rcblk = NULL;
+ }
+
+ error = 0;
+ for (n = 0; n < clstr_imgsz; n++) {
+ blk = n * blk_clstrsz;
+ if (image_data(blk, blk_clstrsz)) {
+ error = image_copyout_region(fd, blk, blk_clstrsz);
+ if (error)
+ break;
+ }
+ }
+ if (!error)
+ error = image_copyout_done(fd);
+
+ out:
+ if (rcblk != NULL)
+ free(rcblk);
+ if (l2tbl != NULL)
+ free(l2tbl);
+ if (rctbl != NULL)
+ free(rctbl);
+ if (l1tbl != NULL)
+ free(l1tbl);
+ if (hdr != NULL)
+ free(hdr);
+ return (error);
+}
+
+static int
+qcow1_write(int fd)
+{
+
+ return (qcow_write(fd, QCOW_VERSION_1));
+}
+
+static int
+qcow2_write(int fd)
+{
+
+ return (qcow_write(fd, QCOW_VERSION_2));
+}
+
+static struct mkimg_format qcow1_format = {
+ .name = "qcow",
+ .description = "QEMU Copy-On-Write, version 1",
+ .resize = qcow1_resize,
+ .write = qcow1_write,
+};
+FORMAT_DEFINE(qcow1_format);
+
+static struct mkimg_format qcow2_format = {
+ .name = "qcow2",
+ .description = "QEMU Copy-On-Write, version 2",
+ .resize = qcow2_resize,
+ .write = qcow2_write,
+};
+FORMAT_DEFINE(qcow2_format);
Modified: user/marcel/mkimg/scheme.c
==============================================================================
--- user/marcel/mkimg/scheme.c Sat Sep 27 23:57:21 2014 (r272230)
+++ user/marcel/mkimg/scheme.c Sun Sep 28 00:20:08 2014 (r272231)
@@ -171,10 +171,8 @@ scheme_max_secsz(void)
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-user
mailing list