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