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