]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/qcow2-util.c
3fab08020e4e5770bf48f0a2bfe22a7d9ee0a94f
[thirdparty/systemd.git] / src / import / qcow2-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6 ***/
7
8 #include <zlib.h>
9
10 #include "alloc-util.h"
11 #include "btrfs-util.h"
12 #include "qcow2-util.h"
13 #include "sparse-endian.h"
14 #include "util.h"
15
16 #define QCOW2_MAGIC 0x514649fb
17
18 #define QCOW2_COPIED (1ULL << 63)
19 #define QCOW2_COMPRESSED (1ULL << 62)
20 #define QCOW2_ZERO (1ULL << 0)
21
22 typedef struct _packed_ Header {
23 be32_t magic;
24 be32_t version;
25
26 be64_t backing_file_offset;
27 be32_t backing_file_size;
28
29 be32_t cluster_bits;
30 be64_t size;
31 be32_t crypt_method;
32
33 be32_t l1_size;
34 be64_t l1_table_offset;
35
36 be64_t refcount_table_offset;
37 be32_t refcount_table_clusters;
38
39 be32_t nb_snapshots;
40 be64_t snapshots_offset;
41
42 /* The remainder is only present on QCOW3 */
43 be64_t incompatible_features;
44 be64_t compatible_features;
45 be64_t autoclear_features;
46
47 be32_t refcount_order;
48 be32_t header_length;
49 } Header;
50
51 #define HEADER_MAGIC(header) be32toh((header)->magic)
52 #define HEADER_VERSION(header) be32toh((header)->version)
53 #define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits)
54 #define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header))
55 #define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3)
56 #define HEADER_SIZE(header) be64toh((header)->size)
57 #define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method)
58 #define HEADER_L1_SIZE(header) be32toh((header)->l1_size)
59 #define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t))
60 #define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset)
61
62 static uint32_t HEADER_HEADER_LENGTH(const Header *h) {
63 if (HEADER_VERSION(h) < 3)
64 return offsetof(Header, incompatible_features);
65
66 return be32toh(h->header_length);
67 }
68
69 static int copy_cluster(
70 int sfd, uint64_t soffset,
71 int dfd, uint64_t doffset,
72 uint64_t cluster_size,
73 void *buffer) {
74
75 ssize_t l;
76 int r;
77
78 r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size);
79 if (r >= 0)
80 return r;
81
82 l = pread(sfd, buffer, cluster_size, soffset);
83 if (l < 0)
84 return -errno;
85 if ((uint64_t) l != cluster_size)
86 return -EIO;
87
88 l = pwrite(dfd, buffer, cluster_size, doffset);
89 if (l < 0)
90 return -errno;
91 if ((uint64_t) l != cluster_size)
92 return -EIO;
93
94 return 0;
95 }
96
97 static int decompress_cluster(
98 int sfd, uint64_t soffset,
99 int dfd, uint64_t doffset,
100 uint64_t compressed_size,
101 uint64_t cluster_size,
102 void *buffer1,
103 void *buffer2) {
104
105 _cleanup_free_ void *large_buffer = NULL;
106 z_stream s = {};
107 uint64_t sz;
108 ssize_t l;
109 int r;
110
111 if (compressed_size > cluster_size) {
112 /* The usual cluster buffer doesn't suffice, let's
113 * allocate a larger one, temporarily */
114
115 large_buffer = malloc(compressed_size);
116 if (!large_buffer)
117 return -ENOMEM;
118
119 buffer1 = large_buffer;
120 }
121
122 l = pread(sfd, buffer1, compressed_size, soffset);
123 if (l < 0)
124 return -errno;
125 if ((uint64_t) l != compressed_size)
126 return -EIO;
127
128 s.next_in = buffer1;
129 s.avail_in = compressed_size;
130 s.next_out = buffer2;
131 s.avail_out = cluster_size;
132
133 r = inflateInit2(&s, -12);
134 if (r != Z_OK)
135 return -EIO;
136
137 r = inflate(&s, Z_FINISH);
138 sz = (uint8_t*) s.next_out - (uint8_t*) buffer2;
139 inflateEnd(&s);
140 if (r != Z_STREAM_END || sz != cluster_size)
141 return -EIO;
142
143 l = pwrite(dfd, buffer2, cluster_size, doffset);
144 if (l < 0)
145 return -errno;
146 if ((uint64_t) l != cluster_size)
147 return -EIO;
148
149 return 0;
150 }
151
152 static int normalize_offset(
153 const Header *header,
154 uint64_t p,
155 uint64_t *ret,
156 bool *compressed,
157 uint64_t *compressed_size) {
158
159 uint64_t q;
160
161 q = be64toh(p);
162
163 if (q & QCOW2_COMPRESSED) {
164 uint64_t sz, csize_shift, csize_mask;
165
166 if (!compressed)
167 return -EOPNOTSUPP;
168
169 csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8);
170 csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1;
171 sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511);
172 q &= ((1ULL << csize_shift) - 1);
173
174 if (compressed_size)
175 *compressed_size = sz;
176
177 *compressed = true;
178
179 } else {
180 if (compressed) {
181 *compressed = false;
182 *compressed_size = 0;
183 }
184
185 if (q & QCOW2_ZERO) {
186 /* We make no distinction between zero blocks and holes */
187 *ret = 0;
188 return 0;
189 }
190
191 q &= ~QCOW2_COPIED;
192 }
193
194 *ret = q;
195 return q > 0; /* returns positive if not a hole */
196 }
197
198 static int verify_header(const Header *header) {
199 assert(header);
200
201 if (HEADER_MAGIC(header) != QCOW2_MAGIC)
202 return -EBADMSG;
203
204 if (!IN_SET(HEADER_VERSION(header), 2, 3))
205 return -EOPNOTSUPP;
206
207 if (HEADER_CRYPT_METHOD(header) != 0)
208 return -EOPNOTSUPP;
209
210 if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */
211 return -EBADMSG;
212
213 if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */
214 return -EBADMSG;
215
216 if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0)
217 return -EBADMSG;
218
219 if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */
220 return -EBADMSG;
221
222 if (HEADER_VERSION(header) == 3) {
223
224 if (header->incompatible_features != 0)
225 return -EOPNOTSUPP;
226
227 if (HEADER_HEADER_LENGTH(header) < sizeof(Header))
228 return -EBADMSG;
229 }
230
231 return 0;
232 }
233
234 int qcow2_convert(int qcow2_fd, int raw_fd) {
235 _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL;
236 _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL;
237 uint64_t sz, i;
238 Header header;
239 ssize_t l;
240 int r;
241
242 l = pread(qcow2_fd, &header, sizeof(header), 0);
243 if (l < 0)
244 return -errno;
245 if (l != sizeof(header))
246 return -EIO;
247
248 r = verify_header(&header);
249 if (r < 0)
250 return r;
251
252 l1_table = new(be64_t, HEADER_L1_SIZE(&header));
253 if (!l1_table)
254 return -ENOMEM;
255
256 l2_table = malloc(HEADER_CLUSTER_SIZE(&header));
257 if (!l2_table)
258 return -ENOMEM;
259
260 buffer1 = malloc(HEADER_CLUSTER_SIZE(&header));
261 if (!buffer1)
262 return -ENOMEM;
263
264 buffer2 = malloc(HEADER_CLUSTER_SIZE(&header));
265 if (!buffer2)
266 return -ENOMEM;
267
268 /* Empty the file if it exists, we rely on zero bits */
269 if (ftruncate(raw_fd, 0) < 0)
270 return -errno;
271
272 if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0)
273 return -errno;
274
275 sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header);
276 l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header));
277 if (l < 0)
278 return -errno;
279 if ((uint64_t) l != sz)
280 return -EIO;
281
282 for (i = 0; i < HEADER_L1_SIZE(&header); i ++) {
283 uint64_t l2_begin, j;
284
285 r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL);
286 if (r < 0)
287 return r;
288 if (r == 0)
289 continue;
290
291 l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin);
292 if (l < 0)
293 return -errno;
294 if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header))
295 return -EIO;
296
297 for (j = 0; j < HEADER_L2_SIZE(&header); j++) {
298 uint64_t data_begin, p, compressed_size;
299 bool compressed;
300
301 p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header);
302
303 r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size);
304 if (r < 0)
305 return r;
306 if (r == 0)
307 continue;
308
309 if (compressed)
310 r = decompress_cluster(
311 qcow2_fd, data_begin,
312 raw_fd, p,
313 compressed_size, HEADER_CLUSTER_SIZE(&header),
314 buffer1, buffer2);
315 else
316 r = copy_cluster(
317 qcow2_fd, data_begin,
318 raw_fd, p,
319 HEADER_CLUSTER_SIZE(&header), buffer1);
320 if (r < 0)
321 return r;
322 }
323 }
324
325 return 0;
326 }
327
328 int qcow2_detect(int fd) {
329 be32_t id;
330 ssize_t l;
331
332 l = pread(fd, &id, sizeof(id), 0);
333 if (l < 0)
334 return -errno;
335 if (l != sizeof(id))
336 return -EIO;
337
338 return htobe32(QCOW2_MAGIC) == id;
339 }