diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2025-09-01 14:53:13 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2025-09-01 15:11:56 -0400 |
commit | e65dd86e419d96aaa616dcb508d3564bd5721ef6 (patch) | |
tree | b307ec60ce65af9a10c8f65b4b7e8a23b55fbffc /c_src/qcow2.c | |
parent | da8f1d04e3b89a576cbb5fdd7cd1c1c451be11d8 (diff) |
bcachefs undump
Add our own version of 'qemu-img convert', which doesn't have the l1
table size limit or require fixing our missing reflink table.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'c_src/qcow2.c')
-rw-r--r-- | c_src/qcow2.c | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/c_src/qcow2.c b/c_src/qcow2.c index 53959a00..7afb6812 100644 --- a/c_src/qcow2.c +++ b/c_src/qcow2.c @@ -143,8 +143,7 @@ void qcow2_image_finish(struct qcow2_image *img) memset(buf, 0, img->block_size); memcpy(buf, &hdr, sizeof(hdr)); - xpwrite(img->outfd, buf, img->block_size, 0, - "qcow2 header"); + xpwrite(img->outfd, buf, img->block_size, 0, "qcow2 header"); free(img->l2_table); free(img->l1_table); @@ -160,3 +159,51 @@ void qcow2_write_image(int infd, int outfd, ranges *data, qcow2_write_ranges(&img, data); qcow2_image_finish(&img); } + +void qcow2_to_raw(int infd, int outfd) +{ + struct qcow2_hdr hdr; + + xpread(infd, &hdr, sizeof(hdr), 0); + + if (hdr.magic != cpu_to_be32(QCOW_MAGIC)) + die("not a qcow2 image"); + + if (hdr.version != cpu_to_be32(QCOW_VERSION)) + die("incorrect qcow2 version"); + + ftruncate(outfd, be64_to_cpu(hdr.size)); + + unsigned block_size = 1U << be32_to_cpu(hdr.block_bits); + + unsigned l1_size = be32_to_cpu(hdr.l1_size); + unsigned l2_size = block_size / sizeof(u64); + + __be64 *l1_table = xcalloc(l1_size, sizeof(u64)); + __be64 *l2_table = xmalloc(block_size); + void *data_buf = xmalloc(block_size); + + xpread(infd, l1_table, l1_size * sizeof(u64), be64_to_cpu(hdr.l1_table_offset)); + + for (u64 i = 0; i < l1_size; i++) { + if (!l1_table[i]) + continue; + + xpread(infd, l2_table, block_size, be64_to_cpu(l1_table[i]) & ~QCOW_OFLAG_COPIED); + + for (unsigned j = 0; j < l2_size; j++) { + u64 src_offset = be64_to_cpu(l2_table[j]) & ~QCOW_OFLAG_COPIED; + if (!src_offset) + continue; + + u64 dst_offset = (i * l2_size + j) * block_size; + + xpread(infd, data_buf, block_size, src_offset); + xpwrite(outfd, data_buf, block_size, dst_offset, "qcow2 data"); + } + } + + free(data_buf); + free(l2_table); + free(l1_table); +} |