Author: marcel
Date: Wed Sep 24 15:14:01 2014
New Revision: 272072
URL: http://svnweb.freebsd.org/changeset/base/272072

Log:
  Fix the creation of the L2 cluster table for version 1. The blkofs
  variable was assigned the image offset in bytes and not in blocks
  (i.e. sectors). This had image_data() return FALSE, which meant that
  we didn't assign a cluster when we needed and also meant that we
  didn't write parts of the L2 table when we should have. The result
  being that the actual data clusters were written at the wrong offset.
  
  Improve support for QCOW version 2. We're having the right layout
  and even know how many refcnt blocks we need. All we need to do is
  populate the refcnt blocks for every cluster we write and allocate
  a cluster when we need a new refcnt block. The allocation part is
  tricky in that it'll interleave with the assignment of clusters to
  L2 tables and data. Since version 2 is not quite done, keep it
  compiled out for now.

Modified:
  head/usr.bin/mkimg/qcow.c

Modified: head/usr.bin/mkimg/qcow.c
==============================================================================
--- head/usr.bin/mkimg/qcow.c   Wed Sep 24 14:35:08 2014        (r272071)
+++ head/usr.bin/mkimg/qcow.c   Wed Sep 24 15:14:01 2014        (r272072)
@@ -46,6 +46,10 @@ __FBSDID("$FreeBSD$");
 #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
@@ -90,7 +94,7 @@ round_clstr(uint64_t ofs)
 static int
 qcow_resize(lba_t imgsz, u_int version)
 {
-       uint64_t clstrsz, imagesz;
+       uint64_t imagesz;
 
        switch (version) {
        case QCOW_VERSION_1:
@@ -103,12 +107,11 @@ qcow_resize(lba_t imgsz, u_int version)
                return (EDOOFUS);
        }
 
-       clstrsz = 1UL << clstr_log2sz;
        imagesz = round_clstr(imgsz * secsz);
 
        if (verbose)
-               fprintf(stderr, "QCOW: image size = %ju, cluster size = %ju\n",
-                   (uintmax_t)imagesz, (uintmax_t)clstrsz);
+               fprintf(stderr, "QCOW: image size = %ju, cluster size = %u\n",
+                   (uintmax_t)imagesz, (u_int)(1U << clstr_log2sz));
 
        return (image_set_size(imagesz / secsz));
 }
@@ -135,24 +138,45 @@ qcow_write(int fd, u_int version)
        struct qcow_header *hdr;
        uint64_t *l1tbl, *l2tbl;
        uint16_t *rctbl;
-       uint64_t n, clstrsz, imagesz, nclstrs;
-       uint64_t l1ofs, l2ofs, ofs, rcofs;
-       lba_t blk, blkofs, blkcnt, imgsz;
-       u_int l1idx, l2idx, l2clstrs;
+       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 = 1UL << clstr_log2sz;
-       blkcnt = clstrsz / secsz;
-       imgsz = image_get_size();
-       imagesz = imgsz * secsz;
-       nclstrs = imagesz >> clstr_log2sz;
-       l2clstrs = (nclstrs * 8 + clstrsz - 1) > clstr_log2sz;
+       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 - First RC block (v2 only)
+        * 4 - L2 tables
+        * n - data
+        */
 
-       l1ofs = clstrsz;
-       rcofs = round_clstr(l1ofs + l2clstrs * 8);
+       l1clno = 1;
+       rcclno = 0;
 
        hdr = calloc(1, clstrsz);
        if (hdr == NULL)
@@ -163,18 +187,21 @@ qcow_write(int fd, u_int version)
        be64enc(&hdr->disk_size, imagesz);
        switch (version) {
        case QCOW_VERSION_1:
-               l2ofs = rcofs;  /* No reference counting. */
+               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, l1ofs);
+               be64enc(&hdr->u.v1.l1_offset, clstrsz * l1clno);
                break;
        case QCOW_VERSION_2:
-               l2ofs = round_clstr(rcofs + (nclstrs + l2clstrs) * 2);
+               ofsflags = QCOW_CLSTR_COPIED;
+               rcclno = l1clno + clstr_l1tblsz;
+               l2clno = rcclno + clstr_rctblsz + 1;
                be32enc(&hdr->clstr_log2sz, clstr_log2sz);
-               be32enc(&hdr->u.v2.l1_entries, l2clstrs);
-               be64enc(&hdr->u.v2.l1_offset, l1ofs);
-               be64enc(&hdr->u.v2.refcnt_offset, rcofs);
-               be32enc(&hdr->u.v2.refcnt_entries, l2clstrs);
+               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);
@@ -183,27 +210,27 @@ qcow_write(int fd, u_int version)
        l2tbl = l1tbl = NULL;
        rctbl = NULL;
 
-       l1tbl = calloc(1, (size_t)(rcofs - l1ofs));
+       l1tbl = calloc(1, clstrsz * clstr_l1tblsz);
        if (l1tbl == NULL) {
                error = ENOMEM;
                goto out;
        }
-       if (l2ofs != rcofs) {
-               rctbl = calloc(1, (size_t)(l2ofs - rcofs));
+       if (rcclno > 0) {
+               rctbl = calloc(1, clstrsz * clstr_rctblsz);
                if (rctbl == NULL) {
                        error = ENOMEM;
                        goto out;
                }
        }
 
-       ofs = l2ofs;
-       for (n = 0; n < nclstrs; n++) {
+       ofs = clstrsz * l2clno;
+       for (n = 0; n < clstr_imgsz; n++) {
                l1idx = n >> (clstr_log2sz - 3);
                if (l1tbl[l1idx] != 0UL)
                        continue;
-               blk = n * blkcnt;
-               if (image_data(blk, blkcnt)) {
-                       be64enc(l1tbl + l1idx, ofs);
+               blk = n * blk_clstrsz;
+               if (image_data(blk, blk_clstrsz)) {
+                       be64enc(l1tbl + l1idx, ofs + ofsflags);
                        ofs += clstrsz;
                }
        }
@@ -211,9 +238,15 @@ qcow_write(int fd, u_int version)
        error = 0;
        if (!error && sparse_write(fd, hdr, clstrsz) < 0)
                error = errno;
-       if (!error && sparse_write(fd, l1tbl, (size_t)(rcofs - l1ofs)) < 0)
+       if (!error && sparse_write(fd, l1tbl, clstrsz * clstr_l1tblsz) < 0)
                error = errno;
-       /* XXX refcnt table. */
+       if (rcclno > 0) {
+               if (!error &&
+                   sparse_write(fd, rctbl, clstrsz * clstr_rctblsz) < 0)
+                       error = errno;
+               if (!error && sparse_write(fd, rctbl, clstrsz) < 0)
+                       error = errno;
+       }
        if (error)
                goto out;
 
@@ -230,17 +263,17 @@ qcow_write(int fd, u_int version)
                goto out;
        }
 
-       for (l1idx = 0; l1idx < l2clstrs; l1idx++) {
+       for (l1idx = 0; l1idx < clstr_l2tbls; l1idx++) {
                if (l1tbl[l1idx] == 0)
                        continue;
                memset(l2tbl, 0, clstrsz);
-               blkofs = (lba_t)l1idx * (clstrsz * (clstrsz >> 3));
+               blkofs = (lba_t)l1idx * blk_clstrsz * (clstrsz >> 3);
                for (l2idx = 0; l2idx < (clstrsz >> 3); l2idx++) {
-                       blk = blkofs + (lba_t)l2idx * blkcnt;
-                       if (blk >= imgsz)
+                       blk = blkofs + (lba_t)l2idx * blk_clstrsz;
+                       if (blk >= blk_imgsz)
                                break;
-                       if (image_data(blk, blkcnt)) {
-                               be64enc(l2tbl + l2idx, ofs);
+                       if (image_data(blk, blk_clstrsz)) {
+                               be64enc(l2tbl + l2idx, ofs + ofsflags);
                                ofs += clstrsz;
                        }
                }
@@ -256,10 +289,10 @@ qcow_write(int fd, u_int version)
        l1tbl = NULL;
 
        error = 0;
-       for (n = 0; n < nclstrs; n++) {
-               blk = n * blkcnt;
-               if (image_data(blk, blkcnt)) {
-                       error = image_copyout_region(fd, blk, blkcnt);
+       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;
                }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to