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