Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <inttypes.h>
4 : : #include <stdlib.h>
5 : : #include <string.h>
6 : : #include <sys/mman.h>
7 : : #include <sys/types.h>
8 : : #include <sys/stat.h>
9 : : #include <unistd.h>
10 : :
11 : : #if HAVE_XZ
12 : : #include <lzma.h>
13 : : #endif
14 : :
15 : : #if HAVE_LZ4
16 : : #include <lz4.h>
17 : : #include <lz4frame.h>
18 : : #endif
19 : :
20 : : #include "alloc-util.h"
21 : : #include "compress.h"
22 : : #include "fd-util.h"
23 : : #include "io-util.h"
24 : : #include "journal-def.h"
25 : : #include "macro.h"
26 : : #include "sparse-endian.h"
27 : : #include "string-table.h"
28 : : #include "string-util.h"
29 : : #include "unaligned.h"
30 : : #include "util.h"
31 : :
32 : : #if HAVE_LZ4
33 [ + - ]: 4 : DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
34 [ + - ]: 12 : DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
35 : : #endif
36 : :
37 : : #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
38 : :
39 : : static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
40 : : [OBJECT_COMPRESSED_XZ] = "XZ",
41 : : [OBJECT_COMPRESSED_LZ4] = "LZ4",
42 : : };
43 : :
44 [ + + + + ]: 128 : DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
45 : :
46 : 1716 : int compress_blob_xz(const void *src, uint64_t src_size,
47 : : void *dst, size_t dst_alloc_size, size_t *dst_size) {
48 : : #if HAVE_XZ
49 : : static const lzma_options_lzma opt = {
50 : : 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
51 : : LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
52 : : };
53 : : static const lzma_filter filters[] = {
54 : : { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
55 : : { LZMA_VLI_UNKNOWN, NULL }
56 : : };
57 : : lzma_ret ret;
58 : 1716 : size_t out_pos = 0;
59 : :
60 [ - + ]: 1716 : assert(src);
61 [ - + ]: 1716 : assert(src_size > 0);
62 [ - + ]: 1716 : assert(dst);
63 [ - + ]: 1716 : assert(dst_alloc_size > 0);
64 [ - + ]: 1716 : assert(dst_size);
65 : :
66 : : /* Returns < 0 if we couldn't compress the data or the
67 : : * compressed result is longer than the original */
68 : :
69 [ - + ]: 1716 : if (src_size < 80)
70 : 0 : return -ENOBUFS;
71 : :
72 : 1716 : ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
73 : : src, src_size, dst, &out_pos, dst_alloc_size);
74 [ + + ]: 1716 : if (ret != LZMA_OK)
75 : 21 : return -ENOBUFS;
76 : :
77 : 1695 : *dst_size = out_pos;
78 : 1695 : return 0;
79 : : #else
80 : : return -EPROTONOSUPPORT;
81 : : #endif
82 : : }
83 : :
84 : 98819 : int compress_blob_lz4(const void *src, uint64_t src_size,
85 : : void *dst, size_t dst_alloc_size, size_t *dst_size) {
86 : : #if HAVE_LZ4
87 : : int r;
88 : :
89 [ - + ]: 98819 : assert(src);
90 [ - + ]: 98819 : assert(src_size > 0);
91 [ - + ]: 98819 : assert(dst);
92 [ - + ]: 98819 : assert(dst_alloc_size > 0);
93 [ - + ]: 98819 : assert(dst_size);
94 : :
95 : : /* Returns < 0 if we couldn't compress the data or the
96 : : * compressed result is longer than the original */
97 : :
98 [ + + ]: 98819 : if (src_size < 9)
99 : 3 : return -ENOBUFS;
100 : :
101 : 98816 : r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
102 [ + + ]: 98816 : if (r <= 0)
103 : 3200 : return -ENOBUFS;
104 : :
105 : 95616 : unaligned_write_le64(dst, src_size);
106 : 95616 : *dst_size = r + 8;
107 : :
108 : 95616 : return 0;
109 : : #else
110 : : return -EPROTONOSUPPORT;
111 : : #endif
112 : : }
113 : :
114 : 1703 : int decompress_blob_xz(const void *src, uint64_t src_size,
115 : : void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
116 : :
117 : : #if HAVE_XZ
118 : 1703 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
119 : : lzma_ret ret;
120 : : size_t space;
121 : :
122 [ - + ]: 1703 : assert(src);
123 [ - + ]: 1703 : assert(src_size > 0);
124 [ - + ]: 1703 : assert(dst);
125 [ - + ]: 1703 : assert(dst_alloc_size);
126 [ - + ]: 1703 : assert(dst_size);
127 [ + + - + ]: 1703 : assert(*dst_alloc_size == 0 || *dst);
128 : :
129 : 1703 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
130 [ - + ]: 1703 : if (ret != LZMA_OK)
131 : 0 : return -ENOMEM;
132 : :
133 [ - + ]: 1703 : space = MIN(src_size * 2, dst_max ?: (size_t) -1);
134 [ - + ]: 1703 : if (!greedy_realloc(dst, dst_alloc_size, space, 1))
135 : 0 : return -ENOMEM;
136 : :
137 : 1703 : s.next_in = src;
138 : 1703 : s.avail_in = src_size;
139 : :
140 : 1703 : s.next_out = *dst;
141 : 1703 : s.avail_out = space;
142 : :
143 : 16613 : for (;;) {
144 : : size_t used;
145 : :
146 : 18316 : ret = lzma_code(&s, LZMA_FINISH);
147 : :
148 [ + + ]: 18316 : if (ret == LZMA_STREAM_END)
149 : 1679 : break;
150 [ + + ]: 16637 : else if (ret != LZMA_OK)
151 : 24 : return -ENOMEM;
152 : :
153 [ - + # # ]: 16613 : if (dst_max > 0 && (space - s.avail_out) >= dst_max)
154 : : break;
155 [ - + # # ]: 16613 : else if (dst_max > 0 && space == dst_max)
156 : 0 : return -ENOBUFS;
157 : :
158 : 16613 : used = space - s.avail_out;
159 [ - + ]: 16613 : space = MIN(2 * space, dst_max ?: (size_t) -1);
160 [ - + ]: 16613 : if (!greedy_realloc(dst, dst_alloc_size, space, 1))
161 : 0 : return -ENOMEM;
162 : :
163 : 16613 : s.avail_out = space - used;
164 : 16613 : s.next_out = *(uint8_t**)dst + used;
165 : : }
166 : :
167 : 1679 : *dst_size = space - s.avail_out;
168 : 1679 : return 0;
169 : : #else
170 : : return -EPROTONOSUPPORT;
171 : : #endif
172 : : }
173 : :
174 : 95744 : int decompress_blob_lz4(const void *src, uint64_t src_size,
175 : : void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
176 : :
177 : : #if HAVE_LZ4
178 : : char* out;
179 : : int r, size; /* LZ4 uses int for size */
180 : :
181 [ - + ]: 95744 : assert(src);
182 [ - + ]: 95744 : assert(src_size > 0);
183 [ - + ]: 95744 : assert(dst);
184 [ - + ]: 95744 : assert(dst_alloc_size);
185 [ - + ]: 95744 : assert(dst_size);
186 [ + + - + ]: 95744 : assert(*dst_alloc_size == 0 || *dst);
187 : :
188 [ + + ]: 95744 : if (src_size <= 8)
189 : 8 : return -EBADMSG;
190 : :
191 : 95736 : size = unaligned_read_le64(src);
192 [ + - + + ]: 95736 : if (size < 0 || (unsigned) size != unaligned_read_le64(src))
193 : 16 : return -EFBIG;
194 [ + + ]: 95720 : if ((size_t) size > *dst_alloc_size) {
195 : 172 : out = realloc(*dst, size);
196 [ - + ]: 172 : if (!out)
197 : 0 : return -ENOMEM;
198 : 172 : *dst = out;
199 : 172 : *dst_alloc_size = size;
200 : : } else
201 : 95548 : out = *dst;
202 : :
203 : 95720 : r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
204 [ + - - + ]: 95720 : if (r < 0 || r != size)
205 : 0 : return -EBADMSG;
206 : :
207 : 95720 : *dst_size = size;
208 : 95720 : return 0;
209 : : #else
210 : : return -EPROTONOSUPPORT;
211 : : #endif
212 : : }
213 : :
214 : 136 : int decompress_blob(int compression,
215 : : const void *src, uint64_t src_size,
216 : : void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
217 [ - + ]: 136 : if (compression == OBJECT_COMPRESSED_XZ)
218 : 0 : return decompress_blob_xz(src, src_size,
219 : : dst, dst_alloc_size, dst_size, dst_max);
220 [ + - ]: 136 : else if (compression == OBJECT_COMPRESSED_LZ4)
221 : 136 : return decompress_blob_lz4(src, src_size,
222 : : dst, dst_alloc_size, dst_size, dst_max);
223 : : else
224 : 0 : return -EBADMSG;
225 : : }
226 : :
227 : 1064 : int decompress_startswith_xz(const void *src, uint64_t src_size,
228 : : void **buffer, size_t *buffer_size,
229 : : const void *prefix, size_t prefix_len,
230 : : uint8_t extra) {
231 : :
232 : : #if HAVE_XZ
233 : 1064 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
234 : : lzma_ret ret;
235 : :
236 : : /* Checks whether the decompressed blob starts with the
237 : : * mentioned prefix. The byte extra needs to follow the
238 : : * prefix */
239 : :
240 [ - + ]: 1064 : assert(src);
241 [ - + ]: 1064 : assert(src_size > 0);
242 [ - + ]: 1064 : assert(buffer);
243 [ - + ]: 1064 : assert(buffer_size);
244 [ - + ]: 1064 : assert(prefix);
245 [ + + - + ]: 1064 : assert(*buffer_size == 0 || *buffer);
246 : :
247 : 1064 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
248 [ - + ]: 1064 : if (ret != LZMA_OK)
249 : 0 : return -EBADMSG;
250 : :
251 [ - + ]: 1064 : if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
252 : 0 : return -ENOMEM;
253 : :
254 : 1064 : s.next_in = src;
255 : 1064 : s.avail_in = src_size;
256 : :
257 : 1064 : s.next_out = *buffer;
258 : 1064 : s.avail_out = *buffer_size;
259 : :
260 : : for (;;) {
261 : 1064 : ret = lzma_code(&s, LZMA_FINISH);
262 : :
263 [ + - - + ]: 1064 : if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
264 : 0 : return -EBADMSG;
265 : :
266 [ + - ]: 1064 : if (*buffer_size - s.avail_out >= prefix_len + 1)
267 [ + + ]: 2116 : return memcmp(*buffer, prefix, prefix_len) == 0 &&
268 [ + + ]: 1052 : ((const uint8_t*) *buffer)[prefix_len] == extra;
269 : :
270 [ # # ]: 0 : if (ret == LZMA_STREAM_END)
271 : 0 : return 0;
272 : :
273 : 0 : s.avail_out += *buffer_size;
274 : :
275 [ # # ]: 0 : if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
276 : 0 : return -ENOMEM;
277 : :
278 : 0 : s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out;
279 : : }
280 : :
281 : : #else
282 : : return -EPROTONOSUPPORT;
283 : : #endif
284 : : }
285 : :
286 : 1064 : int decompress_startswith_lz4(const void *src, uint64_t src_size,
287 : : void **buffer, size_t *buffer_size,
288 : : const void *prefix, size_t prefix_len,
289 : : uint8_t extra) {
290 : : #if HAVE_LZ4
291 : : /* Checks whether the decompressed blob starts with the
292 : : * mentioned prefix. The byte extra needs to follow the
293 : : * prefix */
294 : :
295 : : int r;
296 : :
297 [ - + ]: 1064 : assert(src);
298 [ - + ]: 1064 : assert(src_size > 0);
299 [ - + ]: 1064 : assert(buffer);
300 [ - + ]: 1064 : assert(buffer_size);
301 [ - + ]: 1064 : assert(prefix);
302 [ + + - + ]: 1064 : assert(*buffer_size == 0 || *buffer);
303 : :
304 [ - + ]: 1064 : if (src_size <= 8)
305 : 0 : return -EBADMSG;
306 : :
307 [ - + ]: 1064 : if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
308 : 0 : return -ENOMEM;
309 : :
310 : 2128 : r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
311 : 1064 : prefix_len + 1, *buffer_size);
312 : : /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where
313 : : * just a part of the buffer is decompressed. But if we get a smaller
314 : : * amount of bytes than requested, we don't know whether there isn't enough
315 : : * data to fill the requested size or whether we just got a partial answer.
316 : : */
317 [ + - - + ]: 1064 : if (r < 0 || (size_t) r < prefix_len + 1) {
318 : : size_t size;
319 : :
320 [ # # ]: 0 : if (LZ4_versionNumber() >= 10803)
321 : : /* We trust that the newer lz4 decompresses the number of bytes we
322 : : * requested if available in the compressed string. */
323 : 0 : return 0;
324 : :
325 [ # # ]: 0 : if (r > 0)
326 : : /* Compare what we have first, in case of mismatch we can
327 : : * shortcut the full comparison. */
328 [ # # ]: 0 : if (memcmp(*buffer, prefix, r) != 0)
329 : 0 : return 0;
330 : :
331 : : /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
332 : : * so in pathological cases might need to decompress the full field. */
333 : 0 : r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
334 [ # # ]: 0 : if (r < 0)
335 : 0 : return r;
336 : :
337 [ # # ]: 0 : if (size < prefix_len + 1)
338 : 0 : return 0;
339 : : }
340 : :
341 [ + + ]: 2116 : return memcmp(*buffer, prefix, prefix_len) == 0 &&
342 [ + + ]: 1052 : ((const uint8_t*) *buffer)[prefix_len] == extra;
343 : : #else
344 : : return -EPROTONOSUPPORT;
345 : : #endif
346 : : }
347 : :
348 : 0 : int decompress_startswith(int compression,
349 : : const void *src, uint64_t src_size,
350 : : void **buffer, size_t *buffer_size,
351 : : const void *prefix, size_t prefix_len,
352 : : uint8_t extra) {
353 [ # # ]: 0 : if (compression == OBJECT_COMPRESSED_XZ)
354 : 0 : return decompress_startswith_xz(src, src_size,
355 : : buffer, buffer_size,
356 : : prefix, prefix_len,
357 : : extra);
358 [ # # ]: 0 : else if (compression == OBJECT_COMPRESSED_LZ4)
359 : 0 : return decompress_startswith_lz4(src, src_size,
360 : : buffer, buffer_size,
361 : : prefix, prefix_len,
362 : : extra);
363 : : else
364 : 0 : return -EBADMSG;
365 : : }
366 : :
367 : 4 : int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
368 : : #if HAVE_XZ
369 : 4 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
370 : : lzma_ret ret;
371 : : uint8_t buf[BUFSIZ], out[BUFSIZ];
372 : 4 : lzma_action action = LZMA_RUN;
373 : :
374 [ - + ]: 4 : assert(fdf >= 0);
375 [ - + ]: 4 : assert(fdt >= 0);
376 : :
377 : 4 : ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
378 [ - + ]: 4 : if (ret != LZMA_OK) {
379 [ # # ]: 0 : log_error("Failed to initialize XZ encoder: code %u", ret);
380 : 0 : return -EINVAL;
381 : : }
382 : :
383 : : for (;;) {
384 [ + - + + ]: 48 : if (s.avail_in == 0 && action == LZMA_RUN) {
385 : 40 : size_t m = sizeof(buf);
386 : : ssize_t n;
387 : :
388 [ - + # # ]: 40 : if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes)
389 : 0 : m = (size_t) max_bytes;
390 : :
391 : 40 : n = read(fdf, buf, m);
392 [ - + ]: 40 : if (n < 0)
393 : 0 : return -errno;
394 [ + + ]: 40 : if (n == 0)
395 : 4 : action = LZMA_FINISH;
396 : : else {
397 : 36 : s.next_in = buf;
398 : 36 : s.avail_in = n;
399 : :
400 [ - + ]: 36 : if (max_bytes != (uint64_t) -1) {
401 [ # # ]: 0 : assert(max_bytes >= (uint64_t) n);
402 : 0 : max_bytes -= n;
403 : : }
404 : : }
405 : : }
406 : :
407 [ + + ]: 48 : if (s.avail_out == 0) {
408 : 12 : s.next_out = out;
409 : 12 : s.avail_out = sizeof(out);
410 : : }
411 : :
412 : 48 : ret = lzma_code(&s, action);
413 [ + - - + ]: 48 : if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
414 [ # # ]: 0 : log_error("Compression failed: code %u", ret);
415 : 0 : return -EBADMSG;
416 : : }
417 : :
418 [ + + + + ]: 48 : if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
419 : : ssize_t n, k;
420 : :
421 : 12 : n = sizeof(out) - s.avail_out;
422 : :
423 : 12 : k = loop_write(fdt, out, n, false);
424 [ - + ]: 12 : if (k < 0)
425 : 0 : return k;
426 : :
427 [ + + ]: 12 : if (ret == LZMA_STREAM_END) {
428 [ + - ]: 4 : log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
429 : : s.total_in, s.total_out,
430 : : (double) s.total_out / s.total_in * 100);
431 : :
432 : 4 : return 0;
433 : : }
434 : : }
435 : : }
436 : : #else
437 : : return -EPROTONOSUPPORT;
438 : : #endif
439 : : }
440 : :
441 : : #define LZ4_BUFSIZE (512*1024u)
442 : :
443 : 4 : int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
444 : :
445 : : #if HAVE_LZ4
446 : : LZ4F_errorCode_t c;
447 : 4 : _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
448 : 4 : _cleanup_free_ char *buf = NULL;
449 : 4 : char *src = NULL;
450 : 4 : size_t size, n, total_in = 0, total_out, offset = 0, frame_size;
451 : : struct stat st;
452 : : int r;
453 : : static const LZ4F_compressOptions_t options = {
454 : : .stableSrc = 1,
455 : : };
456 : : static const LZ4F_preferences_t preferences = {
457 : : .frameInfo.blockSizeID = 5,
458 : : };
459 : :
460 : 4 : c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
461 [ - + ]: 4 : if (LZ4F_isError(c))
462 : 0 : return -ENOMEM;
463 : :
464 [ - + ]: 4 : if (fstat(fdf, &st) < 0)
465 [ # # ]: 0 : return log_debug_errno(errno, "fstat() failed: %m");
466 : :
467 : 4 : frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
468 : 4 : size = frame_size + 64*1024; /* add some space for header and trailer */
469 : 4 : buf = malloc(size);
470 [ - + ]: 4 : if (!buf)
471 : 0 : return -ENOMEM;
472 : :
473 : 4 : n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
474 [ - + ]: 4 : if (LZ4F_isError(n))
475 : 0 : return -EINVAL;
476 : :
477 : 4 : src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
478 [ - + ]: 4 : if (src == MAP_FAILED)
479 : 0 : return -errno;
480 : :
481 [ + - ]: 4 : log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n);
482 : :
483 [ + + ]: 8 : while (total_in < (size_t) st.st_size) {
484 : : ssize_t k;
485 : :
486 : 4 : k = MIN(LZ4_BUFSIZE, st.st_size - total_in);
487 : 8 : n = LZ4F_compressUpdate(ctx, buf + offset, size - offset,
488 : 4 : src + total_in, k, &options);
489 [ - + ]: 4 : if (LZ4F_isError(n)) {
490 : 0 : r = -ENOTRECOVERABLE;
491 : 0 : goto cleanup;
492 : : }
493 : :
494 : 4 : total_in += k;
495 : 4 : offset += n;
496 : 4 : total_out += n;
497 : :
498 [ - + # # ]: 4 : if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
499 [ # # ]: 0 : log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes);
500 : 0 : return -EFBIG;
501 : : }
502 : :
503 [ - + ]: 4 : if (size - offset < frame_size + 4) {
504 : 0 : k = loop_write(fdt, buf, offset, false);
505 [ # # ]: 0 : if (k < 0) {
506 : 0 : r = k;
507 : 0 : goto cleanup;
508 : : }
509 : 0 : offset = 0;
510 : : }
511 : : }
512 : :
513 : 4 : n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options);
514 [ - + ]: 4 : if (LZ4F_isError(n)) {
515 : 0 : r = -ENOTRECOVERABLE;
516 : 0 : goto cleanup;
517 : : }
518 : :
519 : 4 : offset += n;
520 : 4 : total_out += n;
521 : 4 : r = loop_write(fdt, buf, offset, false);
522 [ - + ]: 4 : if (r < 0)
523 : 0 : goto cleanup;
524 : :
525 [ + - ]: 4 : log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
526 : : total_in, total_out,
527 : : (double) total_out / total_in * 100);
528 : 4 : cleanup:
529 : 4 : munmap(src, st.st_size);
530 : 4 : return r;
531 : : #else
532 : : return -EPROTONOSUPPORT;
533 : : #endif
534 : : }
535 : :
536 : 12 : int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
537 : :
538 : : #if HAVE_XZ
539 : 12 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
540 : : lzma_ret ret;
541 : :
542 : : uint8_t buf[BUFSIZ], out[BUFSIZ];
543 : 12 : lzma_action action = LZMA_RUN;
544 : :
545 [ - + ]: 12 : assert(fdf >= 0);
546 [ - + ]: 12 : assert(fdt >= 0);
547 : :
548 : 12 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
549 [ - + ]: 12 : if (ret != LZMA_OK) {
550 [ # # ]: 0 : log_debug("Failed to initialize XZ decoder: code %u", ret);
551 : 0 : return -ENOMEM;
552 : : }
553 : :
554 : : for (;;) {
555 [ + + + - ]: 92 : if (s.avail_in == 0 && action == LZMA_RUN) {
556 : : ssize_t n;
557 : :
558 : 28 : n = read(fdf, buf, sizeof(buf));
559 [ - + ]: 28 : if (n < 0)
560 : 0 : return -errno;
561 [ - + ]: 28 : if (n == 0)
562 : 0 : action = LZMA_FINISH;
563 : : else {
564 : 28 : s.next_in = buf;
565 : 28 : s.avail_in = n;
566 : : }
567 : : }
568 : :
569 [ + + ]: 92 : if (s.avail_out == 0) {
570 : 76 : s.next_out = out;
571 : 76 : s.avail_out = sizeof(out);
572 : : }
573 : :
574 : 92 : ret = lzma_code(&s, action);
575 [ + + + + ]: 92 : if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
576 [ + - ]: 4 : log_debug("Decompression failed: code %u", ret);
577 : 4 : return -EBADMSG;
578 : : }
579 : :
580 [ + + + + ]: 88 : if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
581 : : ssize_t n, k;
582 : :
583 : 72 : n = sizeof(out) - s.avail_out;
584 : :
585 [ + - ]: 72 : if (max_bytes != (uint64_t) -1) {
586 [ + + ]: 72 : if (max_bytes < (uint64_t) n)
587 : 4 : return -EFBIG;
588 : :
589 : 68 : max_bytes -= n;
590 : : }
591 : :
592 : 68 : k = loop_write(fdt, out, n, false);
593 [ - + ]: 68 : if (k < 0)
594 : 0 : return k;
595 : :
596 [ + + ]: 68 : if (ret == LZMA_STREAM_END) {
597 [ + - ]: 4 : log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
598 : : s.total_in, s.total_out,
599 : : (double) s.total_out / s.total_in * 100);
600 : :
601 : 4 : return 0;
602 : : }
603 : : }
604 : : }
605 : : #else
606 : : log_debug("Cannot decompress file. Compiled without XZ support.");
607 : : return -EPROTONOSUPPORT;
608 : : #endif
609 : : }
610 : :
611 : 12 : int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
612 : : #if HAVE_LZ4
613 : : size_t c;
614 : 12 : _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
615 : 12 : _cleanup_free_ char *buf = NULL;
616 : : char *src;
617 : : struct stat st;
618 : 12 : int r = 0;
619 : 12 : size_t total_in = 0, total_out = 0;
620 : :
621 : 12 : c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
622 [ - + ]: 12 : if (LZ4F_isError(c))
623 : 0 : return -ENOMEM;
624 : :
625 [ - + ]: 12 : if (fstat(in, &st) < 0)
626 [ # # ]: 0 : return log_debug_errno(errno, "fstat() failed: %m");
627 : :
628 : 12 : buf = malloc(LZ4_BUFSIZE);
629 [ - + ]: 12 : if (!buf)
630 : 0 : return -ENOMEM;
631 : :
632 : 12 : src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
633 [ - + ]: 12 : if (src == MAP_FAILED)
634 : 0 : return -errno;
635 : :
636 [ + + ]: 20 : while (total_in < (size_t) st.st_size) {
637 : 12 : size_t produced = LZ4_BUFSIZE;
638 : 12 : size_t used = st.st_size - total_in;
639 : :
640 : 12 : c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
641 [ - + ]: 12 : if (LZ4F_isError(c)) {
642 : 0 : r = -EBADMSG;
643 : 4 : goto cleanup;
644 : : }
645 : :
646 : 12 : total_in += used;
647 : 12 : total_out += produced;
648 : :
649 [ + - + + ]: 12 : if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
650 [ + - ]: 4 : log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
651 : 4 : r = -EFBIG;
652 : 4 : goto cleanup;
653 : : }
654 : :
655 : 8 : r = loop_write(out, buf, produced, false);
656 [ - + ]: 8 : if (r < 0)
657 : 0 : goto cleanup;
658 : : }
659 : :
660 [ + - + - ]: 8 : log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
661 : : total_in, total_out,
662 : : total_in > 0 ? (double) total_out / total_in * 100 : 0.0);
663 : 12 : cleanup:
664 : 12 : munmap(src, st.st_size);
665 : 12 : return r;
666 : : #else
667 : : log_debug("Cannot decompress file. Compiled without LZ4 support.");
668 : : return -EPROTONOSUPPORT;
669 : : #endif
670 : : }
671 : :
672 : 0 : int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
673 : :
674 [ # # ]: 0 : if (endswith(filename, ".lz4"))
675 : 0 : return decompress_stream_lz4(fdf, fdt, max_bytes);
676 [ # # ]: 0 : else if (endswith(filename, ".xz"))
677 : 0 : return decompress_stream_xz(fdf, fdt, max_bytes);
678 : : else
679 : 0 : return -EPROTONOSUPPORT;
680 : : }
|