Date: Sun, 28 Sep 2014 00:20:08 +0000 (UTC) From: Marcel Moolenaar <marcel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-user@freebsd.org Subject: svn commit: r272231 - in user/marcel/mkimg: . tests Message-ID: <201409280020.s8S0K8lH069033@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201409280020.s8S0K8lH069033>