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:
parent
f721133eb9
commit
0e651cfe2e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272072
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user