]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/test-compress.c
Merge pull request #2216 from zonque/nameownerchanged
[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, O_RDWR|O_CLOEXEC)) >= 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, O_RDWR|O_CLOEXEC)) >= 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 r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size);
220 assert_se(r >= 0);
221 compressed = r;
222 log_info("Compressed %i → %zu", HUGE_SIZE, compressed);
223
224 r = LZ4_decompress_safe(buf, huge, r, HUGE_SIZE);
225 assert_se(r >= 0);
226 log_info("Decompressed → %i", r);
227
228 r = LZ4_decompress_safe_partial(buf, huge,
229 compressed,
230 12, HUGE_SIZE);
231 assert_se(r >= 0);
232 log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r);
233
234 /* We expect this to fail, because that's how current lz4 works. If this
235 * call succeeds, then lz4 has been fixed, and we need to change our code.
236 */
237 r = LZ4_decompress_safe_partial(buf, huge,
238 compressed,
239 12, HUGE_SIZE-1);
240 assert_se(r < 0);
241 log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r);
242 }
243 #endif
244
245 int main(int argc, char *argv[]) {
246 const char text[] =
247 "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
248 "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF";
249
250 char data[512] = "random\0";
251
252 char huge[4096*1024];
253 memset(huge, 'x', sizeof(huge));
254 memcpy(huge, "HUGE=", 5);
255 char_array_0(huge);
256
257 log_set_max_level(LOG_DEBUG);
258
259 random_bytes(data + 7, sizeof(data) - 7);
260
261 #ifdef HAVE_XZ
262 test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
263 text, sizeof(text), false);
264 test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
265 data, sizeof(data), true);
266
267 test_decompress_startswith(OBJECT_COMPRESSED_XZ,
268 compress_blob_xz, decompress_startswith_xz,
269 text, sizeof(text), false);
270 test_decompress_startswith(OBJECT_COMPRESSED_XZ,
271 compress_blob_xz, decompress_startswith_xz,
272 data, sizeof(data), true);
273 test_decompress_startswith(OBJECT_COMPRESSED_XZ,
274 compress_blob_xz, decompress_startswith_xz,
275 huge, sizeof(huge), true);
276
277 test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
278 compress_stream_xz, decompress_stream_xz, argv[0]);
279 #else
280 log_info("/* XZ test skipped */");
281 #endif
282
283 #ifdef HAVE_LZ4
284 test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
285 text, sizeof(text), false);
286 test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
287 data, sizeof(data), true);
288
289 test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
290 compress_blob_lz4, decompress_startswith_lz4,
291 text, sizeof(text), false);
292 test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
293 compress_blob_lz4, decompress_startswith_lz4,
294 data, sizeof(data), true);
295 test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
296 compress_blob_lz4, decompress_startswith_lz4,
297 huge, sizeof(huge), true);
298
299 test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
300 compress_stream_lz4, decompress_stream_lz4, argv[0]);
301
302 test_lz4_decompress_partial();
303 #else
304 log_info("/* LZ4 test skipped */");
305 #endif
306
307 return 0;
308 }