mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-01-22 00:04:31 +03:00
f5baaf48e3
This moves the Rust sources out of rust_src/ and into the top level. Running the bcachefs executable out of the development tree is now: $ ./target/release/bcachefs command or $ cargo run --profile release -- command instead of "./bcachefs command". Building and installing is still: $ make && make install Signed-off-by: Thomas Bertschinger <tahbertschinger@gmail.com> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
135 lines
3.0 KiB
C
135 lines
3.0 KiB
C
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "qcow2.h"
|
|
#include "tools-util.h"
|
|
|
|
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
|
|
#define QCOW_VERSION 2
|
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
|
|
|
struct qcow2_hdr {
|
|
u32 magic;
|
|
u32 version;
|
|
|
|
u64 backing_file_offset;
|
|
u32 backing_file_size;
|
|
|
|
u32 block_bits;
|
|
u64 size;
|
|
u32 crypt_method;
|
|
|
|
u32 l1_size;
|
|
u64 l1_table_offset;
|
|
|
|
u64 refcount_table_offset;
|
|
u32 refcount_table_blocks;
|
|
|
|
u32 nb_snapshots;
|
|
u64 snapshots_offset;
|
|
};
|
|
|
|
struct qcow2_image {
|
|
int fd;
|
|
u32 block_size;
|
|
u64 *l1_table;
|
|
u64 l1_offset;
|
|
u32 l1_index;
|
|
u64 *l2_table;
|
|
u64 offset;
|
|
};
|
|
|
|
static void flush_l2(struct qcow2_image *img)
|
|
{
|
|
if (img->l1_index != -1) {
|
|
img->l1_table[img->l1_index] =
|
|
cpu_to_be64(img->offset|QCOW_OFLAG_COPIED);
|
|
xpwrite(img->fd, img->l2_table, img->block_size, img->offset,
|
|
"qcow2 l2 table");
|
|
img->offset += img->block_size;
|
|
|
|
memset(img->l2_table, 0, img->block_size);
|
|
img->l1_index = -1;
|
|
}
|
|
}
|
|
|
|
static void add_l2(struct qcow2_image *img, u64 src_blk, u64 dst_offset)
|
|
{
|
|
unsigned l2_size = img->block_size / sizeof(u64);
|
|
u64 l1_index = src_blk / l2_size;
|
|
u64 l2_index = src_blk & (l2_size - 1);
|
|
|
|
if (img->l1_index != l1_index) {
|
|
flush_l2(img);
|
|
img->l1_index = l1_index;
|
|
}
|
|
|
|
img->l2_table[l2_index] = cpu_to_be64(dst_offset|QCOW_OFLAG_COPIED);
|
|
}
|
|
|
|
void qcow2_write_image(int infd, int outfd, ranges *data,
|
|
unsigned block_size)
|
|
{
|
|
u64 image_size = get_size(infd);
|
|
unsigned l2_size = block_size / sizeof(u64);
|
|
unsigned l1_size = DIV_ROUND_UP(image_size, (u64) block_size * l2_size);
|
|
struct qcow2_hdr hdr = { 0 };
|
|
struct qcow2_image img = {
|
|
.fd = outfd,
|
|
.block_size = block_size,
|
|
.l2_table = xcalloc(l2_size, sizeof(u64)),
|
|
.l1_table = xcalloc(l1_size, sizeof(u64)),
|
|
.l1_index = -1,
|
|
.offset = round_up(sizeof(hdr), block_size),
|
|
};
|
|
char *buf = xmalloc(block_size);
|
|
u64 src_offset, dst_offset;
|
|
|
|
assert(is_power_of_2(block_size));
|
|
|
|
ranges_roundup(data, block_size);
|
|
ranges_sort_merge(data);
|
|
|
|
/* Write data: */
|
|
darray_for_each(*data, r)
|
|
for (src_offset = r->start;
|
|
src_offset < r->end;
|
|
src_offset += block_size) {
|
|
dst_offset = img.offset;
|
|
img.offset += img.block_size;
|
|
|
|
xpread(infd, buf, block_size, src_offset);
|
|
xpwrite(outfd, buf, block_size, dst_offset,
|
|
"qcow2 data");
|
|
|
|
add_l2(&img, src_offset / block_size, dst_offset);
|
|
}
|
|
|
|
flush_l2(&img);
|
|
|
|
/* Write L1 table: */
|
|
dst_offset = img.offset;
|
|
img.offset += round_up(l1_size * sizeof(u64), block_size);
|
|
xpwrite(img.fd, img.l1_table, l1_size * sizeof(u64), dst_offset,
|
|
"qcow2 l1 table");
|
|
|
|
/* Write header: */
|
|
hdr.magic = cpu_to_be32(QCOW_MAGIC);
|
|
hdr.version = cpu_to_be32(QCOW_VERSION);
|
|
hdr.block_bits = cpu_to_be32(ilog2(block_size));
|
|
hdr.size = cpu_to_be64(image_size);
|
|
hdr.l1_size = cpu_to_be32(l1_size);
|
|
hdr.l1_table_offset = cpu_to_be64(dst_offset);
|
|
|
|
memset(buf, 0, block_size);
|
|
memcpy(buf, &hdr, sizeof(hdr));
|
|
xpwrite(img.fd, buf, block_size, 0,
|
|
"qcow2 header");
|
|
|
|
free(img.l2_table);
|
|
free(img.l1_table);
|
|
free(buf);
|
|
}
|