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