]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/test-compress.c
journal: add "xfail" test for partial lz4 decompression
[thirdparty/systemd.git] / src / journal / test-compress.c
CommitLineData
843fecc0
RC
1/***
2 This file is part of systemd
3
4 Copyright 2014 Ronny Chevalier
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
e3cc7fc4
ZJS
20#ifdef HAVE_LZ4
21#include <lz4.h>
22#endif
23
b5efdb8a 24#include "alloc-util.h"
843fecc0 25#include "compress.h"
3ffd4af2 26#include "fd-util.h"
0d39fa9c 27#include "fileio.h"
843fecc0 28#include "macro.h"
3df3e884 29#include "random-util.h"
3ffd4af2 30#include "util.h"
843fecc0 31
d89c8fdf
ZJS
32#ifdef HAVE_XZ
33# define XZ_OK 0
34#else
35# define XZ_OK -EPROTONOSUPPORT
36#endif
37
92261977 38#ifdef HAVE_LZ4
d89c8fdf
ZJS
39# define LZ4_OK 0
40#else
41# define LZ4_OK -EPROTONOSUPPORT
42#endif
43
44typedef int (compress_blob_t)(const void *src, uint64_t src_size,
5d6f46b6 45 void *dst, size_t dst_alloc_size, size_t *dst_size);
d89c8fdf 46typedef int (decompress_blob_t)(const void *src, uint64_t src_size,
fa1c4b51
ZJS
47 void **dst, size_t *dst_alloc_size,
48 size_t* dst_size, size_t dst_max);
d89c8fdf 49typedef int (decompress_sw_t)(const void *src, uint64_t src_size,
fa1c4b51
ZJS
50 void **buffer, size_t *buffer_size,
51 const void *prefix, size_t prefix_len,
d89c8fdf
ZJS
52 uint8_t extra);
53
59f448cf
LP
54typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
55typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
d89c8fdf
ZJS
56
57static void test_compress_decompress(int compression,
58 compress_blob_t compress,
c552d602
ZJS
59 decompress_blob_t decompress,
60 const char *data,
61 size_t data_len,
62 bool may_fail) {
843fecc0 63 char compressed[512];
5d6f46b6 64 size_t csize, usize = 0;
d89c8fdf
ZJS
65 _cleanup_free_ char *decompressed = NULL;
66 int r;
67
c552d602
ZJS
68 log_info("/* testing %s %s blob compression/decompression */",
69 object_compressed_to_string(compression), data);
70
5d6f46b6 71 r = compress(data, data_len, compressed, sizeof(compressed), &csize);
c552d602 72 if (r == -ENOBUFS) {
da927ba9 73 log_info_errno(r, "compression failed: %m");
787784c4 74 assert_se(may_fail);
c552d602 75 } else {
787784c4 76 assert_se(r == 0);
c552d602
ZJS
77 r = decompress(compressed, csize,
78 (void **) &decompressed, &usize, &csize, 0);
787784c4 79 assert_se(r == 0);
c552d602
ZJS
80 assert_se(decompressed);
81 assert_se(memcmp(decompressed, data, data_len) == 0);
82 }
d89c8fdf
ZJS
83
84 r = decompress("garbage", 7,
85 (void **) &decompressed, &usize, &csize, 0);
787784c4 86 assert_se(r < 0);
d89c8fdf
ZJS
87
88 /* make sure to have the minimal lz4 compressed size */
89 r = decompress("00000000\1g", 9,
90 (void **) &decompressed, &usize, &csize, 0);
787784c4 91 assert_se(r < 0);
d89c8fdf
ZJS
92
93 r = decompress("\100000000g", 9,
94 (void **) &decompressed, &usize, &csize, 0);
787784c4 95 assert_se(r < 0);
d89c8fdf
ZJS
96
97 memzero(decompressed, usize);
843fecc0
RC
98}
99
d89c8fdf
ZJS
100static void test_decompress_startswith(int compression,
101 compress_blob_t compress,
c552d602
ZJS
102 decompress_sw_t decompress_sw,
103 const char *data,
104 size_t data_len,
105 bool may_fail) {
d89c8fdf 106
843fecc0 107 char compressed[512];
5d6f46b6 108 size_t csize, usize = 0;
d89c8fdf 109 _cleanup_free_ char *decompressed = NULL;
c552d602 110 int r;
d89c8fdf 111
c552d602
ZJS
112 log_info("/* testing decompress_startswith with %s on %s text*/",
113 object_compressed_to_string(compression), data);
114
5d6f46b6 115 r = compress(data, data_len, compressed, sizeof(compressed), &csize);
c552d602 116 if (r == -ENOBUFS) {
da927ba9 117 log_info_errno(r, "compression failed: %m");
787784c4 118 assert_se(may_fail);
c552d602
ZJS
119 return;
120 }
787784c4 121 assert_se(r == 0);
d89c8fdf 122
d89c8fdf
ZJS
123 assert_se(decompress_sw(compressed,
124 csize,
125 (void **) &decompressed,
126 &usize,
c552d602 127 data, strlen(data), '\0') > 0);
d89c8fdf
ZJS
128 assert_se(decompress_sw(compressed,
129 csize,
130 (void **) &decompressed,
131 &usize,
c552d602 132 data, strlen(data), 'w') == 0);
d89c8fdf
ZJS
133 assert_se(decompress_sw(compressed,
134 csize,
135 (void **) &decompressed,
136 &usize,
137 "barbarbar", 9, ' ') == 0);
138 assert_se(decompress_sw(compressed,
139 csize,
140 (void **) &decompressed,
141 &usize,
c552d602 142 data, strlen(data), '\0') > 0);
843fecc0
RC
143}
144
d89c8fdf
ZJS
145static void test_compress_stream(int compression,
146 const char* cat,
147 compress_stream_t compress,
148 decompress_stream_t decompress,
149 const char *srcfile) {
150
355b59e2 151 _cleanup_close_ int src = -1, dst = -1, dst2 = -1;
4b5bc539
ZJS
152 char pattern[] = "/tmp/systemd-test.compressed.XXXXXX",
153 pattern2[] = "/tmp/systemd-test.compressed.XXXXXX";
355b59e2 154 int r;
d89c8fdf 155 _cleanup_free_ char *cmd = NULL, *cmd2;
355b59e2
ZJS
156 struct stat st = {};
157
d89c8fdf
ZJS
158 log_debug("/* testing %s compression */",
159 object_compressed_to_string(compression));
160
355b59e2
ZJS
161 log_debug("/* create source from %s */", srcfile);
162
163 assert_se((src = open(srcfile, O_RDONLY|O_CLOEXEC)) >= 0);
164
165 log_debug("/* test compression */");
166
167 assert_se((dst = mkostemp_safe(pattern, O_RDWR|O_CLOEXEC)) >= 0);
168
52754725 169 assert_se(compress(src, dst, -1) == 0);
355b59e2 170
d89c8fdf
ZJS
171 if (cat) {
172 assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
52754725 173 assert_se(system(cmd) == 0);
d89c8fdf 174 }
355b59e2
ZJS
175
176 log_debug("/* test decompression */");
177
178 assert_se((dst2 = mkostemp_safe(pattern2, O_RDWR|O_CLOEXEC)) >= 0);
179
180 assert_se(stat(srcfile, &st) == 0);
181
182 assert_se(lseek(dst, 0, SEEK_SET) == 0);
d89c8fdf 183 r = decompress(dst, dst2, st.st_size);
0c0cdb06 184 assert_se(r == 0);
355b59e2
ZJS
185
186 assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0);
187 assert_se(system(cmd2) == 0);
188
189 log_debug("/* test faulty decompression */");
190
191 assert_se(lseek(dst, 1, SEEK_SET) == 1);
d89c8fdf 192 r = decompress(dst, dst2, st.st_size);
4b5bc539 193 assert_se(r == -EBADMSG || r == 0);
355b59e2
ZJS
194
195 assert_se(lseek(dst, 0, SEEK_SET) == 0);
196 assert_se(lseek(dst2, 0, SEEK_SET) == 0);
d89c8fdf 197 r = decompress(dst, dst2, st.st_size - 1);
0c0cdb06 198 assert_se(r == -EFBIG);
355b59e2
ZJS
199
200 assert_se(unlink(pattern) == 0);
201 assert_se(unlink(pattern2) == 0);
202}
203
e3cc7fc4
ZJS
204#ifdef HAVE_LZ4
205static void test_lz4_decompress_partial(void) {
206 char buf[20000];
207 size_t buf_size = sizeof(buf), compressed;
208 int r;
209
210 char huge[4096*1024];
211 memset(huge, 'x', sizeof(huge));
212 memcpy(huge, "HUGE=", 5);
213
214 r = LZ4_compress_limitedOutput(huge, buf, sizeof(huge), buf_size);
215 assert_se(r >= 0);
216 compressed = r;
217 log_info("Compressed %zu → %zu", sizeof(huge), compressed);
218
219 r = LZ4_decompress_safe(buf, huge, r, sizeof(huge));
220 assert_se(r >= 0);
221 log_info("Decompressed → %i", r);
222
223 r = LZ4_decompress_safe_partial(buf, huge,
224 compressed,
225 12, (int) sizeof(huge));
226 assert_se(r >= 0);
227 log_info("Decompressed partial %i/%zu → %i", 12, sizeof(huge), r);
228
229 /* We expect this to fail, because that's how current lz4 works. If this
230 * call succeeds, then lz4 has been fixed, and we need to change our code.
231 */
232 r = LZ4_decompress_safe_partial(buf, huge,
233 compressed,
234 12, (int) sizeof(huge) - 1);
235 assert_se(r < 0);
236 log_info("Decompressed partial %i/%zu → %i", 12, sizeof(huge), r);
237}
238#endif
239
843fecc0 240int main(int argc, char *argv[]) {
c552d602
ZJS
241 const char text[] =
242 "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
243 "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF";
244
245 char data[512] = "random\0";
355b59e2
ZJS
246
247 log_set_max_level(LOG_DEBUG);
248
c552d602
ZJS
249 random_bytes(data + 7, sizeof(data) - 7);
250
d89c8fdf 251#ifdef HAVE_XZ
c552d602
ZJS
252 test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
253 text, sizeof(text), false);
254 test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
255 data, sizeof(data), true);
256 test_decompress_startswith(OBJECT_COMPRESSED_XZ,
257 compress_blob_xz, decompress_startswith_xz,
258 text, sizeof(text), false);
259 test_decompress_startswith(OBJECT_COMPRESSED_XZ,
260 compress_blob_xz, decompress_startswith_xz,
261 data, sizeof(data), true);
d89c8fdf
ZJS
262 test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
263 compress_stream_xz, decompress_stream_xz, argv[0]);
264#else
265 log_info("/* XZ test skipped */");
266#endif
c552d602 267
d89c8fdf 268#ifdef HAVE_LZ4
c552d602
ZJS
269 test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
270 text, sizeof(text), false);
271 test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
272 data, sizeof(data), true);
273 test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
274 compress_blob_lz4, decompress_startswith_lz4,
275 text, sizeof(text), false);
276 test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
277 compress_blob_lz4, decompress_startswith_lz4,
278 data, sizeof(data), true);
d89c8fdf 279
4b5bc539 280 test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
d89c8fdf 281 compress_stream_lz4, decompress_stream_lz4, argv[0]);
e3cc7fc4
ZJS
282
283 test_lz4_decompress_partial();
d89c8fdf
ZJS
284#else
285 log_info("/* LZ4 test skipped */");
286#endif
843fecc0
RC
287
288 return 0;
289}