]> git.sesse.net Git - bcachefs-tools-debian/blob - qcow2.c
Update bcachefs sources to 5264e9f4d0c0 bcachefs: fix setting version_upgrade_complete
[bcachefs-tools-debian] / qcow2.c
1
2 #include <errno.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5
6 #include "qcow2.h"
7 #include "tools-util.h"
8
9 #define QCOW_MAGIC              (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
10 #define QCOW_VERSION            2
11 #define QCOW_OFLAG_COPIED       (1LL << 63)
12
13 struct qcow2_hdr {
14         u32                     magic;
15         u32                     version;
16
17         u64                     backing_file_offset;
18         u32                     backing_file_size;
19
20         u32                     block_bits;
21         u64                     size;
22         u32                     crypt_method;
23
24         u32                     l1_size;
25         u64                     l1_table_offset;
26
27         u64                     refcount_table_offset;
28         u32                     refcount_table_blocks;
29
30         u32                     nb_snapshots;
31         u64                     snapshots_offset;
32 };
33
34 struct qcow2_image {
35         int                     fd;
36         u32                     block_size;
37         u64                     *l1_table;
38         u64                     l1_offset;
39         u32                     l1_index;
40         u64                     *l2_table;
41         u64                     offset;
42 };
43
44 static void flush_l2(struct qcow2_image *img)
45 {
46         if (img->l1_index != -1) {
47                 img->l1_table[img->l1_index] =
48                         cpu_to_be64(img->offset|QCOW_OFLAG_COPIED);
49                 xpwrite(img->fd, img->l2_table, img->block_size, img->offset,
50                         "qcow2 l2 table");
51                 img->offset += img->block_size;
52
53                 memset(img->l2_table, 0, img->block_size);
54                 img->l1_index = -1;
55         }
56 }
57
58 static void add_l2(struct qcow2_image *img, u64 src_blk, u64 dst_offset)
59 {
60         unsigned l2_size = img->block_size / sizeof(u64);
61         u64 l1_index = src_blk / l2_size;
62         u64 l2_index = src_blk & (l2_size - 1);
63
64         if (img->l1_index != l1_index) {
65                 flush_l2(img);
66                 img->l1_index = l1_index;
67         }
68
69         img->l2_table[l2_index] = cpu_to_be64(dst_offset|QCOW_OFLAG_COPIED);
70 }
71
72 void qcow2_write_image(int infd, int outfd, ranges *data,
73                        unsigned block_size)
74 {
75         u64 image_size = get_size(infd);
76         unsigned l2_size = block_size / sizeof(u64);
77         unsigned l1_size = DIV_ROUND_UP(image_size, (u64) block_size * l2_size);
78         struct qcow2_hdr hdr = { 0 };
79         struct qcow2_image img = {
80                 .fd             = outfd,
81                 .block_size     = block_size,
82                 .l2_table       = xcalloc(l2_size, sizeof(u64)),
83                 .l1_table       = xcalloc(l1_size, sizeof(u64)),
84                 .l1_index       = -1,
85                 .offset         = round_up(sizeof(hdr), block_size),
86         };
87         char *buf = xmalloc(block_size);
88         u64 src_offset, dst_offset;
89
90         assert(is_power_of_2(block_size));
91
92         ranges_roundup(data, block_size);
93         ranges_sort_merge(data);
94
95         /* Write data: */
96         darray_for_each(*data, r)
97                 for (src_offset = r->start;
98                      src_offset < r->end;
99                      src_offset += block_size) {
100                         dst_offset = img.offset;
101                         img.offset += img.block_size;
102
103                         xpread(infd, buf, block_size, src_offset);
104                         xpwrite(outfd, buf, block_size, dst_offset,
105                                 "qcow2 data");
106
107                         add_l2(&img, src_offset / block_size, dst_offset);
108                 }
109
110         flush_l2(&img);
111
112         /* Write L1 table: */
113         dst_offset              = img.offset;
114         img.offset              += round_up(l1_size * sizeof(u64), block_size);
115         xpwrite(img.fd, img.l1_table, l1_size * sizeof(u64), dst_offset,
116                 "qcow2 l1 table");
117
118         /* Write header: */
119         hdr.magic               = cpu_to_be32(QCOW_MAGIC);
120         hdr.version             = cpu_to_be32(QCOW_VERSION);
121         hdr.block_bits          = cpu_to_be32(ilog2(block_size));
122         hdr.size                = cpu_to_be64(image_size);
123         hdr.l1_size             = cpu_to_be32(l1_size);
124         hdr.l1_table_offset     = cpu_to_be64(dst_offset);
125
126         memset(buf, 0, block_size);
127         memcpy(buf, &hdr, sizeof(hdr));
128         xpwrite(img.fd, buf, block_size, 0,
129                 "qcow2 header");
130
131         free(img.l2_table);
132         free(img.l1_table);
133         free(buf);
134 }