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