]> git.sesse.net Git - bcachefs-tools-debian/blob - qcow2.c
Add upstream files
[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                 img->offset += img->block_size;
51
52                 memset(img->l2_table, 0, img->block_size);
53                 img->l1_index = -1;
54         }
55 }
56
57 static void add_l2(struct qcow2_image *img, u64 src_blk, u64 dst_offset)
58 {
59         unsigned l2_size = img->block_size / sizeof(u64);
60         u64 l1_index = src_blk / l2_size;
61         u64 l2_index = src_blk & (l2_size - 1);
62
63         if (img->l1_index != l1_index) {
64                 flush_l2(img);
65                 img->l1_index = l1_index;
66         }
67
68         img->l2_table[l2_index] = cpu_to_be64(dst_offset|QCOW_OFLAG_COPIED);
69 }
70
71 void qcow2_write_image(int infd, int outfd, ranges *data,
72                        unsigned block_size)
73 {
74         u64 image_size = get_size(NULL, infd);
75         unsigned l2_size = block_size / sizeof(u64);
76         unsigned l1_size = DIV_ROUND_UP(image_size, (u64) block_size * l2_size);
77         struct qcow2_hdr hdr = { 0 };
78         struct qcow2_image img = {
79                 .fd             = outfd,
80                 .block_size     = block_size,
81                 .l2_table       = xcalloc(l2_size, sizeof(u64)),
82                 .l1_table       = xcalloc(l1_size, sizeof(u64)),
83                 .l1_index       = -1,
84                 .offset         = round_up(sizeof(hdr), block_size),
85         };
86         struct range *r;
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_foreach(r, *data)
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
106                         add_l2(&img, src_offset / block_size, dst_offset);
107                 }
108
109         flush_l2(&img);
110
111         /* Write L1 table: */
112         dst_offset              = img.offset;
113         img.offset              += round_up(l1_size * sizeof(u64), block_size);
114         xpwrite(img.fd, img.l1_table, l1_size * sizeof(u64), dst_offset);
115
116         /* Write header: */
117         hdr.magic               = cpu_to_be32(QCOW_MAGIC);
118         hdr.version             = cpu_to_be32(QCOW_VERSION);
119         hdr.block_bits          = cpu_to_be32(ilog2(block_size));
120         hdr.size                = cpu_to_be64(image_size);
121         hdr.l1_size             = cpu_to_be32(l1_size);
122         hdr.l1_table_offset     = cpu_to_be64(dst_offset);
123
124         memset(buf, 0, block_size);
125         memcpy(buf, &hdr, sizeof(hdr));
126         xpwrite(img.fd, buf, block_size, 0);
127
128         free(img.l2_table);
129         free(img.l1_table);
130         free(buf);
131 }