Author: marcel
Date: Thu Oct  9 13:36:37 2014
New Revision: 272819
URL: https://svnweb.freebsd.org/changeset/base/272819

Log:
  MF10 r272773: Add QCOW v1 & v2 support
  
  Requested by: gjb@
  Approved by: re (marius)
  Relnotes: yes

Added:
  releng/10.1/usr.bin/mkimg/qcow.c
     - copied unchanged from r272773, stable/10/usr.bin/mkimg/qcow.c
Modified:
  releng/10.1/usr.bin/mkimg/Makefile
Directory Properties:
  releng/10.1/   (props changed)

Modified: releng/10.1/usr.bin/mkimg/Makefile
==============================================================================
--- releng/10.1/usr.bin/mkimg/Makefile  Thu Oct  9 12:54:56 2014        
(r272818)
+++ releng/10.1/usr.bin/mkimg/Makefile  Thu Oct  9 13:36:37 2014        
(r272819)
@@ -8,6 +8,7 @@ CFLAGS+=-DSPARSE_WRITE
 
 # List of formats to support
 SRCS+= \
+       qcow.c \
        raw.c \
        vhd.c \
        vmdk.c

Copied: releng/10.1/usr.bin/mkimg/qcow.c (from r272773, 
stable/10/usr.bin/mkimg/qcow.c)
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ releng/10.1/usr.bin/mkimg/qcow.c    Thu Oct  9 13:36:37 2014        
(r272819, copy of r272773, stable/10/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);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to