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