forked from koverstreet/bcachefs-tools
-
Notifications
You must be signed in to change notification settings - Fork 1
/
qcow2.c
135 lines (109 loc) · 3.06 KB
/
qcow2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#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(NULL, 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),
};
struct range *r;
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);
}