LCOV - code coverage report
Current view: top level - journal - test-compress.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 141 143 98.6 %
Date: 2019-08-23 13:36:53 Functions: 6 6 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 82 156 52.6 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <sys/stat.h>
       4                 :            : 
       5                 :            : #if HAVE_LZ4
       6                 :            : #include <lz4.h>
       7                 :            : #endif
       8                 :            : 
       9                 :            : #include "alloc-util.h"
      10                 :            : #include "compress.h"
      11                 :            : #include "fd-util.h"
      12                 :            : #include "fs-util.h"
      13                 :            : #include "macro.h"
      14                 :            : #include "memory-util.h"
      15                 :            : #include "path-util.h"
      16                 :            : #include "random-util.h"
      17                 :            : #include "tests.h"
      18                 :            : #include "tmpfile-util.h"
      19                 :            : 
      20                 :            : #if HAVE_XZ
      21                 :            : # define XZ_OK 0
      22                 :            : #else
      23                 :            : # define XZ_OK -EPROTONOSUPPORT
      24                 :            : #endif
      25                 :            : 
      26                 :            : #if HAVE_LZ4
      27                 :            : # define LZ4_OK 0
      28                 :            : #else
      29                 :            : # define LZ4_OK -EPROTONOSUPPORT
      30                 :            : #endif
      31                 :            : 
      32                 :            : typedef int (compress_blob_t)(const void *src, uint64_t src_size,
      33                 :            :                               void *dst, size_t dst_alloc_size, size_t *dst_size);
      34                 :            : typedef int (decompress_blob_t)(const void *src, uint64_t src_size,
      35                 :            :                                 void **dst, size_t *dst_alloc_size,
      36                 :            :                                 size_t* dst_size, size_t dst_max);
      37                 :            : typedef int (decompress_sw_t)(const void *src, uint64_t src_size,
      38                 :            :                               void **buffer, size_t *buffer_size,
      39                 :            :                               const void *prefix, size_t prefix_len,
      40                 :            :                               uint8_t extra);
      41                 :            : 
      42                 :            : typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
      43                 :            : typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
      44                 :            : 
      45                 :            : #if HAVE_XZ || HAVE_LZ4
      46                 :         16 : static void test_compress_decompress(int compression,
      47                 :            :                                      compress_blob_t compress,
      48                 :            :                                      decompress_blob_t decompress,
      49                 :            :                                      const char *data,
      50                 :            :                                      size_t data_len,
      51                 :            :                                      bool may_fail) {
      52                 :            :         char compressed[512];
      53                 :         16 :         size_t csize, usize = 0;
      54                 :         16 :         _cleanup_free_ char *decompressed = NULL;
      55                 :            :         int r;
      56                 :            : 
      57         [ +  - ]:         16 :         log_info("/* testing %s %s blob compression/decompression */",
      58                 :            :                  object_compressed_to_string(compression), data);
      59                 :            : 
      60                 :         16 :         r = compress(data, data_len, compressed, sizeof(compressed), &csize);
      61         [ +  + ]:         16 :         if (r == -ENOBUFS) {
      62         [ +  - ]:          8 :                 log_info_errno(r, "compression failed: %m");
      63         [ -  + ]:          8 :                 assert_se(may_fail);
      64                 :            :         } else {
      65         [ -  + ]:          8 :                 assert_se(r == 0);
      66                 :          8 :                 r = decompress(compressed, csize,
      67                 :            :                                (void **) &decompressed, &usize, &csize, 0);
      68         [ -  + ]:          8 :                 assert_se(r == 0);
      69         [ -  + ]:          8 :                 assert_se(decompressed);
      70         [ -  + ]:          8 :                 assert_se(memcmp(decompressed, data, data_len) == 0);
      71                 :            :         }
      72                 :            : 
      73                 :         16 :         r = decompress("garbage", 7,
      74                 :            :                        (void **) &decompressed, &usize, &csize, 0);
      75         [ -  + ]:         16 :         assert_se(r < 0);
      76                 :            : 
      77                 :            :         /* make sure to have the minimal lz4 compressed size */
      78                 :         16 :         r = decompress("00000000\1g", 9,
      79                 :            :                        (void **) &decompressed, &usize, &csize, 0);
      80         [ -  + ]:         16 :         assert_se(r < 0);
      81                 :            : 
      82                 :         16 :         r = decompress("\100000000g", 9,
      83                 :            :                        (void **) &decompressed, &usize, &csize, 0);
      84         [ -  + ]:         16 :         assert_se(r < 0);
      85                 :            : 
      86         [ +  + ]:         16 :         memzero(decompressed, usize);
      87                 :         16 : }
      88                 :            : 
      89                 :         24 : static void test_decompress_startswith(int compression,
      90                 :            :                                        compress_blob_t compress,
      91                 :            :                                        decompress_sw_t decompress_sw,
      92                 :            :                                        const char *data,
      93                 :            :                                        size_t data_len,
      94                 :            :                                        bool may_fail) {
      95                 :            : 
      96                 :            :         char *compressed;
      97                 :         24 :         _cleanup_free_ char *compressed1 = NULL, *compressed2 = NULL, *decompressed = NULL;
      98                 :         24 :         size_t csize, usize = 0, len;
      99                 :            :         int r;
     100                 :            : 
     101         [ +  - ]:         24 :         log_info("/* testing decompress_startswith with %s on %.20s text */",
     102                 :            :                  object_compressed_to_string(compression), data);
     103                 :            : 
     104                 :            : #define BUFSIZE_1 512
     105                 :            : #define BUFSIZE_2 20000
     106                 :            : 
     107                 :         24 :         compressed = compressed1 = malloc(BUFSIZE_1);
     108         [ -  + ]:         24 :         assert_se(compressed1);
     109                 :         24 :         r = compress(data, data_len, compressed, BUFSIZE_1, &csize);
     110         [ +  + ]:         24 :         if (r == -ENOBUFS) {
     111         [ +  - ]:         16 :                 log_info_errno(r, "compression failed: %m");
     112         [ -  + ]:         16 :                 assert_se(may_fail);
     113                 :            : 
     114                 :         16 :                 compressed = compressed2 = malloc(BUFSIZE_2);
     115         [ -  + ]:         16 :                 assert_se(compressed2);
     116                 :         16 :                 r = compress(data, data_len, compressed, BUFSIZE_2, &csize);
     117         [ -  + ]:         16 :                 assert(r == 0);
     118                 :            :         }
     119         [ -  + ]:         24 :         assert_se(r == 0);
     120                 :            : 
     121                 :         24 :         len = strlen(data);
     122                 :            : 
     123                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
     124         [ -  + ]:         24 :         assert_se(r > 0);
     125                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, 'w');
     126         [ -  + ]:         24 :         assert_se(r == 0);
     127                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, "barbarbar", 9, ' ');
     128         [ -  + ]:         24 :         assert_se(r == 0);
     129                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, data[len-1]);
     130         [ -  + ]:         24 :         assert_se(r > 0);
     131                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len - 1, 'w');
     132         [ -  + ]:         24 :         assert_se(r == 0);
     133                 :         24 :         r = decompress_sw(compressed, csize, (void **) &decompressed, &usize, data, len, '\0');
     134         [ -  + ]:         24 :         assert_se(r > 0);
     135                 :         24 : }
     136                 :            : 
     137                 :          8 : static void test_decompress_startswith_short(int compression,
     138                 :            :                                              compress_blob_t compress,
     139                 :            :                                              decompress_sw_t decompress_sw) {
     140                 :            : 
     141                 :            : #define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
     142                 :            : 
     143                 :            :         char buf[1024];
     144                 :            :         size_t i, csize;
     145                 :            :         int r;
     146                 :            : 
     147         [ +  - ]:          8 :         log_info("/* %s with %s */", __func__, object_compressed_to_string(compression));
     148                 :            : 
     149                 :          8 :         r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize);
     150         [ -  + ]:          8 :         assert_se(r == 0);
     151                 :            : 
     152         [ +  + ]:       1000 :         for (i = 1; i < strlen(TEXT); i++) {
     153                 :        992 :                 size_t alloc_size = i;
     154                 :        992 :                 _cleanup_free_ void *buf2 = NULL;
     155                 :            : 
     156         [ -  + ]:        992 :                 assert_se(buf2 = malloc(i));
     157                 :            : 
     158         [ -  + ]:        992 :                 assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, TEXT[i]) == 1);
     159         [ -  + ]:        992 :                 assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, 'y') == 0);
     160                 :            :         }
     161                 :          8 : }
     162                 :            : 
     163                 :          8 : static void test_compress_stream(int compression,
     164                 :            :                                  const char* cat,
     165                 :            :                                  compress_stream_t compress,
     166                 :            :                                  decompress_stream_t decompress,
     167                 :            :                                  const char *srcfile) {
     168                 :            : 
     169   [ +  -  +  -  :          8 :         _cleanup_close_ int src = -1, dst = -1, dst2 = -1;
                   +  - ]
     170                 :            :         _cleanup_(unlink_tempfilep) char
     171         [ +  - ]:          8 :                 pattern[] = "/tmp/systemd-test.compressed.XXXXXX",
     172         [ +  - ]:          8 :                 pattern2[] = "/tmp/systemd-test.compressed.XXXXXX";
     173                 :            :         int r;
     174   [ +  -  +  - ]:          8 :         _cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
     175                 :          8 :         struct stat st = {};
     176                 :            : 
     177                 :          8 :         r = find_binary(cat, NULL);
     178         [ -  + ]:          8 :         if (r < 0) {
     179         [ #  # ]:          0 :                 log_error_errno(r, "Skipping %s, could not find %s binary: %m", __func__, cat);
     180                 :          0 :                 return;
     181                 :            :         }
     182                 :            : 
     183         [ +  - ]:          8 :         log_debug("/* testing %s compression */",
     184                 :            :                   object_compressed_to_string(compression));
     185                 :            : 
     186         [ +  - ]:          8 :         log_debug("/* create source from %s */", srcfile);
     187                 :            : 
     188         [ -  + ]:          8 :         assert_se((src = open(srcfile, O_RDONLY|O_CLOEXEC)) >= 0);
     189                 :            : 
     190         [ +  - ]:          8 :         log_debug("/* test compression */");
     191                 :            : 
     192         [ -  + ]:          8 :         assert_se((dst = mkostemp_safe(pattern)) >= 0);
     193                 :            : 
     194         [ -  + ]:          8 :         assert_se(compress(src, dst, -1) == 0);
     195                 :            : 
     196         [ +  - ]:          8 :         if (cat) {
     197         [ -  + ]:          8 :                 assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
     198         [ -  + ]:          8 :                 assert_se(system(cmd) == 0);
     199                 :            :         }
     200                 :            : 
     201         [ +  - ]:          8 :         log_debug("/* test decompression */");
     202                 :            : 
     203         [ -  + ]:          8 :         assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
     204                 :            : 
     205         [ -  + ]:          8 :         assert_se(stat(srcfile, &st) == 0);
     206                 :            : 
     207         [ -  + ]:          8 :         assert_se(lseek(dst, 0, SEEK_SET) == 0);
     208                 :          8 :         r = decompress(dst, dst2, st.st_size);
     209         [ -  + ]:          8 :         assert_se(r == 0);
     210                 :            : 
     211         [ -  + ]:          8 :         assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0);
     212         [ -  + ]:          8 :         assert_se(system(cmd2) == 0);
     213                 :            : 
     214         [ +  - ]:          8 :         log_debug("/* test faulty decompression */");
     215                 :            : 
     216         [ -  + ]:          8 :         assert_se(lseek(dst, 1, SEEK_SET) == 1);
     217                 :          8 :         r = decompress(dst, dst2, st.st_size);
     218   [ +  -  -  + ]:          8 :         assert_se(IN_SET(r, 0, -EBADMSG));
     219                 :            : 
     220         [ -  + ]:          8 :         assert_se(lseek(dst, 0, SEEK_SET) == 0);
     221         [ -  + ]:          8 :         assert_se(lseek(dst2, 0, SEEK_SET) == 0);
     222                 :          8 :         r = decompress(dst, dst2, st.st_size - 1);
     223         [ -  + ]:          8 :         assert_se(r == -EFBIG);
     224                 :            : }
     225                 :            : #endif
     226                 :            : 
     227                 :            : #if HAVE_LZ4
     228                 :          4 : static void test_lz4_decompress_partial(void) {
     229                 :            :         char buf[20000], buf2[100];
     230                 :          4 :         size_t buf_size = sizeof(buf), compressed;
     231                 :            :         int r;
     232                 :          4 :         _cleanup_free_ char *huge = NULL;
     233                 :            : 
     234                 :            : #define HUGE_SIZE (4096*1024)
     235         [ -  + ]:          4 :         assert_se(huge = malloc(HUGE_SIZE));
     236                 :          4 :         memset(huge, 'x', HUGE_SIZE);
     237                 :          4 :         memcpy(huge, "HUGE=", 5);
     238                 :            : 
     239                 :          4 :         r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size);
     240         [ -  + ]:          4 :         assert_se(r >= 0);
     241                 :          4 :         compressed = r;
     242         [ +  - ]:          4 :         log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
     243                 :            : 
     244                 :          4 :         r = LZ4_decompress_safe(buf, huge, r, HUGE_SIZE);
     245         [ -  + ]:          4 :         assert_se(r >= 0);
     246         [ +  - ]:          4 :         log_info("Decompressed → %i", r);
     247                 :            : 
     248                 :          4 :         r = LZ4_decompress_safe_partial(buf, huge,
     249                 :            :                                         compressed,
     250                 :            :                                         12, HUGE_SIZE);
     251         [ -  + ]:          4 :         assert_se(r >= 0);
     252         [ +  - ]:          4 :         log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
     253                 :            : 
     254         [ +  + ]:        400 :         for (size_t size = 1; size < sizeof(buf2); size++) {
     255                 :            :                 /* This failed in older lz4s but works in newer ones. */
     256                 :        396 :                 r = LZ4_decompress_safe_partial(buf, buf2, compressed, size, size);
     257   [ +  -  -  + ]:        396 :                 log_info("Decompressed partial %zu/%zu → %i (%s)", size, size, r,
     258                 :            :                                                                    r < 0 ? "bad" : "good");
     259   [ +  -  +  - ]:        396 :                 if (r >= 0 && LZ4_versionNumber() >= 10803)
     260                 :            :                         /* lz4 <= 1.8.2 should fail that test, let's only check for newer ones */
     261         [ -  + ]:        396 :                         assert_se(memcmp(buf2, huge, r) == 0);
     262                 :            :         }
     263                 :          4 : }
     264                 :            : #endif
     265                 :            : 
     266                 :          4 : int main(int argc, char *argv[]) {
     267                 :            : #if HAVE_XZ || HAVE_LZ4
     268                 :          4 :         const char text[] =
     269                 :            :                 "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
     270                 :            :                 "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF";
     271                 :            : 
     272                 :            :         /* The file to test compression on can be specified as the first argument */
     273         [ -  + ]:          4 :         const char *srcfile = argc > 1 ? argv[1] : argv[0];
     274                 :            : 
     275                 :          4 :         char data[512] = "random\0";
     276                 :            : 
     277                 :            :         char huge[4096*1024];
     278                 :          4 :         memset(huge, 'x', sizeof(huge));
     279                 :          4 :         memcpy(huge, "HUGE=", 5);
     280                 :          4 :         char_array_0(huge);
     281                 :            : 
     282                 :          4 :         test_setup_logging(LOG_DEBUG);
     283                 :            : 
     284                 :          4 :         random_bytes(data + 7, sizeof(data) - 7);
     285                 :            : 
     286                 :            : #if HAVE_XZ
     287                 :          4 :         test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
     288                 :            :                                  text, sizeof(text), false);
     289                 :          4 :         test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
     290                 :            :                                  data, sizeof(data), true);
     291                 :            : 
     292                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_XZ,
     293                 :            :                                    compress_blob_xz, decompress_startswith_xz,
     294                 :            :                                    text, sizeof(text), false);
     295                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_XZ,
     296                 :            :                                    compress_blob_xz, decompress_startswith_xz,
     297                 :            :                                    data, sizeof(data), true);
     298                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_XZ,
     299                 :            :                                    compress_blob_xz, decompress_startswith_xz,
     300                 :            :                                    huge, sizeof(huge), true);
     301                 :            : 
     302                 :          4 :         test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
     303                 :            :                              compress_stream_xz, decompress_stream_xz, srcfile);
     304                 :            : 
     305                 :          4 :         test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz);
     306                 :            : 
     307                 :            : #else
     308                 :            :         log_info("/* XZ test skipped */");
     309                 :            : #endif
     310                 :            : 
     311                 :            : #if HAVE_LZ4
     312                 :          4 :         test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
     313                 :            :                                  text, sizeof(text), false);
     314                 :          4 :         test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
     315                 :            :                                  data, sizeof(data), true);
     316                 :            : 
     317                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
     318                 :            :                                    compress_blob_lz4, decompress_startswith_lz4,
     319                 :            :                                    text, sizeof(text), false);
     320                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
     321                 :            :                                    compress_blob_lz4, decompress_startswith_lz4,
     322                 :            :                                    data, sizeof(data), true);
     323                 :          4 :         test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
     324                 :            :                                    compress_blob_lz4, decompress_startswith_lz4,
     325                 :            :                                    huge, sizeof(huge), true);
     326                 :            : 
     327                 :          4 :         test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
     328                 :            :                              compress_stream_lz4, decompress_stream_lz4, srcfile);
     329                 :            : 
     330                 :          4 :         test_lz4_decompress_partial();
     331                 :            : 
     332                 :          4 :         test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4);
     333                 :            : 
     334                 :            : #else
     335                 :            :         log_info("/* LZ4 test skipped */");
     336                 :            : #endif
     337                 :            : 
     338                 :          4 :         return 0;
     339                 :            : #else
     340                 :            :         log_info("/* XZ and LZ4 tests skipped */");
     341                 :            :         return EXIT_TEST_SKIP;
     342                 :            : #endif
     343                 :            : }

Generated by: LCOV version 1.14