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