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.
This commit is contained in:
Marcel Moolenaar 2014-09-24 15:14:01 +00:00
parent f721133eb9
commit 0e651cfe2e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272072

View File

@ -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));
l1ofs = clstrsz;
rcofs = round_clstr(l1ofs + l2clstrs * 8);
/*
* 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
*/
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;
}