LCOV - code coverage report
Current view: top level - journal - compress.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 247 327 75.5 %
Date: 2019-08-22 15:41:25 Functions: 15 17 88.2 %

          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           1 : DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
      34           3 : 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          32 : DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
      45             : 
      46          15 : 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          15 :         size_t out_pos = 0;
      59             : 
      60          15 :         assert(src);
      61          15 :         assert(src_size > 0);
      62          15 :         assert(dst);
      63          15 :         assert(dst_alloc_size > 0);
      64          15 :         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          15 :         if (src_size < 80)
      70           0 :                 return -ENOBUFS;
      71             : 
      72          15 :         ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
      73             :                                         src, src_size, dst, &out_pos, dst_alloc_size);
      74          15 :         if (ret != LZMA_OK)
      75           3 :                 return -ENOBUFS;
      76             : 
      77          12 :         *dst_size = out_pos;
      78          12 :         return 0;
      79             : #else
      80             :         return -EPROTONOSUPPORT;
      81             : #endif
      82             : }
      83             : 
      84         274 : 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         274 :         assert(src);
      90         274 :         assert(src_size > 0);
      91         274 :         assert(dst);
      92         274 :         assert(dst_alloc_size > 0);
      93         274 :         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         274 :         if (src_size < 9)
      99           0 :                 return -ENOBUFS;
     100             : 
     101         274 :         r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
     102         274 :         if (r <= 0)
     103          12 :                 return -ENOBUFS;
     104             : 
     105         262 :         unaligned_write_le64(dst, src_size);
     106         262 :         *dst_size = r + 8;
     107             : 
     108         262 :         return 0;
     109             : #else
     110             :         return -EPROTONOSUPPORT;
     111             : #endif
     112             : }
     113             : 
     114          14 : 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          14 :         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
     119             :         lzma_ret ret;
     120             :         size_t space;
     121             : 
     122          14 :         assert(src);
     123          14 :         assert(src_size > 0);
     124          14 :         assert(dst);
     125          14 :         assert(dst_alloc_size);
     126          14 :         assert(dst_size);
     127          14 :         assert(*dst_alloc_size == 0 || *dst);
     128             : 
     129          14 :         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
     130          14 :         if (ret != LZMA_OK)
     131           0 :                 return -ENOMEM;
     132             : 
     133          14 :         space = MIN(src_size * 2, dst_max ?: (size_t) -1);
     134          14 :         if (!greedy_realloc(dst, dst_alloc_size, space, 1))
     135           0 :                 return -ENOMEM;
     136             : 
     137          14 :         s.next_in = src;
     138          14 :         s.avail_in = src_size;
     139             : 
     140          14 :         s.next_out = *dst;
     141          14 :         s.avail_out = space;
     142             : 
     143          73 :         for (;;) {
     144             :                 size_t used;
     145             : 
     146          87 :                 ret = lzma_code(&s, LZMA_FINISH);
     147             : 
     148          87 :                 if (ret == LZMA_STREAM_END)
     149           8 :                         break;
     150          79 :                 else if (ret != LZMA_OK)
     151           6 :                         return -ENOMEM;
     152             : 
     153          73 :                 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
     154             :                         break;
     155          73 :                 else if (dst_max > 0 && space == dst_max)
     156           0 :                         return -ENOBUFS;
     157             : 
     158          73 :                 used = space - s.avail_out;
     159          73 :                 space = MIN(2 * space, dst_max ?: (size_t) -1);
     160          73 :                 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
     161           0 :                         return -ENOMEM;
     162             : 
     163          73 :                 s.avail_out = space - used;
     164          73 :                 s.next_out = *(uint8_t**)dst + used;
     165             :         }
     166             : 
     167           8 :         *dst_size = space - s.avail_out;
     168           8 :         return 0;
     169             : #else
     170             :         return -EPROTONOSUPPORT;
     171             : #endif
     172             : }
     173             : 
     174         294 : 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         294 :         assert(src);
     182         294 :         assert(src_size > 0);
     183         294 :         assert(dst);
     184         294 :         assert(dst_alloc_size);
     185         294 :         assert(dst_size);
     186         294 :         assert(*dst_alloc_size == 0 || *dst);
     187             : 
     188         294 :         if (src_size <= 8)
     189           2 :                 return -EBADMSG;
     190             : 
     191         292 :         size = unaligned_read_le64(src);
     192         292 :         if (size < 0 || (unsigned) size != unaligned_read_le64(src))
     193           4 :                 return -EFBIG;
     194         288 :         if ((size_t) size > *dst_alloc_size) {
     195          17 :                 out = realloc(*dst, size);
     196          17 :                 if (!out)
     197           0 :                         return -ENOMEM;
     198          17 :                 *dst = out;
     199          17 :                 *dst_alloc_size = size;
     200             :         } else
     201         271 :                 out = *dst;
     202             : 
     203         288 :         r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
     204         288 :         if (r < 0 || r != size)
     205           0 :                 return -EBADMSG;
     206             : 
     207         288 :         *dst_size = size;
     208         288 :         return 0;
     209             : #else
     210             :         return -EPROTONOSUPPORT;
     211             : #endif
     212             : }
     213             : 
     214          34 : 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          34 :         if (compression == OBJECT_COMPRESSED_XZ)
     218           0 :                 return decompress_blob_xz(src, src_size,
     219             :                                           dst, dst_alloc_size, dst_size, dst_max);
     220          34 :         else if (compression == OBJECT_COMPRESSED_LZ4)
     221          34 :                 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         266 : 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         266 :         _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         266 :         assert(src);
     241         266 :         assert(src_size > 0);
     242         266 :         assert(buffer);
     243         266 :         assert(buffer_size);
     244         266 :         assert(prefix);
     245         266 :         assert(*buffer_size == 0 || *buffer);
     246             : 
     247         266 :         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
     248         266 :         if (ret != LZMA_OK)
     249           0 :                 return -EBADMSG;
     250             : 
     251         266 :         if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
     252           0 :                 return -ENOMEM;
     253             : 
     254         266 :         s.next_in = src;
     255         266 :         s.avail_in = src_size;
     256             : 
     257         266 :         s.next_out = *buffer;
     258         266 :         s.avail_out = *buffer_size;
     259             : 
     260             :         for (;;) {
     261         266 :                 ret = lzma_code(&s, LZMA_FINISH);
     262             : 
     263         266 :                 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
     264           0 :                         return -EBADMSG;
     265             : 
     266         266 :                 if (*buffer_size - s.avail_out >= prefix_len + 1)
     267         529 :                         return memcmp(*buffer, prefix, prefix_len) == 0 &&
     268         263 :                                 ((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         266 : 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         266 :         assert(src);
     298         266 :         assert(src_size > 0);
     299         266 :         assert(buffer);
     300         266 :         assert(buffer_size);
     301         266 :         assert(prefix);
     302         266 :         assert(*buffer_size == 0 || *buffer);
     303             : 
     304         266 :         if (src_size <= 8)
     305           0 :                 return -EBADMSG;
     306             : 
     307         266 :         if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
     308           0 :                 return -ENOMEM;
     309             : 
     310         532 :         r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
     311         266 :                                         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         266 :         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         529 :         return memcmp(*buffer, prefix, prefix_len) == 0 &&
     342         263 :                 ((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           1 : int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
     368             : #if HAVE_XZ
     369           1 :         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
     370             :         lzma_ret ret;
     371             :         uint8_t buf[BUFSIZ], out[BUFSIZ];
     372           1 :         lzma_action action = LZMA_RUN;
     373             : 
     374           1 :         assert(fdf >= 0);
     375           1 :         assert(fdt >= 0);
     376             : 
     377           1 :         ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
     378           1 :         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          12 :                 if (s.avail_in == 0 && action == LZMA_RUN) {
     385          10 :                         size_t m = sizeof(buf);
     386             :                         ssize_t n;
     387             : 
     388          10 :                         if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes)
     389           0 :                                 m = (size_t) max_bytes;
     390             : 
     391          10 :                         n = read(fdf, buf, m);
     392          10 :                         if (n < 0)
     393           0 :                                 return -errno;
     394          10 :                         if (n == 0)
     395           1 :                                 action = LZMA_FINISH;
     396             :                         else {
     397           9 :                                 s.next_in = buf;
     398           9 :                                 s.avail_in = n;
     399             : 
     400           9 :                                 if (max_bytes != (uint64_t) -1) {
     401           0 :                                         assert(max_bytes >= (uint64_t) n);
     402           0 :                                         max_bytes -= n;
     403             :                                 }
     404             :                         }
     405             :                 }
     406             : 
     407          12 :                 if (s.avail_out == 0) {
     408           3 :                         s.next_out = out;
     409           3 :                         s.avail_out = sizeof(out);
     410             :                 }
     411             : 
     412          12 :                 ret = lzma_code(&s, action);
     413          12 :                 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          12 :                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
     419             :                         ssize_t n, k;
     420             : 
     421           3 :                         n = sizeof(out) - s.avail_out;
     422             : 
     423           3 :                         k = loop_write(fdt, out, n, false);
     424           3 :                         if (k < 0)
     425           0 :                                 return k;
     426             : 
     427           3 :                         if (ret == LZMA_STREAM_END) {
     428           1 :                                 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           1 :                                 return 0;
     433             :                         }
     434             :                 }
     435             :         }
     436             : #else
     437             :         return -EPROTONOSUPPORT;
     438             : #endif
     439             : }
     440             : 
     441             : #define LZ4_BUFSIZE (512*1024u)
     442             : 
     443           1 : int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
     444             : 
     445             : #if HAVE_LZ4
     446             :         LZ4F_errorCode_t c;
     447           1 :         _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
     448           1 :         _cleanup_free_ char *buf = NULL;
     449           1 :         char *src = NULL;
     450           1 :         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           1 :         c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
     461           1 :         if (LZ4F_isError(c))
     462           0 :                 return -ENOMEM;
     463             : 
     464           1 :         if (fstat(fdf, &st) < 0)
     465           0 :                 return log_debug_errno(errno, "fstat() failed: %m");
     466             : 
     467           1 :         frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
     468           1 :         size =  frame_size + 64*1024; /* add some space for header and trailer */
     469           1 :         buf = malloc(size);
     470           1 :         if (!buf)
     471           0 :                 return -ENOMEM;
     472             : 
     473           1 :         n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
     474           1 :         if (LZ4F_isError(n))
     475           0 :                 return -EINVAL;
     476             : 
     477           1 :         src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
     478           1 :         if (src == MAP_FAILED)
     479           0 :                 return -errno;
     480             : 
     481           1 :         log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n);
     482             : 
     483           2 :         while (total_in < (size_t) st.st_size) {
     484             :                 ssize_t k;
     485             : 
     486           1 :                 k = MIN(LZ4_BUFSIZE, st.st_size - total_in);
     487           2 :                 n = LZ4F_compressUpdate(ctx, buf + offset, size - offset,
     488           1 :                                         src + total_in, k, &options);
     489           1 :                 if (LZ4F_isError(n)) {
     490           0 :                         r = -ENOTRECOVERABLE;
     491           0 :                         goto cleanup;
     492             :                 }
     493             : 
     494           1 :                 total_in += k;
     495           1 :                 offset += n;
     496           1 :                 total_out += n;
     497             : 
     498           1 :                 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           1 :                 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           1 :         n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options);
     514           1 :         if (LZ4F_isError(n)) {
     515           0 :                 r = -ENOTRECOVERABLE;
     516           0 :                 goto cleanup;
     517             :         }
     518             : 
     519           1 :         offset += n;
     520           1 :         total_out += n;
     521           1 :         r = loop_write(fdt, buf, offset, false);
     522           1 :         if (r < 0)
     523           0 :                 goto cleanup;
     524             : 
     525           1 :         log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
     526             :                   total_in, total_out,
     527             :                   (double) total_out / total_in * 100);
     528           1 :  cleanup:
     529           1 :         munmap(src, st.st_size);
     530           1 :         return r;
     531             : #else
     532             :         return -EPROTONOSUPPORT;
     533             : #endif
     534             : }
     535             : 
     536           3 : int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
     537             : 
     538             : #if HAVE_XZ
     539           3 :         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
     540             :         lzma_ret ret;
     541             : 
     542             :         uint8_t buf[BUFSIZ], out[BUFSIZ];
     543           3 :         lzma_action action = LZMA_RUN;
     544             : 
     545           3 :         assert(fdf >= 0);
     546           3 :         assert(fdt >= 0);
     547             : 
     548           3 :         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
     549           3 :         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          23 :                 if (s.avail_in == 0 && action == LZMA_RUN) {
     556             :                         ssize_t n;
     557             : 
     558           7 :                         n = read(fdf, buf, sizeof(buf));
     559           7 :                         if (n < 0)
     560           0 :                                 return -errno;
     561           7 :                         if (n == 0)
     562           0 :                                 action = LZMA_FINISH;
     563             :                         else {
     564           7 :                                 s.next_in = buf;
     565           7 :                                 s.avail_in = n;
     566             :                         }
     567             :                 }
     568             : 
     569          23 :                 if (s.avail_out == 0) {
     570          19 :                         s.next_out = out;
     571          19 :                         s.avail_out = sizeof(out);
     572             :                 }
     573             : 
     574          23 :                 ret = lzma_code(&s, action);
     575          23 :                 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
     576           1 :                         log_debug("Decompression failed: code %u", ret);
     577           1 :                         return -EBADMSG;
     578             :                 }
     579             : 
     580          22 :                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
     581             :                         ssize_t n, k;
     582             : 
     583          18 :                         n = sizeof(out) - s.avail_out;
     584             : 
     585          18 :                         if (max_bytes != (uint64_t) -1) {
     586          18 :                                 if (max_bytes < (uint64_t) n)
     587           1 :                                         return -EFBIG;
     588             : 
     589          17 :                                 max_bytes -= n;
     590             :                         }
     591             : 
     592          17 :                         k = loop_write(fdt, out, n, false);
     593          17 :                         if (k < 0)
     594           0 :                                 return k;
     595             : 
     596          17 :                         if (ret == LZMA_STREAM_END) {
     597           1 :                                 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           1 :                                 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           3 : int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
     612             : #if HAVE_LZ4
     613             :         size_t c;
     614           3 :         _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
     615           3 :         _cleanup_free_ char *buf = NULL;
     616             :         char *src;
     617             :         struct stat st;
     618           3 :         int r = 0;
     619           3 :         size_t total_in = 0, total_out = 0;
     620             : 
     621           3 :         c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
     622           3 :         if (LZ4F_isError(c))
     623           0 :                 return -ENOMEM;
     624             : 
     625           3 :         if (fstat(in, &st) < 0)
     626           0 :                 return log_debug_errno(errno, "fstat() failed: %m");
     627             : 
     628           3 :         buf = malloc(LZ4_BUFSIZE);
     629           3 :         if (!buf)
     630           0 :                 return -ENOMEM;
     631             : 
     632           3 :         src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
     633           3 :         if (src == MAP_FAILED)
     634           0 :                 return -errno;
     635             : 
     636           5 :         while (total_in < (size_t) st.st_size) {
     637           3 :                 size_t produced = LZ4_BUFSIZE;
     638           3 :                 size_t used = st.st_size - total_in;
     639             : 
     640           3 :                 c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
     641           3 :                 if (LZ4F_isError(c)) {
     642           0 :                         r = -EBADMSG;
     643           1 :                         goto cleanup;
     644             :                 }
     645             : 
     646           3 :                 total_in += used;
     647           3 :                 total_out += produced;
     648             : 
     649           3 :                 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
     650           1 :                         log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
     651           1 :                         r = -EFBIG;
     652           1 :                         goto cleanup;
     653             :                 }
     654             : 
     655           2 :                 r = loop_write(out, buf, produced, false);
     656           2 :                 if (r < 0)
     657           0 :                         goto cleanup;
     658             :         }
     659             : 
     660           2 :         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           3 :  cleanup:
     664           3 :         munmap(src, st.st_size);
     665           3 :         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             : }

Generated by: LCOV version 1.14