| File: | build-scan/../src/import/qcow2-util.c |
| Warning: | line 261, column 25 Potential leak of memory pointed to by 'buffer1' |
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 | } |